Liunx进程间通信—管道
来源:互联网 发布:网络电视同步 编辑:程序博客网 时间:2024/05/18 01:17
管道
管道,顾名思义你可以把它想成一根数据线,连接了两个进程,使他们可以互相通信。更严谨来说,它是一个文件或者一块共享区,一个进程往里面写数据,另一个进程从里面拿数据,以此种方式完成进程间通信。
管道是UNIX系统IPC最古老的形式,所有的UNIX系统都提供此种通信机制(UNIX系统IPC是各种进程通信方式的统称)。
管道在进行通信时,基于字节流。管道是单向的、先进先出的。它将一个程序的输入和另一个程序的输出连接起来。数据被一个进程读出后,将被从管道中删除。
分位两种 :匿名管道:只能完成具有血缘关系间进程(或者说具有公共祖先的进程)的通信。如父子、兄弟进程。
命名管道:允许同一系统内任意两个进程间通信。
匿名管道(pipe)
1.创建函数pipe
匿名管道是通过调用函数pipe创建的:
- 1
- 2
- 1
- 2
返回值:成功返回0,失败返回-1。
该函数会经过参数fd返回两个文件描述符fd[0]和fd[1]。我们可以理解为fd[0]是为了从管道中读数据而打开的;fd[1]是为了向管道中写数据而打开的。所以管道在用户程序看了像是打开了一个文件。向这个文件读写数据其实就是在读写内核缓冲区。
2.原理
1.父进程调用pipe()开辟一个管道,使用open()得到两个文件描述符指向这个管道两端。
2.父进程调用fork()创建子进程,那么子进程也有两个相同的文件描述符指向这个管道的两端。
3.父进程关闭读端,子进程关闭写端,父进程可以往管道里写数据,子进程可以从管道里读数据。管道是通过环形队列实现的,数据从写端流入,从读端流出,这样就实现了进程间通信。
#include<stdio.h>#include<string.h>#include<unistd.h>int main(){ int fd[2]; int ret = pipe(fd);//创建管道 if(ret == -1) { perror("pipe"); return 0; } pid_t id = fork(); if(id == -1) { perror("fork"); return 2; } else if(id == 0)//child 从管道里面读取 { close(fd[1]); int i = 0; char read_buf[1024] = {0}; while(i<10) { size_t ret = read(fd[0],read_buf, sizeof(read_buf)); if(ret< 0) { perror("write"); return 1; } printf("child process read -->%s\n",read_buf); i++; } } else//father 往管道里面写入 { close(fd[0]); char *write_buf = "father process writes data to the pipe"; int i = 0; while(i < 10) { size_t ret = write(fd[1],write_buf,strlen(write_buf)+1); if(ret< 0) { perror("write"); return 1; } printf("father finished writing\n"); sleep(2); i++; } wait(); } return 0;}
匿名管道有以下四种情况需要注意:
1.当管道的写端已被关闭,这时有一个进程去读这个管道时,在管道内的剩余数据都被读取完之后就,read函数就会返回0,相 当于读到文件末尾。
2.当管道写端没有关闭,但在进程的读速度大于另一个进程的写速度时,在管道内的剩余数据都被读取后,read会阻塞,直到管 道内有新的数据。
3.当管道的读端已被关闭,这时有一个进程向管道写数据时,这时进程就会收到SIGPIPE信号,导致进程异常终止。
4.当管道读端没有关闭,但在进程的写速度大于另一个进程的读速度时,在管道被写满之后,write被阻塞,直到管道有空位置。
命名管道(FIFO)
FIFO被成为名名管道,命名管道不同于PIPE之处在于命名管道是一个设备文件,它真真在在的存在于硬盘之上,存在于文件系统中。而匿名管道存在于内存或者内核中,它对文件系统不可见。也正因为如此,命名管道可以完成任意两个进程间的通信。命名管道的创建有一个路径名path与之关联,以FIFO文件的形式存储在文件系统中,所以只要可以访问该路径,就能够通过FIFO互相通信。
注意:FIFO是按照先入先出的规则进行数据读写,第一个被写入的也将最先被读出。
1.相关函数
(1)创建管道
在shell命令行上可以通过mknod或者mkfifo创建命名管道文件
在程序内通过调用mknod和mkfifo函数也可以创建命名管道文件。
- 1
- 2
- 3
- 1
- 2
- 3
函数mknod的参数path即为创建的命名管道的全路径名(包括管道文件的文件名),如”/home/tmp/fifo”。第二个参数mod为创建命名管道的模式(一般为S_IFIFO|0666,0666表示管道IPC的默认权限,这里要注意umask对生成管道文件权限的影响)。dev为设备值,取决于文件创建的种类,它只在创建设备文件时才会用到。
返回值:成功返回0,失败返回-1。
- 1
- 2
- 3
- 1
- 2
- 3
此函的的两个参数与上相同,返回值也相同,成功返回0,失败返回-1。
mknod是比较老的函数,mkfifo函数更加的简单规范,所以建议尽量使用mkfifo。
(2)获取指向管道的文件描述符
当我们创建好管道之后并不能直接使用它,使用之前必须调用open函数将其打开,并获取文件描述符。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
参数pathname即为我们要打开的管道文件
参数flags为打开方式,一般有以只读方式打开O_RDONLY 或以只写方式打开O_WRONLY。
open函数的返回值为一个文件描述符,我们通过这个文件描述符对管道进行读写操作。
2.代码实例
server.c
#include<string.h>#include<stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define PATH "/home/long/task/pipe/fifoname"int main(){ int ret = mkfifo(PATH, S_IFIFO|0666); if(ret < 0) { perror("mkfifo"); return 1; } int fd = open(PATH,O_WRONLY); if(fd < 0) { perror("open"); return 2; } char buf[100] = {0}; while(1) { memset(buf,'\0',sizeof(buf)/sizeof(buf[0])); printf("server say:"); scanf("%s",buf); int ret = write(fd,buf,sizeof(buf)/sizeof(buf[0])+1); if(ret < 0) { perror("write"); break; } if(strncmp(buf,"quit",4) == 0) { break; } } close(fd); printf("server end...\n"); return 0;}
client.c
#include<stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define PATH "/home/long/task/pipe/fifoname"int main(){ int fd = open(PATH,O_RDONLY); if(fd < 0) { perror("open"); return 1; } char buf[1024] = {0}; while(1) { size_t ret = read(fd,buf,sizeof(buf)/sizeof(buf[0])); if(ret < 0) { perror("read"); break; } printf("client read --> %s\n",buf); if(strncmp(buf,"quit",4) == 0) { break; } } close(fd); printf("client end...\n"); return 0;}
- 文件系统中的路径名是全局的,各个进程都可以访问,因此可以用文件系统的路径名来标识一个IPC通道。
- 命名管道FIFO是一个特殊的设备文件,它在文件系统中真实存在。
- 命名管道与匿名管道的使用方法是一致的,只是命名管道在使用前要调用open函数打开。因为命名管道是存在于硬盘上的真实文件,而匿名管道是存在与内存中的特殊文件。
- 命名管道打开方式的不同也可能引起阻塞。如果同时以读写方式打开,则一定不会阻塞;若单单以读方式打开,则调用open函数的进程会一直阻塞到有写方式打开;同样,单单以写方式打开的进程会一直阻塞到有读方式打开。当然在阻塞时我们可以异常终止程序。
- Liunx进程间通信—管道
- 进程间通信—管道
- 进程间通信—管道
- 进程间通信—管道
- 进程间通信——管道通信
- 进程间通信——管道通信
- 进程间管道通信
- 进程间通信: 管道
- 进程间通信--管道
- 进程间通信----管道
- 进程间通信--管道
- 进程间通信--管道
- 进程间通信----管道
- 进程间通信----管道
- 进程间通信--管道
- 进程间通信--管道
- 进程间通信--管道
- 进程间通信-管道
- Python中的__new__和__init__的区别
- 函数调用行为
- strtok
- Android中动画简单使用
- super 与 superclass subclass
- Liunx进程间通信—管道
- 创建线程和配置属性、释放
- 注入AspectJ切面
- 【KF8V111控制器】系统时钟配置
- Word 2010双栏文档插入跨栏图片(或表格或公式)后的文字阅读顺序问题
- bug
- oralce的序列转换成mysql
- POJ 1434 Fill the Cisterns! 笔记
- vs字体和颜色