进程间通信之管道

时间:2022-04-24 19:08:56

无名管道(pipe)


1,管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
2,只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
3,单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中
4,数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

管道创建:

管道由调用pipe函数而创建
#include <unistd.h>int pipe(int fileds[2]); #返回值:若成功则返回0,若出错则返回-1
经由参数filedes返回两个文件描述符:fileds[0]为读而打开,fileds[1]为写而打开,fileds[1]的输出是fileds[0]的输入。但是单个进程中的管道没有什么用处。一般都是调用pipe的进程接着调用fork创建子进程,这样管道也相同的复制了一份到子进程,就创建了从父进程到子进程的IPC通道,如下图:进程间通信之管道
进程间通信之管道

数据流方向:

父进程到子进程的管道,则父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1]),如下图:进程间通信之管道
进程间通信之管道子进程到父进程的管道,则父进程关闭写端,子进程关闭读端。
当管道的一端被关闭后,下列两条规则起作用:1,当读一个写端已被关闭的管道时,在所有数据都被读取后,read返回0,以指示达到了文件结束处2,如果写一个读端已关闭的管道,则产生信号SIGPIPE注:在写管道时,常量PIPE_BUF规定了内核中管道缓冲区的大小。如果对管道调用write,而且要求写的字节数小于等于PIPE_BUF,则此操作不会与其他进程对同一管道的write操作穿插进行。(主要发生在后续要讲的FIFO),用pathconf或者fpathconf函数可以确定PIPE_BUF的值。示例:
#include "apue.h" int main(void){ int n; int pipe_fd[2]; pid_t pid; char r_buf[100]; char w_buf[4];   memset(r_buf,0,sizeof(r_buf)); memset(w_buf,0,sizeof(r_buf)); p_wbuf=w_buf; if(pipe(pipe_fd)< 0) err_sys("pipe error"); if((pid=fork()) < 0) err_sys("fork error"); else if(pid > 0) //parent { close(pipe_fd[0]);//read strcpy(w_buf,"111"); if(write(pipe_fd[1],w_buf,4)!=-1) printf("parent write over\n"); } else //child { close(pipe_fd[1]);//write n = read(pipe_fd[0], r_buf, 100); write(STDOUT_FILENO, line, n); } exit(0); }

标准IO库函数:

在标准IO库中,为管道操作提供了两个函数(前面都是Unix环境下的库函数,标准库是跨平台的,在其他平台上仍能使用)
#include <stdio.h>FILE *popen(const char *cmdstring, const char *type); #返回值:若成功则返回文件指针,若出错则返回NULLint pclose(FILE *fp); #返回值:cmdstring的终止状态,若出错则返回-1
函数popen先执行fork,然后调用exec执行cmdstring,并且返回一个标准I/O文件指针。如果type是“r”,则文件指针连接到cmdstring的标准输出,如果type是“w”,则文件指针连接到cmdstring的标准输入。即执行popen,会先生成一个子进程来执行我们指定的程序,然后type是“r”,则子进程的标准输出会通过管道发送到原先进程,type是“w”,则原先进程通过管道发送给子进程的数据会成为子进程的标准输入。进程间通信之管道        进程间通信之管道
而pclose函数则关闭标准IO流,等待命令执行结束,然后返回shell的终止状态。

命名管道(FIFO)


FIFO也是管道的一种,与无命名管道相比,FIFO可以用于不相关进程的数据交换。与前面提到pipe是一种文件一样,FIFO也是一种文件类型。创建FIFO类似于创建文件,FIFO的路径名存在于文件系统中。(操作时,把FIFO看作文件即可,不同进程可以通过对FIFO读写,从而来实现进程通信
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode); #返回值:若成功则返回0,出错则返回-1
参数说明:
pathname:是FIFO的路径名称。mode:与open打开文件的模式一样,例如“r”、“w”等。注:一般的文件I/O函数(open、close、read、write等)都可以用于已经创建的FIFO。另外,一个给定的FIFO有多个写进程是很常见的,这样则有可能出现多个进程的所写的数据互相穿插。为避免数据穿插,则需要考虑原子操作。如上面所说,常量PIPE_BUF说明了可被原子地写到FIFO的最大数据量。FIFO的用途:1),FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。2),FIFO用于客户进程-服务器进程应用程序中,以在客户进程和服务进程之间传递数据。