进程间通信——管道

1 管道

       作为进程之间的通信手段之一,本质就是内核中的一块有限的缓冲区(64K字节,不同操作系统不一样),而且独立于文件系统,自己构成一个只存在于内存中的文件系统(可追踪内核的pipe.c存在于文件系统目录下)。其特点如下。

1)半双工通信,同一时间数据只能从一端流向另一端;

2)分为匿名管道和有名管道。而且对于匿名管道,只能用于具有共同祖先的进程之间通信,即父子进程或者父孙进程等有关系的进程之间通信,有名管道则没有此限制;

3)填充的是文件描述符数组,其中fd[0] 表示读端,fd[1]表示写端。其在创建子进程前后的关系如下图所示。


2 通用编程方式

2.1 匿名管道

       如下表所示,需要注意的就是,父子进程可以关闭管道无用的端口。

  1 

//假设用例是子进程通过管道发送给父进程

  2 

// 子进程需要关闭读端,父进程需要关闭写端

  3 

int main(int argc, char *argv[])

  4 

{

  5 

    int pipefd[2];

  6 

    if (pipe(pipefd) == -1)

  7 

        ERR_EXIT("pipe error");

  8 

    pid_t pid;

  9 

    pid = fork();

 10 

    if (pid == -1)

 11 

        ERR_EXIT("fork error");

 12 

    if (pid == 0)

 13 

    {

 14 

        close(pipefd[0]);   

 15 

        write(pipefd[1], "hello", 5);

 16 

        close(pipefd[1]);

 17 

        exit(EXIT_SUCCESS);

 18 

    }

 19 

    close(pipefd[1]);

 20 

    char buf[10] = {0};

 21 

    read(pipefd[0], buf, 10);

 22 

    printf("buf=%s\n", buf);

 23 

    return 0;

 24 

}

2.2 有名管道

       简单示例,如下表所示。注意打开方式的不同,除了下示的open打开一个已经存在的管道文件外,还可以通过mkfifo函数创建,事实上它也是一个命令。

       其用法说明如下。

1)有名管道,是一种特殊的文件类型,即通过ls可以查看到其指定为“p”,除此之外还有六种类型,共计七种,d-目录文件,l-符号链接文件,s-套接字文件,b-块设备文件,c-字符设备文件,“-”表示普通文件。

2) 打开方式有所不同,有可能会阻塞。没错,打开时就阻塞。具体可参见下表,注意区分O_NONBLOCK使能后,对应的错误。

 

O_NONBLOCK disable

O_NONBLOCK enable

READ

阻塞,直至有程序以写打开该管道

返回成功

WRITE

阻塞,直至有程序以读打开该管道

返回失败,错误码为ENXIO

3)示例程序

  1 

int main(int argc, char *argv[])

  2 

{

  3 

    int fd;

  4 

/*    fd = open("p1", O_RDONLY);*/

  5 

    fd = open("p1", O_RDONLY | O_NONBLOCK);

  6 

   

  7 

    if (fd == -1)

  8 

        ERR_EXIT("open error");

  9 

 

 10 

    printf("open succ\n");

 11 

    return 0;

 12 

}

2.3 对比

       主要体现在两点,一是打开方式,匿名管道不可能会发生阻塞;二是用途,匿名管道不能用于不具备亲缘关系的进程之间通信。

 

3 读写规则

       若管道的写端不存在,继续读该管道时,认为已经读到了末尾,返回0;

       若管道的读端不存在,继续写该管道时,返回错误;

       [可见系统对写采取0容忍的态度 ]

       若管道不存在可读数据,那么在非阻塞模式下返回-1,且errno等于EAGAIN,而在阻塞模式下则会继续阻塞直至有数据;

       若管道已经满时,那么在非阻塞模式下同样返回-1,且errno等于EAGAIN,在阻塞模式下则继续阻塞,直至有空间可写;

       当写入的数据大于PIPEBUF(在文章一开始处已经介绍了管道本质就是内核的一块缓冲区大小为PIPEBUF 64K),不会保证写入的原子性。如下代码所示。

  1 

int main(void)

  2 

{

  3 

    char a[TEST_SIZE];

  4 

    char b[TEST_SIZE];

  5 

    char c[TEST_SIZE];

  6 

 

  7 

    memset(a, 'A', sizeof(a));

  8 

    memset(b, 'B', sizeof(b));

  9 

    memset(c, 'C', sizeof(c));

 10 

 

 11 

    int pipefd[2];

 12 

 

 13 

    int ret = pipe(pipefd);

 14 

    if (ret == -1)

 15 

        ERR_EXIT("pipe error");

 16 

 

 17 

    pid_t pid;

 18 

    pid = fork();

 19 

    if (pid == 0)//第一个子进程

 20 

    {

 21 

        close(pipefd[0]);

 22 

        ret = write(pipefd[1], a, sizeof(a));

 23 

        printf("apid=%d write %d bytes to pipe\n", getpid(), ret);

 24 

        exit(0);

 25 

    }

 26 

 

 27 

    pid = fork();

 28 

 

 29 

   

 30 

    if (pid == 0)//第二个子进程

 31 

    {

 32 

        close(pipefd[0]);

 33 

        ret = write(pipefd[1], b, sizeof(b));

 34 

        printf("bpid=%d write %d bytes to pipe\n", getpid(), ret);

 35 

        exit(0);

 36 

    }

 37 

 

 38 

    pid = fork();

 39 

 

 40 

   

 41 

    if (pid == 0)//第三个子进程

 42 

    {

 43 

        close(pipefd[0]);

 44 

        ret = write(pipefd[1], c, sizeof(c));

 45 

        printf("bpid=%d write %d bytes to pipe\n", getpid(), ret);

 46 

        exit(0);

 47 

    }

 48 

 

 49 

 

 50 

    close(pipefd[1]);

 51 

   

 52 

    sleep(1);

 53 

    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);

 54 

    char buf[1024*4] = {0};

 55 

    int n = 1;

 56 

    while (1)

 57 

    {

 58 

        ret = read(pipefd[0], buf, sizeof(buf));

 59 

        if (ret == 0)

 60 

            break;

 61 

        printf("n=%02d pid=%d read %d bytes from pipe buf[4095]=%c\n", n++, getpid(), ret, buf[4095]);

 62 

        write(fd, buf, ret);

 63 

 

 64 

    }

 65 

    return 0;   

 66 

}

       其结果,如下图所示。“A”、“B”、“C”完全是乱序输出的,并不是按顺序,写完A再写B和C的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC是微软基于C++的一个应用程序框架,它可以帮助开发人员快速创建Windows应用程序。进程间通信(IPC)是不同进程之间进行数据交换和通信的一种方法。管道是一种常见的IPC机制,它允许两个进程之间进行双向通信。 在MFC中,使用管道进行进程间通信可以分为两个步骤:创建管道和使用管道进行通信。 首先,需要创建一个管道。可以使用CreatePipe函数来创建匿名管道,它接受两个参数,第一个参数是用于接收管道句柄的指针,第二个参数是用于发送管道句柄的指针。成功创建管道后,你将获得两个句柄,一个用于读取数据,一个用于写入数据。 然后,可以使用ReadFile和WriteFile函数来读取和写入管道中的数据。这些函数可以传入一个管道句柄,一个缓冲区来存储数据以及数据的长度。通过这些函数,可以在两个进程之间传递数据。 如果需要实现双向通信,可以在每个进程中使用一个管道来进行读取和写入操作。这样,两个进程就可以通过各自的管道进行双向通信了。例如,进程A使用管道A向进程B发送数据,进程B使用管道B向进程A发送数据。 总结起来,MFC可以使用管道进行进程间通信,通过创建管道和使用ReadFile和WriteFile函数来实现数据的读取和写入。如果需要双向通信,则可以在两个进程中分别创建管道来进行双向数据传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值