专栏首页Zaqdt_ACMLinux进程间通信--管道(pipe和fifo)

Linux进程间通信--管道(pipe和fifo)

       通过管道来实现进程间的通信的方法很经典,因为多个进程共享3-4G中的内核,所以在内核中存在一个管道(缓冲区),然后进程通过连接管道的两端从而实现通信。假如说我们现在有一根管道,我们从左端放入一个小球,那么它会从右端滚出来,那么如果我们同时向两端都放入一个小球,那么就不可能实现交叉传递了,所以管道是半双工通信(即双方都可以发送信息,但是双方不能同时发送信息),因此管道的两端一端是读端,一端是写端。那么要实现两个进程的同时读写操作,就需要用两个管道。

pipe

       首先先来说一下pipe,这是一个匿名管道(为啥叫匿名呢,下面讲命名管道的时候就知道了),实现方式是循环队列它只能用于有血缘关系的进程间通信。首先我们先来看一下pipe函数的原型:

       #include <unistd.h>
       
       int pipe(int pipefd[2]);

       传入的参数是一个大小为2的数组,然后就得到了两个文件描述符pipefd[0]和pipefd[1],前者用来指向管道的读端,后者用来指向写端。用一个父子进程来举例,如果要实现父子进程间的通信,在fork前就需要创建一个pipe管道,如果创建成功返回0,如果失败返回-1并设置errno,由于子进程复制了父进程的PCB,所以子进程也有父进程的文件描述符表,因此父子进程的pipefd都指向了同一个pipe管道,然后我们要规定管道的传输方向,如果我们要求父写子读的话,我们就在父进程中close(pipefd[0]),在子进程中close(pipefd[1])就好了,创建好管道后我们通过write和read函数进行读写操作。

那么在使用pipe通信的时候可能会遇到以下的几种情况:

1. 当读管道时,如果管道中没有数据,则会阻塞,直到管道另一端写入数据。

          2. 当写管道时,如果管道中已经满了,则会阻塞,直到管道另一端读出数据(可见读出数据时,管道中将不会保留该数据)。

          3. 当管道写端关闭时,读端读完管道内的数据时,如果再次去读没有数据的管道会返回0,相当于读到了EOF。

          4. 当管道读端关闭时,如果写端在写入数据时,产生SIGPIPE信号,写进程默认情况下会终止进程。

       下面以父子进程的例子,来写一个程序来实现一下,代码如下:

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

int main(void)
{
        char buf[1024] = "Hello Child\n";
        char str[1024];
        int fd[2];
        if(pipe(fd) == -1){
                perror("pipe");
                exit(1);
        }
        pid_t pid = fork();
        // 父写子读 0写端 1读端
        if(pid > 0){
                printf("parent pid\n");
                close(fd[0]);                    // 关闭读端
                sleep(5);
                write(fd[1], buf, strlen(buf));  // 在写端写入buf中的数据
                wait(NULL);
                close(fd[1]);
        }
        else if(pid == 0){
                close(fd[1]);                   // 关闭写端
                int len = read(fd[0], str, sizeof(str));   // 在读端将数据读到str
                write(STDOUT_FILENO, str, len);
                close(fd[0]);
        }
        else {
                perror("fork");
                exit(1);
        }
        return 0;
}

       运行结果如下:

parent pid
Hello Child

       我们可以用fpathconf来查看管道的缓冲区大小,通过传入文件描述符和_PC_PIPE_BUF两个参数来获得一个long整型的值。代码如下:

printf("%ld\n", fpathconf(fd[0], _PC_PIPE_BUF));

       还有就是我们可以通过设置O_NONBLOCK参数来实现非阻塞的情况,也就是说当一个进程还没有写数据时,另一个读进程就会阻塞在那里,那么如果设置了O_NONBLOCK参数,该进程就不会阻塞在那里,会返回-1,并设置errno为EAGAIN,可以用goto语句或者while循环实现,那么设置O_NONBLOCK使用fcntl函数

fifo

       fifo用来创建一个命名管道,可以解决非血缘关系的进程间通信,它的底层的实现原理和匿名管道相同,只不过是生成了一个可见的管道文件。管道文件用mkfifo命令来创建,如下图所示:

       这个管道文件连接一个在内核中的管道,那么这个管道文件对于所有的进程都是可见的,那么进程通过打开这个管道文件就可以通过管道文件所连接的管道来实现非血缘关系的进程间通信了。因为这个管道有一个所有进程都可以访问到的管道文件,所以fifo叫做命名管道,那么同理,pipe就只能通过fork的方式来复制文件描述符表来共享管道,而其他的进程却访问不到,所以叫做匿名管道。

       下面也通过代码,来简单的实现一下fifo的效果,这里我提前用mkfifo的命令来创建了一个管道文件,当然也可以在代码中使用mkfifo函数来创建,先来看一下写操作的进程的代码:

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

int main(int argc,char *argv[])
{
        if(argc < 2){
                printf("run error\n");
                exit(1);
        }
        char buf[1024] = "Hello, I'm Charles\n";
        int fd = open(argv[1], O_WRONLY);
        if(fd == -1){
                perror("open file");
                exit(1);
        }
        write(fd, buf, strlen(buf));
        close(fd);
        return 0;
}

     下面是执行读操作的代码:

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

int main(int argc, char *argv[])
{
	if(argc < 2){
		printf("run error\n");
		exit(1);
	}
	char buf[1024];
	int fd = open(argv[1], O_RDONLY);
	int len = read(fd, buf, sizeof(buf));
	write(STDOUT_FILENO, buf, len);
	close(fd);
	return 0;
}

       然后我们开两个终端去分别运行这两个程序,我们先运行写操作的程序,然后再运行读操作的程序(当你运行写操作的程序时会阻塞在write,当运行读操作的程序时才会往下执行,这个就是上面所说的四种情况中的一种),最终结果如下:

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux 文件I/O操作(简单实现文件复制)

           简单的实现一下文件的复制操作,直接贴源码了,中间也有一些注释,至于更多的详细的命令参数,推荐看下这篇博客,讲的很详细:传送门

    Ch_Zaqdt
  • CodeForces 954A Diagonal Walking

            题意是给一个长度为n的字符串,然后要缩短这个字符串,字符串中UR或者RU的长度可以缩为1,求最短长度。

    Ch_Zaqdt
  • CodeForces 934B A Prosperous Lot(水题)

          题意就是从0-9这10个阿拉伯数字里找圈,比如0,4,6,8,9这五个数都是有圈的,其中8有两个圈,这道题就是输入一个数n,让你输出一个含有n个圈的...

    Ch_Zaqdt
  • Super快报第23期:中国移动的4G

    1、中移动董事长:微信和Skype比电信联通更可怕 巴塞罗那MWC移动世界大会,中国移动董事长奚国华游走于产业链各方,向外界频繁传递他对中国移动主导的4G标准的...

    罗超频道
  • 【先行者】一份不合格的作业的讲解

    然后我收到了几份作业,其中有二份作业不是特别的合格。就作业本身来讲吧,也不能说它是错的,但它确实是不对,把作业图贴在下面大家看看,

    web前端教室
  • 腾讯国双微信法院方案

    腾讯&国双微信法院产品是以微信小程序为平台,腾讯云底层能力为基础,通过与公众号功能融合,实现当事人完成网上立案、诉讼费缴纳、送达文书签收、在线纠纷处理等全流程诉...

    腾讯云视频
  • 微信架构相关文章

    http://www.blogjava.net/yongboy/archive/2014/03/05/410636.html

    杉枫
  • 微信:订阅号功成身退,小游戏方得永生?

    鉴于小游戏的分享滥用行为对于熟人社交关系的透支,小游戏有可能是压垮熟人社交的最后一根稻草。

    深圳秀思科技有限公司
  • 54. Python 爬虫(3)

    如果已经知道cookie,或者说你是通过抓包获取到的cookie,直接放在header的信息中直接登陆就可以;

    py3study
  • 王老板Python面试(4):Python面试攻略(嗨谈篇)

    答:*args表示可变参数(variadic arguments),它允许你传入0个或任意个无名参数,这些参数在函数调用时自动组装为一个tuple; **kwa...

    Python之道

扫码关注云+社区

领取腾讯云代金券