专栏首页Frank909Linux 多进程通信开发(三):管道

Linux 多进程通信开发(三):管道

这会是一系列文章,讲解的内容也很简单,文章的目的是让自己的知识固话和文档化,以备自己不时的复习,同时也希望能够给予初学者一些帮助。

在代码编程过程中,进程之间进行通信是很常见的需求,它有个专业的名词 IPC,能够实现 IPC 的手段有许多,管道(Pipe)是其中一种。

本文的示例是用 C++ 编写的,不熟悉 C++ 的同学,用 c 也是可以实现效果的。

管道(Pipe)

管道的本质是一个文件,它具备如下特点:

  • FIFO 先进先出
  • 单向通信

先存进去的信息,最先被读取出来。

只可以从固定的一端读,从另一端写,这是它被称为单向的原因。

管道可以分为

  • 无名管道
  • 有名管道

无名管道

无名管道创建的方式很简单通过 linux 系统调用就好了。

#include <unistd.h>

int pipe(int fd[2])

如果创建失败,则会返回 -1,反之返回 0.

创建成功后,fd 数组会被填充两个文件描述符。

fd[0] 代表读文件描述符,只能用来读,不能用来写 fd[1] 代表写文件描述符,只能用来写,不能用来读

无名管道一般用于 父子进程的通信,或者兄弟进程之间的通信。

细节:

  • 读管道的时候,要关闭 fd[1]
  • 写管道的时候,要关闭 fd[0]
  • pipe 调用要在 fork 之前

下面示例说明

无名管道示例

testpipe1.cpp

#include <unistd.h>
#include <iostream>
#include <string>

using namespace std;

int main(int argc,char** argv)
{
    int fds[2];
    string s("Hello,i am child.");

    if (pipe(fds) < 0)
    {
        cerr << "Make pipe failed " << endl;
        return -1;
    }

    pid_t pid = fork();

    switch (pid)
    {
        case -1/* constant-expression */:
            /* code */
            cerr <<  "Fork ocuur error!" << endl;
            break;
        
        case 0:
            close(fds[0]);
            write(fds[1],s.c_str(),s.length());
            break;
        default:
            close(fds[1]);
            char msg[50];
            read(fds[0],msg,50);
            cout << "Read msg from child : " << msg << endl;
            break;
    }

    return 0;
}

代码很简单,读写和操作平常的文件一样。

g++ testpipe1.cpp
./a.out

编译然后运行结果如下:

Read msg from child : Hello,i am child.

有名管道

无名管道只是内存中的一个特殊文件,并没有名字,所以它只能在父子进程之间进行通信。

如果要在两个独立的进程之间进行通信的话就要用到有名管道,有名管道也叫 FIFO,会在本地文件系统创建一个有名字的文件,用于通信。

有名管道创建的方式有 2 种:shell 和 系统调用。

shell 创建 FIFO

有两个命令都可以在 shell 环境下创建管道,它们是 mknod、mkfifo。

比如

mkfifo fifotest

会在本地创建 fifotest 这个文件。

linux 系统调用创建 FIFO

linux 下创建管道的系统调用函数也有两个,它们定义在 <sys/stat.h>头文件当中

#include <sys/stat.h>
int mknod (const char *__path, __mode_t __mode, __dev_t __dev)
  • path 是管道路径
  • mode 是管道的权限
  • dev 设备号 一般创建设备文件才使用
#include <sys/stat.h>
int mkfifo (const char *__path, __mode_t __mode)

mkfifo 也能创建有名管道,并且只需要传递 2 个参数就好了。

mkfifo("fifoone",S_IFIFO|0666)

这段代码的目的是在当前目录创建一个名为 fifoone 的 FIFO 文件,并且权限为 666,这样保证了其它进程也可以正常读写。

FIFO 的读写

FIFO 就是一个文件,所以 linux 下怎么读写文件的操作,都可以应用在 FIFO 上。

不同于无名管道的是,FIFO 读写前先要打开 FIFO,需要调用 Open() 函数。

然后 Open() 可能会让进程阻塞。

如果不想进程被阻塞,就需要用 O_RDWR 同时读写的模型打开 FIFO.

另外还有两个细节

  • 以 O_RDONLY 只读模型打开 FIFO,进程会阻塞,直到另外一个进程往 FIFO 里面写数据
  • 以 O_WRONLY 只写模型打开 FIFO,进程会阻塞,直到另外一个进程往 FIFO 里面读数据

下面同样简单的代码示例一下,演示不同的两个进程如何通过 FIFO 通信。

testfiforead.cpp

#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <string.h>
#include <errno.h> 
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

int main(int arg,char** argv)
{
    char msg[512];

    int fd = open("fifoone",O_RDONLY);

    if (fd < 0)
    {
        cerr << "open fifo failed " << strerror(errno) << endl;
        return -1;
    }

    read(fd,msg,512);

    cout << "Read msg from fifo : " << msg << endl;
    
    close(fd);

    return 0;
}

这个程序的作用就是要读取名字为 fifoone 的管道,你可以在终端中通过 mkfifo fifoone命令创建,也可以在函数当中创建,这里我为了方便在终端中直接创建了。

testfifowrite.cpp

#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <string.h>
#include <errno.h> 
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

int main(int arg,char** argv)
{
    char msg[512];

    int fd = open("fifoone",O_WRONLY);

    if (fd < 0)
    {
        cerr << "open fifo failed " << strerror(errno) << endl;
        return -1;
    }

    write(fd,"Hello,how are you?\n",512);

    
    close(fd);

    return 0;
}

这段程序的作用是往 FIFO 里面写数据。

最后,我们试一下它们的运行效果。

g++ testfiforead.cpp -o fiforead
g++ testfifowrite.cpp -o fifowrite

把它们分别编译成 fiforead 和 fifowrite 两个可执行文件,然后分别运行。

./fifowrite &
./fiforead

运行后,它们的进程是独立的,结果如下

Read msg from fifo : Hello,how are you?

[1]+  已完成               ./fifowrite

这个结果说明了,两个没有关系的进程可以通过 FIFO 进行通信。

管道的全双工通信

前面说过,管道是单工通信的,只能固定一端发送,另一端接收。

一个管道只能做到一个进程读,另一个进程写。

如果进程之间自由读写的话,也很简单,创建 2 个管道就好了,但注意处理好管道的文件描述符。

每一个进程都需要 1 个管道的读描述符,另一个管道的写描述符。

这个不难,有兴趣的同学可以自己编写代码测试一下。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux 多进程通信开发(八): unix domain socket 之 TCP 通信

    前面的文章一系列文章有介绍了 linux 下常见的 IPC 机制,如管道、消息队列、信号量、共享内存。

    Frank909
  • Linux 多进程通信开发(七): unix domain socket 之 UDP 通信

    前面的文章一系列文章有介绍了 linux 下常见的 IPC 机制,如管道、消息队列、信号量、共享内存。

    Frank909
  • 神奇的 ViewDragHelper,让你轻松定制拥有拖拽能力的 ViewGroup

    相信这种效果大家都见过吧?我第一次见到这样的效果时,心里也痒痒的,急于想实现这种功能,后来因为拖延症的问题,就一直没有去弄这件事。现在这段时间,工作比较轻...

    Frank909
  • 普华永道报告:风口上的区块链,将颠覆这8大领域

    作者:普华永道 2018年一开年,借着全球市场的东风,区块链概念顿时成为A股市场最为火热的炒作主题,不少上市公司纷纷迎面而上,“拥抱”区块链概念。本文是普华永道...

    钱塘数据
  • 宜信Blockworm BaaS:用区块链技术构建可信商业环境

    2008年,中本聪发表了一篇题为《比特币:点对点的电子现金系统》的论文,首次提出了关于区块链概念的描述。

    宜信技术学院
  • sql server 2008 数据库的完整性约束

    一、数据库完整性概述 1.数据库的完整性: ①数据库的完整性是指数据的正确性和相容性 ②数据库完整性是防止不合语义或不正确的数据进入数据库 ③完整性体现了是否真...

    欠扁的小篮子
  • 如何创建一个最小的区块链

    这是我在一个外文网站上看到的一篇博文,作者通过50行代码写出了区块链的简化版本.麻雀虽小,但是五脏俱全.我觉得通过实践,这是了解区块链的一个好的方式.于是我将代...

    云时之间
  • BTA | 陈建闽(阿德):Token的分散过程,一定要基于场景

    区块链大本营
  • linux系统编程之文件与I/O(二):文件的读取写入

    一、read系统调用 一旦有了与一个打开文件描述相关连的文件描述符,只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件...

    s1mba
  • Wing IDE Pro (Wing pro 6.0) for Ubuntu/linux

    Wing IDE Pro (Wing pro 6.0) for Ubuntu/linux

    用户1148525

扫码关注云+社区

领取腾讯云代金券