传统的进程间通信——管道通信
传统的进程间通信——管道通信
-
管道是UNIX系统中最古老的进程间通信方式,是一种特殊文件读写机制
-
当进程从管道文件中读取数据时,如果管道中没有数据则进程会进入阻塞状态,直到有数据读取出来才返回,因此不需要借助信号、文件锁来协调读写时间
-
管道中的数据一旦读取完毕就会消失,因此也不需要管理文件的位置指针,所以使用管道文件比普通文件的进程间通信要方便很多
-
古老的好处是所有系统都支持,早期的管道文件是半双工,现在有些系统支持管道文件的全双工,现在绝大多数情况已经不使用管道来通信了
有名管道:
-
在文件系统中创建出一个实体的有文件名的管道文件,然后通过系统I/O的相关API来进行相关操作
使用函数创建:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 功能:创建一个有名管道文件 pathname:管道文件的名字 mode:管道文件的权限 返回值:成功返回0 失败-1
使用命令创建:
mkfifo <file>
管道单向通信的编程模型:
进程A -> 进程B 创建有名管道 打开管道 打开管道 写数据 读数据 关闭管道 关闭管道 删除管道
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main(int argc,const char* argv[]) { // 创建管道文件 if(mkfifo("fifo",0644)) { perror("mkfifo"); return -1; } // 打开管道 int fd = open("fifo",O_WRONLY); if(0 > fd) { perror("open"); unlink("fifo"); return -1; } char buf[256] = {}; for(;;) { printf(">>>"); // 使用封装好的my_gets() gets(buf); // 写文件 发送数据 write(fd,buf,strlen(buf)); // 检查是否quit if(0 == strcmp(buf,"quit")) { printf("通信结束\n"); usleep(1000); break; } } // 关闭管道 close(fd); // 删除管道 unlink("fifo"); }
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main(int argc,const char* argv[]) { // 打开管道 int fd = open("fifo",O_RDONLY); if(0 > fd) { perror("open"); return -1; } char buf[256] = {}; // 读数据 for(;;) { read(fd,buf,sizeof(buf)); printf("read:%s\n",buf); if(0 == strcmp(buf,"quit")) { printf("通信结束\n"); break; } } // 关闭管道 close(fd); }
匿名管道:
-
只在内核中创建的管道文件对象并返回该对象的文件描述符,然后使用系统IO进行相关操作,匿名管道文件不会在文件系统中显示,在创建时不需要提供路径,也不会占用磁盘空间,只是使用内核空间来临时存储数据,当关闭文件描述符后会自动回收
-
注意:只适合fork创建的有关系的父子进程之间进行通信
相关API:
#include <unistd.h> int pipe(int pipefd[2]); 功能:创建出匿名管道文件对象,并返回该对象的文件描述符 pipefd:输出型参数,用于存储文件描述符的数组,其中 pipefd[0] 用于读操作 pipefd[1] 用于写操作 使用步骤: 1、调用该函数在内核中创建出管道文件,并获取到该文件的两个文件描述符 2、通过fork创建出子进程,子进程可以直接拷贝父进程的pipefd描述符 3、写数据的进程要关闭读端,读数据的进程要关闭写端 4、发送完毕后,父子进程分别关闭文件描述符
编程模型:
父进程 -> 子进程 创建匿名管道 创建子进程 拷贝一对fd 关闭读端(fd[0]) 关闭写端(fd[1]) 写数据(fd[1]) 读数据(fd[0]) 关闭写 关闭读
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main(int argc,const char* argv[]) { // 创建匿名管道 int fd[2] = {}; if(pipe(fd)) { perror("pipe"); return -1; } // 创建子进程 if(fork()) { // 父进程 负责写 关闭读 close(fd[0]); char buf[256] = {}; for(;;) { printf("父进程>>>"); gets(buf); write(fd[1],buf,strlen(buf)); if(0 == strncmp(buf,"quit",4)) { printf("通信结束\n"); break; } usleep(1000); } usleep(1000); close(fd[1]); usleep(1000); } else { // 子进程 负责读 关闭写 close(fd[1]); char buf[256] ={}; for(;;) { read(fd[0],buf,sizeof(buf)); printf("read:%s\n",buf); if(0 == strncmp(buf,"quit",4)) { printf("父进程要结束通信\n"); break; } } close(fd[0]); } }