Linux进程间通信(一)之无名管道(PIPE)和有名管道(FIFO)
2017-10-21 09:43
866 查看
何为进程间通信
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。简单说就是进程之间可以相互发送数据。IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。Socket用在网络编程中。
管道
管道通常指无名管道,是 UNIX 系统IPC最古老的形式。特点有:
1、它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
2、它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间),实现依赖父子进程文件共享。
3、它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
函数原型
#include <unistd.h> int pipe(int filedes[2]);
由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。
单个进程中的管道几乎没有任何用处。通常,调用pipe的进程接着调用fork,这样就创建了从父进程到子进程(或反向)的IPC通道。
父子进程都有读端和写端,子进程的是从父进程复制过来的。
进程复制的时候复制了PCB、文件结构体,不止拷贝了文件描述符。
调用fork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])。
举例:
例1:只有一个进程,读写管道文件
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include <unistd.h> int main() { int fd[2]; pipe(fd); write(fd[1],"hello world",12); sleep(2); char buff[128]; int n = read(fd[0],buff,127); printf("read:%s\n",buff); close(fd[0]); close(fd[1]); }
输出结果是:
例2:创建由父进程到子进程的管道,父进程写入helloworld,子进程读取数据到buf,然后打印输出。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> int main() { int fd[2]; pipe(fd);//fd[0]是读,fd[1]是写 if(fork() != 0) { close(fd[0]); write(fd[1],"helloworld",10); } else { close(fd[1]); char buf[128] = {0}; read(fd[0],buf,127); printf("%s\n",buf); } return 0; }
输出结果如下:
例3:使用管道文件,创建由父进程到子进程的管道,父进程循环输入数据,子进程循环读取数据,当写端输入end时,父子线程都结束。
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> int main() { int fd[2]; pipe(fd); pid_t pid = fork(); if(pid == 0) { close(fd[1]); char buff[128] = {0}; int n = 0; while((n = read(fd[0],buff,127)) > 0) { printf("child read:%s\n",buff); } close(fd[0]); } else { close(fd[0]); while(1) { char buff[128] = {0}; printf("input:\n"); fgets(buff,128,stdin); if(strncmp(buff,"end",3)==0) { break; } write(fd[1],buff,127); } close(fd[1]); } exit(0); }
注意:
(1)当读一个写端已被关闭的管道是,在所有数据都被读取后,read返回0,以指示达到文件结束处。管道的写端彻底关闭(父子进程的写端都得关闭,否则会有进程处于未关闭状态,还在等待写),读端返回一个0,父子进程都得关闭。
(2)如果写一个读端已被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从处理程序返回,则write返回-1,errno设置为EPIPE。
有名管道
有名管道也叫命名管道,在文件系统目录中存在一个管道文件。管道文件仅仅是文件系统中的标示,并不在磁盘上占据空间。在使用时,在内存上开辟空间,作为两个进程数据交互的通道。
管道文件的创建:
1) 在shell中使用mkfifo 命令
mkfifo filename
2) mkfifo 函数 (在代码中使用其创建管道文件)
函数原型:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename,mode_t mode);
使用方式:
1)使用open函数打开管道文件
如果一个进程以只读(只写)打开,那么这个进程会被阻塞到open,直到另一个进程以只写(只读)或者读写。
2)使用read函数读取内容
read读取普通文件,read不会阻塞。而read读取管道文件,read会阻塞运行,直到管道中有数据或者所有的写端关闭。
3)使用write函数发送内容,使用close函数关闭打开的文件。
举例:
首先通过命令创建管道文件:fifo
mkfifo fifo
例1:一个进程写,一个进程读。
写端:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <fcntl.h> #include <signal.h> //信号处理函数。 void fun(int sig) { printf("sig == %d\n",sig); } int main() { signal(SIGPIPE,fun); int fd = open("fifo",O_WRONLY); // int fd = open("fifo",O_RDWR); assert(fd != -1); printf("fd = %d\n",fd); char buff[128] = {0}; while(1) { printf("input:\n"); fgets(buff,128,stdin); write(fd,buff,strlen(buff)); if(strncmp(buff,"end",3)==0) { break; } } close(fd); exit(0); }
读端:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <fcntl.h> int main() { int fd = open("fifo",O_RDONLY); assert(fd != -1); printf("fd = %d\n",fd); char buff[128] = {0}; // while(1) // { // int n = read(fd,buff,127);//127是期望要读的字符个数 // if(strncmp(buff,"end",3)==0) // { // break; // } // printf("read:%s\n",buff); // printf("n = %d\n",n); // } int n = 0; while((n = read(fd,buff,127))>0) { printf("read:(n = %d)%s\n",n,buff); //将buff中的数据清空 memset(buff,0,128); } close(fd); exit(0); }
输出结果:
(1)正常写入和读取,输入end结束读写。
(2)写端彻底关闭、读端read返回0,也会关闭。
(3)管道的读端关闭,当写端继续写入数据时,会产生SIGPIPE信号,修改默认响应方式,故信号被捕获之后执行信号处理函数fun。
例2:有三个进程分别是进程C(写端)和进程A、B (读端),写端写入数据“Hello World”,A读端读取数据,B再去读取。
写端C:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #include <fcntl.h> int main() { int fw = open("myfifo",O_WRONLY); assert(fw != -1); write(fw,"helloworld",10); // sleep(2); // write(fw,"how are you?",12);//继续写入数据 sleep(15); // close(fw); return 0; }
读端A
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #include <fcntl.h> int main() { int fr = open("myfifo",O_RDONLY); assert(fr != -1); char buf[128] = {0}; int len = read(fr,buf,5); if(len != -1) { printf("buf:%s\n",buf); } sleep(10); // close(fr); return 0; }
读端B
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #include <fcntl.h> int main() { int fr = open("myfifo",O_RDONLY); assert(fr != -1); char buf[128] = {0}; int len = read(fr,buf,5); if(len != -1) { printf("buf:%s\n",buf); } sleep(10); // close(fr); return 0; }
输出结果如下:
其中test1.c和test3.c是读文件,test2.c是写文件,需要关注的是写端和读端的关闭时间,只有写端开着,读端才能读到数据,而且如果有一个读端关的早,写端如果再写入数据会产生异常,系统会按默认处理异常信号,然后关闭,这样的话另一个读端也不能读取数据。
总结:
(1)两个进程运行时,写端彻底关闭,则读端read返回0,也会关闭。
(2)如果管道的读端关闭,继续写入数据会发生异常,写端收到未捕获的信号SIGPIPE会关闭写端。当然也可以修改默认的信号响应方式,比如增加信号处理函数。
(3)写端写入数据以后,读端不从里面读取内容:数据保持在管道中存在一段时间。管道文件的大小是0。
(4)管道通讯发送的数据若没有指定进程接收,任何一个进程只要打开的是同一个管道文件,都有可能读到数据。
(5)read读取管道中的数据,只要读过的数据就会被清空 。
有名管道和无名管道的异同点
1、相同点open打开管道文件以后,在内存中开辟了一块空间,管道的内容在内存中存放,有两个指针—-头指针(指向写的位置)和尾指针(指向读的位置)指向它。读写数据都是在给内存的操作,并且都是半双工通讯。
2、区别
有名在任意进程之间使用,无名在父子进程之间使用。
拓展:
全双工、半双工、单工通讯的区别:
单工:方向是固定的,只有一个方向可以写,例如广播。
半双工:方向不固定,但在某一刻只能有一个方向进行写,例如对讲机。
全双工:两个方向都可以同时写,例如打电话。
相关文章推荐
- (转)linux 无名管道pipe和有名管道FIFO
- (转)linux 无名管道pipe和有名管道FIFO
- 进程间通信IPC之--无名管道(pipe)和有名管道(fifo)(转)
- linux 无名管道pipe和有名管道FIFO
- 传统的进程间通信方式 1.无名管道(pipe) 2.有名管道(fifo) 3.信号(signal)
- 传统的进程间通信方式 1.无名管道(pipe) 2.有名管道(fifo) 3.信号(signal); 闹钟信号应用实例
- linux 无名管道pipe和有名管道FIFO
- 进程间通信IPC之--无名管道(pipe)和有名管道(fifo)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- 通信方式详解,无名管道pipe,有名管道fifo,共享内存share memory,消息队列msg
- Linux 进程间通信之管道(pipe),(fifo)
- Linux--进程间通信(管道及有名管道FIFO)
- linux编程---进程间通信---FIFO---有名管道
- Linux环境进程间通信(一)——管道(pipe)和命名管道(fifo)
- 无名管道pipe 有名管道fifo
- Linux环境进程间通信(一)——管道(pipe)和命名管道(fifo)
- linux进程间通信-有名管道(FIFO)
- Linux进程间通信——有名管道 FIFO 详解
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux环境进程间通信(一)——管道(pipe)和命名管道(fifo)