亚博app|Linux进程通信(一)——pipe管线

发布时间 : 2021-02-21 16:21:42 浏览: 125次 来源:网络整理 作者:佚名

本章内容 哪些是进程通信

进程通讯就是两个进程之间进行数据交换,在Linux中有好几种可以进行进程通讯的形式,在这篇文章中我们主要介绍最基本的进程通讯方法——pipe管路。

进程通讯的途径

进程之间交换信息的惟一途径就是传送打开的文件。

管道(pipe)

管道是一种最古老也是最基本的系统IPC方式,所有的Linux系统都提供此种通讯机制。但是管线有以下两个局限性:

pipe的实现机制

管道是由内核管理的一个缓冲区,它的一端联接一个进程的输出,另一端联接一个进程的输入。管道的缓冲区不需要很大,它被设计为环型的数据结构,当两个进程都中止后,管道的生命周期也会被结束。

管道的创建

管道是通过调用pipe函数创建的。


#include 
int     pipe(int fd[2]);

它由输出型参数fd返回两个文件描述符,fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出是fd[0]的输入,当管线创建成功后pipe函数返回0,如果创建失败则返回-1,fd[0]和fd[1]之间的关系如下图:

(pipe1)

如何通过pipe进行通讯

上面我们在单个进程中构建了管线,但是实际上,单个进程中的管线是没有哪些用的,通常,进程会先调用pipe函数形成管路,接着调用fork()函数,fork函数会将父进程的相关数据结构承继到子进程中,这样就使子进程中的文件描述符表中的fd[0]和fd[1]指向父进程所指向的管线文件,这样才能实现两个进程之间的通讯了。上面的过程如下图:

这里写图片描述

利用pipe通讯的相关规则

对于一个从子进程到父进程的管线(子进程写,父进程读),父进程关掉fd[1],子进程关掉fd[0],当管线的一段被关闭后(在里面的基础上关掉管线的一端)下列两条规则起作用:

当读一个写段早已被关掉的管线时,在所有的数据都被读取后,read返回0(read返回0表示早已读到文件结束符);

下面我们进行验证:

#include
#include 
#include 
#include 
#include 
#include 
int main()
{
    //create pipe
    int fd[2]={0,0};
    if(pipe(fd)!=0){
        //create false
        perror("pipe");
        exit(1);
    }
    // pipe create success
    pid_t id=fork();
    if(id==0){
        //child -->write fd[1]
        printf("Child\n");
        sleep(2);
        const char* msg="Hello,leap\n";
        close(fd[0]);
        int count=3;
        while(count--){
            ssize_t size=write(fd[1],msg,strlen(msg));
            printf("size:%d\n",size);
            //if(count--){
            //  sleep(1);
            //}
            sleep(1);
            printf("child is writing...\n");
        }
        close(fd[1]);
        exit(0);
    }
    else{
        //father -->read fd[0]
        printf("Father\n");
        sleep(2);
        close(fd[1]);
        char buf[1024];
        int count=3;
        while(1){
            ssize_t Len=read(fd[0],buf,1024);
            //printf("Len::%d\n",Len);
            printf("Father is reading...\n");
            if(Len>0){
                //read success
                buf[Len]='\0';
                printf("child say:%s",buf);
            }
            else if(Len==0){
                //read end of file
                printf("Read the end of pipe\n");
                break;
            }
            else{
                perror("read");
                exit(1);
            }
        }
        close(fd[0]);
        int status=0;
        pid_t _pid=waitpid(id,&status,0);
        if(_pid==id){
            printf("Wait success for child\n");
            printf("Exit code:%d,Exit signal:%d\n",(status>>8)&0xff,status&0xff);
        }
        else{
            perror("wait");
        }
        exit(0);
    }
    return 0;
}

程序让子进程写三次字符串之后关掉子进程fd[1],即关掉管线的写端,不关掉父进程的fd[0],即管线的读端。

这里写图片描述

如果写一个读端早已被关掉的管线,则会形成相关讯号对写段的进程进行中止,如果忽视该讯号或捕捉该讯号并从处理程序返回,则write会返回-1,errno会设置为EPIPE;

下面我们进行验证:

#include
#include 
#include 
#include 
#include 
#include 
int main()
{
    //create pipe
    int fd[2]={0,0};
    if(pipe(fd)!=0){
        //create false
        perror("pipe");
        exit(1);
    }
    // pipe create success
    pid_t id=fork();
    if(id==0){
        //child -->write fd[1]
        printf("Child\n");
        sleep(2);
        const char* msg="Hello,leap\n";
        close(fd[0]);
        int count=5;
        while(1){
            ssize_t size=write(fd[1],msg,strlen(msg));
            printf("size:%d\n",size);
            if(count--){
                sleep(1);
            }
            printf("child is writing...\n");
        }
        close(fd[1]);
    }
    else{
        //father -->read fd[0]
        printf("Father\n");
        sleep(2);
        close(fd[1]);
        char buf[1024];
        int count=3;
        while(count--){
            ssize_t Len=read(fd[0],buf,1024);
            //printf("Len::%d\n",Len);
            printf("Father is reading...\n");
            if(Len>0){
                //read success
                buf[Len]='\0';
                printf("child say:%s",buf);
            }
            else if(Len==0){
                //read end of file
                printf("Read the end of pipe\n");
            }
            else{
                perror("read");
                exit(1);
            }
        }
        close(fd[0]);
        int status=0;
        pid_t _pid=waitpid(id,&status,0);
        if(_pid==id){
            printf("Wait success for child\n");
            printf("Exit code:%d,Exit signal:%d\n",(status>>8)&0xff,status&0xff);
        }
        else{
            perror("wait");
        }
        exit(0);
    }
    return 0;
}

代码的意图是这样:我们使write端(子进程)一直写字符串msg,而read端(父进程)先读三次之后在关掉掉父进程的fd[0],这样就产生了子进程仍然写,而父进程没有在读的情况。结果如下:

这里写图片描述

我们发觉父进程关掉掉fd[0]后子进程被异常中止了,我们从子进程的退出码和退出讯号码发觉它是被13号讯号(SIGPIPE)所中止的,所以写一个读端关掉的管线这对PIPE来说并不创立进程通信 pipe,操作系统会在读端关掉后向写端的进程发送SIGPIPE让进程被中止。

如果管线的读端和写端都没有关掉,但是管线的写端没有再向管线写数据了。这时假若管线中没有数据了,那么在此read进程会形成阻塞,直到管线中有数据了才读取数据并返回。如果有指向管线读端的文件描述符没有关掉,而持有管线读端的没有从管线中读数据,这时有进程向管线中写数据,如果管线被写满再向管线写数据是,再次write会导致进程阻塞,直到管线中有空间了才能继续向管线中写数据并返回。 pipe管线容量

我们可以通过* man 7 pipe*;来查询管线的容量pipe_capacity

## Linux的管线实现机制

从本质上说,管道也是一种文件,但它又和通常的文件有所不同,管道可以克服使用文件进行通讯的两个问题,具体表现为:

管道是一个固定大小的缓冲县,在Linux中,该缓冲区的大小为一页,即4kb,使它的大小不会象普通文件那样不加检验的下降。在Linux中,内核使用struct pipe_inode_info结构体来描述一个管线进程通信 pipe,这个结构体定义在pipe_fs_i.h中。

struct pipe_inode_info结构体

struct pipe_inode_info {
//管道等待队列,当pipe为空/满时指向等待的读者和写者
    wait_queue_head_t wait;            
//pipe中非空缓冲区的数量和当前pipe的入口
    unsigned int nrbufs, curbuf;
//临时释放的页也叫高速缓存区页框指针
    struct page *tmp_page;
//读进程的标志或ID号
    unsigned int readers;
//写进程的标志或ID号
    unsigned int writers;
//在等待队列中睡眠的写进程的个数
    unsigned int waiting_writers;
//reader的总数
    unsigned int r_counter;
//writer的总数
    unsigned int w_counter;
//用于通过信号进行异步I/O通知
    struct fasync_struct *fasync_readers;
    struct fasync_struct *fasync_writers;
//pipe对应的inode
    struct inode *inode;
//pipe的环形缓冲区
    struct pipe_buffer bufs[PIPE_BUFFERS];
};

缓冲区的个数

#define PIPE_BUFFERS (16)

管理缓冲区的结构

struct pipe_buffer {
//包含当前pipe_buffer数据的页
    struct page *page;   
//页中所包含的数据的偏移量,长度
    unsigned int offset, len;
//与buffer相关的操作
    const struct pipe_buf_operations *ops;
//pipe_buffer标志
    unsigned int flags;
    unsigned long private;
};

亚博app|老王


上一篇:亚博app|1.5__料筒的设计 下一篇: 亚博app|王健林手下万达老将朱战备因贪腐被北京民警带走,已被除名
Copyright © 2012-2019 老王机械公司/老王有限公司 版权所有苏ICP备xxxxxxxx号
亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app亚博app