进程间通信之管道
2014-09-18 11:31
381 查看
进程间通信之管道
规则分析:
适用范围:
管道(pipe)是unix最早的进程间通信方式之一,主要用于有亲缘关系的进程之间通信。原型:
int pipe(int filedes[2]);(filedes以下简称fd)使用方法:
其中fd[0]为可用作读取端的文件描述符,fd[1]为可用作写入端的文件描述符,这样一对标识符,用于标识一条管道。步骤:
先用pipe创建一个管道然后fork()出一个子进程,则父,子进程都有fd[0],fd[1]。
若父进程用作写入端,则close掉父进程中的fd[0],也就是读标识符。同理close掉子进程的fd[1]。
这样父子进程中就建立了一条通讯管道,可以由父进程写数据,然后由子进程进行读取。
怎么写?怎么读?使用i/o中的write,read就可以了。
读端规则:rtSize = read(readfd,buff,buffSize);阻塞情况下
定义:readfd为读端文件描述符,读取到的内容存在buff中,buffsize为请求的数据大小,返回值rtSize为读到的数据大小。系统默认内核有一个PIPE_BUF(在include/linux/limits.h中定义),定义了管道缓冲区容量,也就是能放多大数据。
当前管道缓冲区中所有数据的大小定义为curSize;必有curSize <= PIPE_BUF。
写端本来就不存在,那么直接就返回rtSize = 0,不会阻塞。
写端本来存在后来全部都关闭了(writefd引用计数为0),那么读完管道中的curSize数据后,再去读就会返回rtSize = 0,不会阻塞。
如果有写端存在,请求读取管道数据时,如果管道有数据则执行下面的流程,如果没有数据,则会一直阻塞直到写端写入数据,或者写端关闭。
if( buffSize > PIPE_BUF )//请求数据大小大于缓冲区容量
{
rtSize = curSize;//返回全部的缓冲区数据
}
else
{
if( curSize >= buffSize )//缓冲区数据 大于等于 请求数据
{
rtSize = buffSize;//返回请求数据大小
}
else
rtSize = curSize;//返回当前缓冲区所有数据
}
写端规则 :rtSize = write(writefd,buff,buffSize);阻塞情况下
定义:writefd为写端文件描述符,要写入的内容放在buff中,buffSize为请求写入的数据大小,返回值rtSize为写入的实际大小。如果管道的读端不存在时,写入数据会产生SIGPIPE信号,默认终止当前写进程。
如果管道读端存在时,执行以下流程。
如果buffSize < PIPE_BUF
则能够保证数据的原子性(也就是连续性),否则不能。
第二条的流程:
if(buffSize + curSize > PIPE_BUF)//写入后总数据如果将大于管道缓冲则阻塞,知道有管道读端读走数据
else
rtSize = buffSize;//否则就全部写入
第三条原子性解释:
也就是如果PIPE_BUF = 10,而写端A要写入AAAAAAAAAA(10个),写端B要写入BBBBB。
那么经过管道的数据肯定是 AAAAAAAAAABBBBB 或者BBBBBAAAAAAAAAA。
但是如果要写入AAAAAAAAAAAAAAA(15个)
那么数据就可能是AAAAABBBBBAAAAAAAAAA 当然还有别的可能,就无法保证数据的原子性
实例:
这是《unix网络编程第2卷》中的一个例子,书上是原型是子进程充当服务器,父进程充当客户端之间创建两条管道A,B用于通信,首先由客户端读入用户的输入的文件名,然后有客户端写入管道A,服务器从管道A读出文件名,
打开文件读出文件中的内容,写入管道B,有客户端从管道B中读取出来并输出。
下面是改写了一下的例子:
客户端主线程循环写入管道多次文件名,并创建个线程去读管道B的数据,而服务端基本不变。
#include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <pthread.h> #include <errno.h> #include <string> #define MAXLINE 100 #define TIMES 10 #define SAFE_DELETE(x) if(x){delete (x);(x) = NULL;} typedef unsigned int uint32; typedef struct stARG { int fd; }ARG; void client(int readfd,int writefd) { printf("startClient\n"); uint32 times = TIMES; size_t len; char buff[MAXLINE]; memset(buff,0,MAXLINE); printf("输入文件名:"); fgets(buff,MAXLINE,stdin); len = strlen(buff); if(buff[len-1]=='\n') len--; while(times--) { write(writefd,buff,len); printf("enter times:%u\n",TIMES - times); sleep(1); } } void server(int readfd,int writefd) { printf("startServer\n"); FILE *file; ssize_t n; size_t len = 0; char *buff = new char(MAXLINE); while((n=read(readfd,buff,MAXLINE)) > 0) { if(n>0) { buff ='\0'; } if((file = fopen(buff,"r")) == NULL) { printf("文件%s读出失败\n",buff); snprintf(buff+n,sizeof(buff)-n,"can't open,%s/n",strerror(errno)); n = strlen(buff); write(writefd,buff,n); } else { printf("文件%s读出开始\n",buff); while((n = getline(&buff,&len,file))>0) { buff = '\0'; //printf("文件读出%s\n",buff); write(writefd,buff,n); } } } SAFE_DELETE(buff); } void *function(void * arg) { int n = 0; uint32 times = 0; ARG *info = (ARG*)arg; char buffRead[MAXLINE]; memset(buffRead,0,MAXLINE); while((n=read(info->fd,buffRead,MAXLINE)) > 0) { buffRead = '\0'; printf("管道客户端读出times:%u\n%s\n",++times,buffRead); } pthread_exit(NULL); printf("管道客户端读出线程退出"); } int main(int argc,char** argv) { int pipe1[2],pipe2[2]; pid_t childpid; pipe(pipe1); pipe(pipe2); if((childpid=fork())==0) { close(pipe1[1]); close(pipe2[0]); server(pipe1[0],pipe2[1]); printf("子进程退出"); exit(0); } else { close(pipe1[0]); close(pipe2[1]); /*创建一个单独的线程来读取*/ pthread_t tid = 0;; ARG arg; arg.fd = pipe2[0]; pthread_create(&tid,NULL,function,(void*)&arg); client(pipe2[0],pipe1[1]); waitpid(childpid,NULL,0); } return 0; }
相关文章推荐
- win32下进程间通信方式之管道、邮件槽、剪切板、共享内存、消息、套接字、RPC、DDE等
- Linux进程间通信之管道
- Linux进程间通信(一)——管道、信号量 .
- 进程间通信方式之管道
- 进程间通信之管道
- 管道通常用在两个线程间通信或进程间通信
- 进程间通信之管道篇
- Linux进程间通信--信号,管道,消息队列,信号量,共享内存,socket
- (转)在.NET中使用命名管道完成进程间通信
- 【Linux系统编程】进程间通信--有名管道
- 在.NET中使用命名管道完成进程间通信[转]
- Linux 上实现双向进程间通信管道
- 进程间通信之管道
- linux进程间通信(命名管道)
- 进程间通信——命名管道
- Linux进程间通信-管道
- 命名管道实现进程间通信--石头、剪刀、布游戏 分类: linux 2014-06-01 22:50 467人阅读 评论(0) 收藏
- 进程间通信 - 匿名管道实现
- 进程间通信编程(2) - 无名管道
- 进程间通信-管道