您的位置:首页 > 其它

进程间通信之管道

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: