进程间通信管道的应用


管道的概念:


管道是一种最基本的IPC机制,由pipe函数创建:

#include<unistd.h>

int pipe(int fileds[2]);

调用pipe函数时在内核中开辟一块缓冲区用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端。

进程在管道间通信:

1.父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。

2.父进程调用fork创建子进程,子进程有两个文件描述符指向同一管道。

3.父进程关闭管道读端,子进程关闭管道斜段。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入,从读端流出,这样就实现了进程间通信。

代码实现如下:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 
  6 int main()
  7 {
  8     int _pipe[2];
  9     int ret = pipe(_pipe);
 10     if(ret == -1){
 11        printf("create pipe erro!errno code is:%d\n",errno);
 12        return 1;
 13       }
 14     pid_t id=fork();
 15     if(id<0){
 16       printf("fork error!");
 17       return 2;
 18      }else if(id == 0){
 19        close(_pipe[0]);
 20        int i=0;
 21        char* _mesg_c=NULL;
 22        while(i<100){
 23          _mesg_c="I am a child!";
 24            write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 25            sleep(1);
 26            i++;
 27        }
 28      }else{//father
 29            close(_pipe[1]);
 30            char _mesg[100];
 31            int j=0;
 32            while(j<100) {
 33            memset(_mesg,‘\0‘,sizeof(_mesg));
 34            read(_pipe[0],_mesg,sizeof(_mesg));
 35            printf("%s\n",_mesg);
 36                 j++;
 37            }
 38         }
 39         return 0;
 40 
 41 
 42 }

程序运行结果如下:

管道的四个特殊情况:

1.所有指向管道写端的文件描述符都关闭了,仍然有进程从管道的读端读取数据,当管道中剩余的数据都被读取后,再次读取会返回0,就像读到文件末尾一样。

代码实现:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8     int _pipe[2];
  9     int ret = pipe(_pipe);
 10     if(ret == -1){
 11        printf("create pipe erro!errno code is:%d\n",errno);
 12        return 1;
 13       }
 14     pid_t id=fork();
 15     if(id<0){
 16       printf("fork error!");
 17       return 2;
 18      }else if(id == 0){//child
 19        close(_pipe[0]);
 20        int i=0;
 21        char* _mesg_c=NULL;
 22        while(i<10){
 23          _mesg_c="I am a child!";
 24            write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 25            sleep(1);
 26            i++;
 27        }
 28            close(_pipe[1]);
 29      }else{//father
 30            close(_pipe[1]);
 31            char _mesg[100];
 32            int j=0;
 33            while(j<100) {
 34            memset(_mesg,‘\0‘,sizeof(_mesg));
 35            int ret = read(_pipe[0],_mesg,sizeof(_mesg));
 36            printf("%s:code is:%d\n",_mesg,ret);
 37                 j++;
 38            }
 39            if(waitpid(id,NULL,0)<0)
 40              {
 41                return 3;
 42              }
 43         }
 44         return 0;
 45 }

运行结果如下:

2.指向管道写端的文件描述符没关闭,持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读取数据,当管道中剩余的数据都被读取后,再次读取会阻塞,直到管道中有数据可读了才读取数据并返回。

代码实现:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 #include<sys/wait.h>
  6 
  7 int main()
  8 {
  9     int _pipe[2];
 10     int ret = pipe(_pipe);
 11     if(ret == -1){
 12        printf("create pipe erro!errno code is:%d\n",errno);
 13        return 1;
 14       }
 15     pid_t id=fork();
 16     if(id<0){
 17       printf("fork error!");
 18       return 2;
 19      }else if(id == 0){//child
 20        close(_pipe[0]);
 21        int i=0;
 22        char* _mesg_c=NULL;
 23        while(i<20){
 24           if(i<10){
 25               _mesg_c="I am a child!";
 26               write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 27             }
 28            sleep(1);
 29            i++;
 30        }
 31            close(_pipe[1]);
 32      }else{//father
 33            close(_pipe[1]);
 34            char _mesg[100];
 35            int j=0;
 36            while(j<20) {
 37            memset(_mesg,‘\0‘,sizeof(_mesg));
 38            int ret = read(_pipe[0],_mesg,sizeof(_mesg));
 39            printf("%s:code is:%d\n",_mesg,ret);
 40                 j++;
 41            }
 42            if(waitpid(id,NULL,0)<0)
 43              {
 44                return 3;
 45              }
 46         }
 47         return 0;
 48 }

运行结果:

3.如果所有指向管道读端的文件描述符都关闭了,这时有进程向管道的写端write,该进程会收到信号SIGPIPE,会导致进程异常终止。

代码实现:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 #include<sys/wait.h>
  6 
  7 int main()
  8 {
  9     int _pipe[2];
 10     int ret = pipe(_pipe);
 11     if(ret == -1){
 12        printf("create pipe erro!errno code is:%d\n",errno);
 13        return 1;
 14       }
 15     pid_t id=fork();
 16     if(id<0){
 17       printf("fork error!");
 18       return 2;
 19      }else if(id == 0){//child
 20        close(_pipe[0]);
 21        int i=0;
 22        char* _mesg_c=NULL;
 23        while(i<20){
 24           if(i<10){
 25               _mesg_c="I am a child!";
 26               write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 27             }
 28            sleep(1);
 29            i++;
 30        }
 31      }else{//father
 32            close(_pipe[1]);
 33            char _mesg[100];
 34            int j=0;
 35            while(j<3) {
 36            memset(_mesg,‘\0‘,sizeof(_mesg));
 37            int ret = read(_pipe[0],_mesg,sizeof(_mesg));
 38            printf("%s:code is:%d\n",_mesg,ret);
 39                 j++;
 40            }
 41            close(_pipe[0]);
 42            sleep(10);
 43            if(waitpid(id,NULL,0)<0)
 44              {
 45                return 3;
 46              }
 47         }
 48         return 0;
 49 }

运行结果:

4.如果有指向管道读端的文件描述符没关闭,而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。

代码实现:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 #include<sys/wait.h>
  6 
  7 int main()
  8 {
  9     int _pipe[2];
 10     int ret = pipe(_pipe);
 11     if(ret == -1){
 12        printf("create pipe erro!errno code is:%d\n",errno);
 13        return 1;
 14       }
 15     pid_t id=fork();
 16     if(id<0){
 17       printf("fork error!");
 18       return 2;
 19      }else if(id == 0){//child
 20 //       close(_pipe[0]);
 21        int i=0;
 22        char* _mesg_c=NULL;
 23        while(i<20){
 24 //          if(i<10){
 25               _mesg_c="I am a child!";
 26               write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 27   //          }
 28            sleep(1);
 29            i++;
 30        }
 31      close(_pipe[1]);
 32      }else{//father
 33            close(_pipe[1]);
 34            char _mesg[100];
 35            int j=0;
 36            while(j<3) {
 37            memset(_mesg,‘\0‘,sizeof(_mesg));
 38            int ret = read(_pipe[0],_mesg,sizeof(_mesg));
 39            printf("%s:code is:%d\n",_mesg,ret);
 40                 j++;
 41            }
 42   //         close(_pipe[0]);
 43            sleep(10);
 44            if(waitpid(id,NULL,0)<0)
 45              {
 46                return 3;
 47              }
 48         }
 49         return 0;
 50 }

运行结果:


管道通信的特点:

1.它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间) 。

2.它是一个单向通信的通信模式,具有固定的读端和写端。

3.管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

命名管道:

管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管道(namedpipe或FIFO)提出后,该限制得到了克服。

FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是, FIFO严格遵循先进先出(firstinfirstout),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

系统调用形式:

#include<sys/types.h>

#include<sys/stat.h>

int mknod(const char*path,mode_t mod,dev_t dev)

int mkfifo(const  char*path,mode_t mode)

该函数的第一个参数是一个路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open(),函数中的mode参数相同,有以下几种:

O_RDONLY:读管道

O_WRONLY:写管道

O_RDWR:读写管道

O_NONBLOCK:非阻塞

O_CREAT:

O_EXCL:

一般文件的I/O函数都可以用于FIFO,如close、read、write等等。

FIFO读规则

约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。

– 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。

– 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:

A.当前FIFO内有数据,但有其它进程在读这些数据;

B.另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量。读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。

FIFO写规则

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

对于设置了阻塞标志的写操作:

– 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。

– 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:

– 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。

– 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;

代码实现:

fiforead.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 #include<string.h>
  7 #define _PATH_"/tmp/file.tmp"
  8 #define _SIZE_ 100
  9 
 10 int main()
 11 {
 12   int fd = open(_PATH_,O_RDONLY);
 13   if(fd<0){
 14     printf("open file error!\n");
 15     return 1;
 16   }
 17   char buf[_SIZE_];
 18   memset(buf,‘\0‘,sizeof(buf));
 19   while(1){
 20     int ret = read(fd,buf,sizeof(buf));
 21     if(ret<=0)//error or end of file
 22       {
 23          printf("read end or error!\n");
 24          break;
 25       }
 26   }
 27    close(fd);
 28      return 0;
 29 }

fifowrite.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 #include<string.h>
  7 #define _PATH_"/tmp/file.tmp"
  8 #define _SIZE_ 100
  9 
 10 int main()
 11 {
 12   int ret=mkfifo(_PATH_,0666|S_IFIFO);
 13   if(ret==-1){
 14       printf("mkfifo error\n");
 15       return 1;
 16     }
 17 
 18   int fd = open(_PATH_,O_RDONLY);
 19   if(fd<0){
 20     printf("open file error!\n");
 21     return 1;
 22   }
 23   char buf[_SIZE_];
 24   memset(buf,‘\0‘,sizeof(buf));
 25   while(1){
 26     scanf("%s",buf);
 27     int ret = write(fd,buf,sizeof(buf)+1);
 28     if(ret<=0)//error or end of file
 29       {
 30          printf("write  error!\n");
 31          break;
 32       }
 33       if(strncmp(buf,"quit",4)==0){
 34         break;
 35        }
 36   }
 37    close(fd);
 38      return 0;
 39 }
时间: 2024-08-12 05:58:47

进程间通信管道的应用的相关文章

Linux程序设计学习笔记----进程间通信——管道

转载请注明出处: http://blog.csdn.net/suool/article/details/38444149, 谢谢! 进程通信概述 在Linux系统中,进程是一个独立的资源管理单元,但是独立而不孤立,他们需要之间的通信,因此便需要一个进程间数据传递.异步.同步的机制,这个机制显然需要由OS来完成管理和维护.如下: 1.同一主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO),消息队列(Message Queue)和共享内存(Share Memory).无名管道多用于亲

Linux 进程间通信-管道

进程间通讯———管道 Linux 进程间通信-管道 进程是一个独立的资源分配单位,不同进程之间的资源是相互独立的,没有关联,不能在一个进程中直接访问另一个进程中的资源.但是,进程不是孤立的,不同的进程之间需要信息的交换以及状态的传递,因此需要进程间数据传递.同步与异步的机制. 此篇博文记录管道. 管道pipe 管道是进程间通信的主要手段之一.一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端.管道是一种特殊的文件,它不属于某一种文件系统,

进程间通信---管道

每个进程各自有着不同的用户地址空间,任何一个进程的全局变量在另一个进程中是看不到的,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2在从内核中把数据读走,内核提供的这种机制称为进程间通信. 进程间通信的本质:让不同的进程看到同一份系统资源. 进程通信方式:管道(pipe) pipe函数: #include <unistd.h> int pipe(int fd[2]); 调用pipe函数时在内核中开辟一块缓冲区(管道)用于通信,它有一个读端

进程间通信 ---- 管道与FIFO 用法技巧

1.管道的创建 1.1 mkfifo(const char *pathname,mode_t mode); 函数已隐含指定O_CREAT|O_EXCL,所以它要么创建一个新的FIFO,要么返回EEXIST错误(已存在). 所以在创建已存在FIFO或新的FIFO,应该先调用mkfifo,并检查返回值 是否是EEXIST错误,若是EEXIST错误,则调      用open函数. 2.FIFO或管道读写 2.1对管道或FIFO的write 总是往末尾添加数据,对管道或FIFO的read总是从头开始读

Linux学习笔记(12)-进程间通信|管道

Linux的进程间通信有几种方式,包括,管道,信号,信号灯,共享内存,消息队列和套接字等-- 现在一个个的开始学习! -------------------------------------------------- 管道是一个进程链接另一个进程的数据通道,它通常是把一个进程的输出,接到另一个进程的输入,从而传递数据. 在Linux的终端上,用单竖线|来表示,那么,这个符号可以做什么呢? 举个栗子,如果我用ps -ef命令,可以查看我当前所有的进程: 正如上图表示,显示出来的东西太多了,让人眼

进程间通信——管道

1.管道 管道通信--只能在一台电脑上面运行. 管道:一定是半双工的通信,只能流向一个方向(规定流向): 管道是一个进程间通信的概念,在要通信的进程间构建一个单向的数据流动的通道.数据通过该通道从一个进程流向另一个进程时是具有时间先后顺序的.就像是在进程间架起了一个"管道". 模型分析 管道的实现:在Linux(Posix标准)的操作系统下,管道是通过文件来实现的.在后来的操作系统的发展中,依然使用了文件的访问方式来使用管道,但是具体的管道已经从外存挪到了内存. 2.无名管道   只能

【linux高级程序设计】(第九章)进程间通信-管道 3

有名管道 无名管道和有名管道: 1. 管道是特殊类型的文件,在满足先入先出的原则写可以读写,不能定位读写位置. 2.管道是单向的. 3.无名管道阻塞于读写位置,而有名管道阻塞在创建位置. 4.无名管道一般只用于亲缘关系进程间通信:有名管道以磁盘文件的方式存在,可以实现本机任意两进程间通信. shell创建有名管道 mknod 管道名 p  //创建名为PIPETEST的有名管道 mknod为命令 p是参数,表示有名管道 指令 > 管道名 &   //将指令结果输入到到管道文件中 指令 <

Linux进程间通信--管道

管道概述及相关API应用 1.1 管道相关的关键概念 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道: 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中. 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出.写入的

linux学习:进程间通信—管道

1.进程间通信当中一种比較简单的方法是管道操作 /* ============================================================================ Name : Test.c Author : wangchuan Version : Copyright : Your copyright notice Description : Hello World in C, Ansi-style ==========================