linux进程间通信--管道(PIPE & FIFO)

linux进程间通信–管道(PIPE & FIFO)

参考资料:

  1. overview of pipes and FIFOs:man 7 pipe
  2. 《The Linux Programming inTerface》

管道一般分为无名管道pipe和有名管道mkfifo.都用于进程之间的通信。下面将一一介绍它们。

无名管道

无名管道(pipe)一般用于关联进程(如父子进程)之间的通信。它的使用类似文件,但他不是普通文件,不属于某种文件系统。我们最常见的pipe使用是shell命令,如ls | wc -l。pipe有如下特点:

  1. 半双工:数据只能向一个方向流动.需要双方通信时,建立两个管道.
  2. 关联进程:只能用于关联进程之间。如父子进程或者兄弟进程之间通信。
  3. 面向字节流:从管道可读取任意大小的数据块。且只能顺序的读取,即不能使用lseek()随机访问管道中的数据。
  4. 原子操作:如果多个进程向一个管道写入数据,如果它们一次写入的字节不超过PIPE_BUF(在limits.h),就可以保证它们的数据不会混杂在一起。

管道在读写时的行为如下:

  1. 写入达到上限:当写入管道的数据达到上限,则阻塞.直到管道数据被读走。然后在继续写入。上限默认为65536.可通过fcntl(fd, F_SETPIPE_SZ, size)修改。
  2. 读出为空:从空的管道中读取数据,进程阻塞。直到至少有一个字节被写入该管道。如果管道的写端被关闭,那么读取数据的进程在读取完管道中剩余数据后将看到文件结束(end-of-file)。

使用函数pipe()创建管道,需要包含头文件unistd.h.:

  1. int pipe(int fildes[2])
    1. 创建一个管道,并将管道的读写端文件描述符(分别)放入filedes[0]和filedes[1]中。有了文件操作符后,我们就可以使用write(),read()进行读写了。
    2. fildes:文件描述符数组,fildes[0]保存读文件描述符。fildes[0]保存写文件描述符。
    3. return:if true:0,if false:-1.errno如下:
      1. EMFILE:进程打开文件过多
      2. ENFILE:整个系统中打开的文件太多了。

linux提供了非标准的pipe2()函数。功能比pipe()丰富。可自行查询了解。

利用管道进行进程间通信的原理如下图:pipe

下面是一个pipe使用的例程,展示了父子进程之间的通信:

#include <unistd.h>
#include <sys/ipc.h>
#include  <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

void main(void)
{
    int res;
    int pipe_fd[2];

    //1. 创建管道
    res = pipe(pipe_fd);
    if(res == -1){
        printf("pipe error\n");
        exit(1);
    }

    //2. 创建线程
    pid_t pid = fork();
    if(pid == 0){//子进程
        char buf[24];

        //3. 子进程用于读,所以关掉写文件描述符
        if (close(pipe_fd[1]) == -1){
            printf("pipe pipe_fd[1] close error\n");
            exit(1);
        }

        //4. 等待接收父进程的数据
        while (1){
            ssize_t num = read(pipe_fd[0], buf, 24);
            if(num == 0){
                break;
            }
            else if (num == -1){
                printf("read error\n");
                exit(1);
            }
            write(STDOUT_FILENO, buf, num);//打印到控制台
        }
        write(STDOUT_FILENO, "\n", 1);

        //5. 关掉读文件描述符
        if (close(pipe_fd[0]) == -1){
            printf("pipe pipe_fd[0] close error\n");
            exit(1);
        }

        exit(0);
    }
    else{//父进程
        //6. 父进程用于写,所以关掉读文件描述符
        if (close(pipe_fd[0]) == -1){
            printf("pipe pipe_fd[0] close error\n");
            exit(1);
        }

        //7. 发送相关数据
        char write_buff[] = "hello word";
        write(pipe_fd[1], write_buff,sizeof(write_buff));

        //8. 关掉写文件描述符
        if (close(pipe_fd[1]) == -1){
            printf("pipe pipe_fd[1] close error\n");
            exit(1);
        }

        wait(NULL);
        exit(0);
    }
}

有名管道

有名管道(FIFO)是无名管道(pipe)的一种升级版。主要区别如下:

  1. 有名管道有文件名称且在文件系统中可见。
  2. 由于有名称,所以可以在任意进程下建立通信。

有名管道在读写时的行为如下:

  1. 读取FIFO文件的进程只能以RDONLY方式打开FIFO文件.
  2. 写FIFO文件的进程只能以WRONLY方式打开FIFO.

其他与无名管道相似,例如:

  1. 不允许文件定位,只能顺序访问。
  2. 面向字节流。
  3. 管道在进行读写时必须都处于打开状态:这句话说的比较模糊需要解释一下。笔者测试后得到如下结论:
    1. 在open()函数打开fifo文件阶段:写方式的open()会阻塞,等待读方式的open()打开fifo文件。这个阶段后管道的读写端便处于同时打开状态。即满足上边的条件。
    2. 使用read(),write()操作fifo阶段:在fifo读写端都打开之后(即满足第一个条件后),便可以进行读写操作了。行为与pipe相同
      1. 写入达到上限:当写入管道的数据达到上限,则阻塞.直到管道数据被读走。然后在继续写入。上限默认为65536.
      2. 读出为空:从空的管道中读取数据,进程阻塞。直到至少有一个字节被写入该管道。如果管道的写端被关闭,那么读取数据的进程在读取完管道中剩余数据后将看到文件结束(end-of-file)。
    3. 在读写过程中,如果close()了读取管道的文件操作符:此时进行写操作会触发SIGPIPE信号,如果信号被处理或阻塞,它会以错误代码EPIPE失败。
    4. 在读写过程中,如果close()了写入管道的文件操作符:读取数据的进程在读取完管道中剩余数据后将看到文件结束(end-of-file)。

使用mkfifo()函数创建一个命名管道。使用时需要包含头文件sys/stat.h。函数如下:

  1. int mkfifo (const char *filename, mode t mode)
    1. 创建一个命名管道.
    2. filename:文件名
    3. mode:文件权限。如0666
    4. return:if true:0,if false:-1.errno如下:
      1. EEXIST:名称已经存在

其余的函数和文件操作函数相同。

演示例程如下:

#include <unistd.h>
#include <sys/ipc.h>
#include  <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

void main(void)
{
    int res;
    //1. 创建fifo
    res = mkfifo("/tmp/fifo", 0666);
    if(res == -1  && errno != EEXIST){
        printf("mkfifo error\n");
        exit(1);
    }

    //2. 进程之间使用fifo通信,为了方便直接使用父子进程
    pid_t pid = fork();
    if(pid == 0){//子进程
        //3. 打开fifo
        int fd = open("/tmp/fifo", O_RDONLY);
        if(fd == -1){
            printf("(%d)open error\n",getpid());
            exit(1);
        }

        char buff[32];
        while(1){
            //4. 读取fifo的内容
            ssize_t num = read(fd, buff, sizeof(buff));
            if(num == 0){
                break;
            }
            else if (num == -1){
                printf("read error\n");
                exit(1);
            }
            write(STDOUT_FILENO,buff,num);
        }
        write(STDOUT_FILENO, "\n", 1);

        //5. 关掉读文件描述符
        if (close(fd) == -1){
            printf("(%d) close error\n",getpid());
            exit(1);
        }
    }
    else{
        //6. 父进程以只写方式打开
        int fd = open("/tmp/fifo", O_WRONLY);
        if (fd == -1){
            printf("(%d)open error\n",getpid());
            exit(1);
        }

        //7. 发送相关数据
        char write_buff[] = "hello word";
        write(fd, write_buff,sizeof(write_buff));

        //8. 关掉写文件描述符
        if (close(fd) == -1){
            printf("(%d) close error\n",getpid());
            exit(1);
        }

        wait(NULL);
        exit(0);
    }
}
buff,sizeof(write_buff));

        //8. 关掉写文件描述符
        if (close(fd) == -1){
            printf("(%d) close error\n",getpid());
            exit(1);
        }

        wait(NULL);
        exit(0);
    }
}

关于技术交流

此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。二维码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theboynoName

感谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值