Linux进程间的通信方式之管道
2017-05-29 13:35
435 查看
简介
管道是进程间通信(IPC)的一种重要方式,在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的,如图。它由进程之间的关系可以分为匿名管道和命名管道。匿名管道即两进程之间存在血缘关系时使用的管道,命名管道即两进程之间不存在血缘关系时使用的管道。匿名管道
我们所说的管道(pipe)它用于具有血缘关系的进程通信。创建管道使用语句#include<unistd.h> int pipe(int filedes[2])
调⽤pipe函数时在内核中开辟⼀块缓冲区(称为管道)⽤于通信,它有⼀个读端⼀个写端,然后通过filedes参数传出给⽤户程序两个⽂件描符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输⼊1是标准输出⼀样)。所以管道在⽤户程序看起来就像⼀个打开的⽂件,通过read(filedes[0]);或者write(filedes[1]);向这个⽂件读写数据其实是在读写内核缓冲区。pipe函数调⽤成功返回0,调⽤失败返回-1。
测试代码
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> int main() { int _pipe[2]; int ret = pipe(_pipe); if(ret == -1) { printf("create pipe error!"); return 1; } pid_t id = fork(); if( id < 0 ) { printf("fork error!"); return 2; } else if( id == 0 ) { close(_pipe[0]); int i =0; char *_mesg_c=NULL; while(i<100) { _mesg_c="i am child!"; write(_pipe[1], _mesg_c, strlen(_mesg_c)+1); sleep(1); i++; } } else { close(_pipe[1]); char _mesg[100]; int j = 0; while(j<100) { memset(_mesg, '\0', sizeof(_mesg)); read(_pipe[0], _mesg, sizeof(_mesg)); printf("%s\n",_mesg); j++; } } return 0; }
注意
1.两个进程通过⼀个管道只能实现单向通信。例如父进程写,子进程就只能读。2.管道的生命周期是随进程的。
3.如果所有指向管道写端的⽂件描述符都关闭了,⽽仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后再次read会返回0,就像读到⽂件末尾⼀样。
4.如果有指向管道写端的⽂件描述符没关闭,⽽持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
5.如果所有指向管道读端的⽂件描述符都关闭了,这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终⽌。
6.如果有指向管道读端的⽂件描述符没关闭,⽽持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写⼊数据并返回。
命名管道
上面所说的只能用于有血缘关系的进程间通信的管道称为匿名管道。另一种可以用于没有血缘关系的进程通信的管道我们称之为命名管道(FIFO),FIFO提供了一个路径名与之关联,以FIFO的⽂件形式存储于⽂件系统中。命名管道是⼀个设备⽂件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(first input first output)总是按照先进先出的原则工作,第⼀个被写⼊的数据将⾸先从管道中读出。命名管道的创建通常使用shell命令mknod和mkfifo:
mkfifo name
另一种fifo创建方式在程序中创建,使用函数mknod或者mkfifo:
#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);
参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于⽂件创建的种类,它只在创建设备⽂件时才会⽤到。这两个函数调⽤成功都返回0,失败都返回-1。
测试代码
fifo read端:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #define _PATH_ "/tmp/file.tmp" #define _SIZE_ 100 int main() { int fd = open(_PATH_, O_RDONLY); if(fd < 0) { printf("open file error!\n"); return 1; } char buf[_SIZE_]; memset(buf, '\0', sizeof(buf)); while(1) { int ret = read(fd, buf, sizeof(buf)); if (ret <= 0) { printf("read end or error!\n"); break; } printf("%s\n", buf); if( strncmp(buf, "quit", 4) == 0 ) { break; } } close(fd); return 0; }
fifo write端:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #define _PATH_ "/tmp/file.tmp" #define _SIZE_ 100 int main() { int ret = mkfifo(_PATH_,0666|S_IFIFO); if(ret == -1) { printf("mkfifo error\n"); return 1; } int fd = open(_PATH_, O_WRONLY); if(fd < 0) { printf("open error\n"); } char buf[_SIZE_]; memset(buf, '\0', sizeof(buf)); while(1) { scanf("%s", buf); int ret = write(fd, buf, strlen(buf)+1); if(ret < 0) { printf("write error\n"); break; } if( strncmp(buf, "quit", 4) == 0 ) { break; } } close(fd); return 0; }
相关文章推荐
- linux的IPC进程通信方式-匿名管道(一)
- linux进程间的通信方式:管道和FIFO
- Linux下进程的通信方式: 有名管道(命名管道)
- Linux下的进程通信方式: 管道通信详解
- 【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)
- linux先进程通信 管道方式和消息队列
- msgget();msgsnd();msgrcv();msgctl(); 消息队列 Linux进程间的通信方式之消息队列
- Linux的进程/线程通信方式总结
- linux下父子进程间的通信——管道
- linux进程通信--有名管道(FIFO)(含实例代码)
- linux基础编程:进程通信之管道
- Linux 进程间通讯之有名管道方式
- Linux进程通信:命名管道FIFO小结
- Linux进程通信方法--管道
- linux进程间的通信(C): 命名管道
- Linux\Unix IPC进程通信实例分析(一):共享内存通信---文件映射mmap方式
- Linux进程通信:管道
- Linux进程通信 命名管道
- Linux进程通信总结(二) --管道
- linux 命名管道fifo实现进程间小数据通信