linux进程间管道通信pipe与fifo

来源:互联网 发布:51单片机项目外包网 编辑:程序博客网 时间:2024/05/21 13:26

进程间通信的方式有很多种,管道通信就是其中一种,所谓管道通信就是通信流程类似管道流水具有单向性,数据只能从一个方向流向另一个方向。不能反向传输。

管道通信又分为无名管道通信、有名管道通信。无名管道只能用于父子进程间通信、自身通信(不常用意义不大),相互独立的进程不能通信,无名管道通信示意如下:


实际管道1、2的角色是由内核充当的。

pipe函数格式如下

#include <unistd.h>int pipe(int pipefd[2]);
函数返回值:管道创建成功返回0 失败返回 -1

函数的参数:数量为2的整形数组

规定无名管道通信 fd[1]数据写入端  fd[0]数据读取端

举例说明无名管道的用法,其实很简单:

#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/wait.h>#include <stdlib.h>int main(int argc,char *argv[]){  int fd[2];  //定义管道数组  pid_t pid,pd;      //定义进程ID  char buffer[100];  //定义管道数据缓冲区  if(pipe(fd)<0)        printf("creat pipe failed!!\r\n");   //管道创建失败  pid=fork();                    //创建子进程  if(pid<0)        {    printf("creat fork failed");   //创建进程失败  }else if(pid==0)   {     close(fd[1]);         //子进程 关闭fd[1] 只用于接收数据     printf("this is %d child process wait data now!!\r\n",getpid());  //打印子进程进程号 显示等待数据     read(fd[0],buffer,50);    //管道读取数据存放在buffer中     printf("read string is %s\r\n",buffer);   //打印读取的数据     close(fd[0]);                              //关闭fd[0]  }else if(pid>0)    {      close(fd[0]);                       //父进程关闭fd[0]  用于穿送数据      printf("this is father process!!\r\n");   //打印身份标识  这是父进程      sleep(5);                              //睡眠5秒钟  观察子进程状态      write(fd[1],argv[1],10);            //向管道写入数据 数据为传入的第二个参数      printf("wait child exit\r\n");       //等待子进程退出      pd= wait(NULL);                                printf("child process %d is exit\r\n",pd);  //子进程退出 打印子进程进程号      close(fd[1]);                     //关闭管道写入口  }  exit(0);             //进程结束}
执行以上代码结果:

[root@localhost pipe]# gcc -o test test.c [root@localhost pipe]# lstest  test.c[root@localhost pipe]# ./test 123456this is father process!!this is 3199 child process wait data now!!wait child exitread string is 123456child process 3199 is exit
可以看出父进程先执行,随后进入休眠状态,子进程开始执行。等待管道数据,父进程苏醒以后,向管道写入数据,随后等待子进程结束,子进程读取管道信息打印出来之后,结束进程。父进程收到子进程结束的消息。父进程打印结束的子进程进程号,结束自身。程序运行结束。

结果表明如果管道为空,读取管道的进程将会阻塞,不再继续执行直到数据的来临。(可以通过运行./test 不向函数传入参数实验,程序将卡死)。

那么有读取空管道而进程不阻塞的办法么?其实是有的在linux发布的2.6.xx版本之后就加入了pipe2()函数,与pipe不同的是,pipe2函数加入了第二个参数,参数填写O_NOBLOCK之后,进程在读取空管道或写入非空管道时将不会阻塞进程。这里不在演示。

下面介绍有名管道fifo,fifo的优势在于它可以在不同进程间通讯,实质上fifo是一种特殊的文件,fifo一旦创建就可以用open函数打开它,一般的文件操作函数(close、write、read)都可以用于fifo,相关进程通过访问该文件,通过读取或写入相应数据达到进程间通讯的目的。

创建有名管道的函数结构如下:

#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *pathname,,mode_t mode);

当管道创建失败返回值小于0。

参数一是所创建fifo的名称可以跟路径。

参数二是fifo的参数:其中最重要的是 0_CREAT 和O_EXCL这两个参数,这两个参数同时使用,O_CREAT创建fifo,O_EXCL创建管道失败返回错误。

同无名管道一样,访问有名管道无法满足进程要求时,进程将会阻塞,如试图读取空的fifo。如果想避免这种状况在打开fifo加入O_NONBLOCK这个参数即可,当访问无法满足要求时,进程不阻塞,但立刻出错返回,errno是ENXIO。
下面写两个例程说明一下fifo的用法。为什么是两个?因为fifo可以用于不同进程。所以这里利用两个例程来说明。

例程1:创建并读取fifo 打开fifo时加入O_NONBLOCK即使读取空fifo进程不会阻塞。

#include <stdio.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/types.h>#include <stdlib.h>#include <string.h>#define MYFIFO "/zhangchao/fifo/fifotest" //定义管道路径 名称int main(void){  int fd; //定义文件描述符  char buf[100]; //读取数据缓存区  if(mkfifo(MYFIFO,O_CREAT | O_EXCL)<0) //创建fifo管道 如果没有就创建 创建失败返回错误 {  printf("创建FIFO失败\r\n");  return 0; }  printf("等待读取管道数据\r\n"); fd=open(MYFIFO,O_RDONLY | O_NONBLOCK);//打开fifo管道 只读打开 没有数据不等待不会导致进程阻塞 printf("文件描述符:%d\r\n",fd);  while(1)  {    memset(buf,0,sizeof(buf)); //清空缓存区    if(read(fd,buf,100)<0)   //没有数据       printf("管道没有数据\r\n");       printf("读出管道数据为:%s\r\n",buf);  sleep(1);}
close(fd);return 0; }
例程2:写fifo

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#define MYFIFO "/zhangchao/fifo/fifotest"int main(void){  int fd;  char buf[]="123456789";fd=open(MYFIFO,O_WRONLY|O_NONBLOCK);if(write(fd,buf,100)<0) {   printf("管道有数据没有被读走\r\n");  }  close(fd); return 0;}


编译两个例程:

[root@localhost fifo]# gcc -o fifo1 fiforead.c [root@localhost fifo]# gcc -o fifo2 fifowrite.c      [root@localhost fifo]# lsfifo1  fifo2  fiforead.c  fifowrite.c[root@localhost fifo]# ./fifo1等待读取管道数据文件描述符:3读出管道数据为:读出管道数据为:读出管道数据为:读出管道数据为:读出管道数据为:读出管道数据为:读出管道数据为:123456789读出管道数据为:读出管道数据为:读出管道数据为:读出管道数据为:读出管道数据为:
在另外一个终端执行

[root@localhost fifo]# ./fifo2

在文件目录可以查看一下文件情况

[root@localhost fifo]# lsfifo1  fifo2  fiforead.c  fifotest  fifowrite.c[root@localhost fifo]# ls -l fifotest p-wx------. 1 root root 0 9月  15 22:32 fifotest
程序运行之后在文件目录多了 fifotest 文件,查看它的属性属于以p开头的管道文件,更加可以说明fifo有名管道其实就是一种特殊的文件。

如果使用命令行界面,切换终端的方式为ctrl+alt+F(1~6);

代码运行平台CentOS6.3










阅读全文
0 0
原创粉丝点击