linux c编程:管道

 

 

2在前面介绍过,进程之间交换信息的唯一途径就是传送打开的文件。可以经由fork或者exec来传送。这一章将介绍新的进程共享方式
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信,如下图所示

 


 
不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同,而 pipe就是提供这份公共资源的形式的一种。
管道是由调用pipe函数来创建
#include <unistd.h>
int pipe (int fd[2]);
fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
 
管道是如何实现进程间的通信
(1)父进程创建管道,得到两个⽂件描述符指向管道的两端
 
(2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。
 
(3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管读写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。  
 
 
实现如下图所示

 


 
实现代码
int chapter15_5()
{
    int n;
    int fd[2];
    pid_t pid;
    char line[MAXLINE];
    if (pipe(fd) < 0)
    {
        printf("pipe error");
    }
    pid=fork();
    if (pid == 0)
    {
        close(fd[1]);
        n=read(fd[0],line,MAXLINE);
        write(STDOUT_FILENO,line,n);
    }
    else
    {
        close(fd[0]);
        write(fd[1],"hello world\n",12);
    }
}
在上面的代码中,是父进程关闭读通道,子进程关闭写通道。父进程向fd[1]中写入数据,子进程从fd[0]中读出数据并显示在终端上。同样的我们也可以子进程关闭读通道,父进程关闭写通道,由子进程来写入数据,父进程读取数据
 
管道读取数据的4种情况:
1 读端不读,写端一直写

写端不写,但是读端一直读

3 读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。 

如果一个管道读端一直在读数据,而管道写端的引⽤计数⼤于0决定管道是否会堵塞,引用计数大于0,只读不写会导致管道堵塞。

4 读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。

来看下这种异常的代码:

int chapter_15_5_1()

{

    int fd[2];

    int ret = pipe(fd);

    if (ret == -1)

    {

        printf("pipe error\n");

        return 1;

    }

    pid_t id = fork();

    if (id == 0)

    {

        int i = 0;

        close(fd[0]);

        char child[20] = "I am a student";

        while (i<10)

        {

            write(fd[1], child, strlen(child) + 1);

            sleep(2);

            i++;

        }

    }

    else if (id>0)

    {

        close(fd[1]);

        char msg[100];

        int status = 0;

        int j = 0;

        while (j<5)

        {

            memset(msg,'\0',sizeof(msg));

            ssize_t s = read(fd[0], msg, sizeof(msg));

            if (s>0)

            {

                msg[s-1] = '\0';

            }

            printf("%s %d\n",msg,j);

            j++;

        }

        close(fd[0]);

        pid_t ret = waitpid(id, &status, 0);

        printf("exit single(%d),exit(%d)\n",status & 0xff, (status >> 8) & 0xff);

 

    }

    else

    {

        printf("fork error\n");

        return 2;

    }

    return  0;

 

}

在这段代码中,子进程关闭读端,每隔2秒向管道写数据,父进程关闭写端,当读了5次数据后就关闭读端。此时再往管道写数据则会报错。

如果一个管道的写端一直在写,而读端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只写不读再次调用write会导致管道堵塞;

如果一个管道的读端一直在读,而写端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只读不写再次调用read会导致管道堵塞;

而当他们的引用计数等于0时,只写不读会导致写端的进程收到一个SIGPIPE信号,导致进程终止,只写不读会导致read返回0,就像读到⽂件末尾⼀样。

 

posted @ 2018-10-14 10:39  red_leaf_412  阅读(5325)  评论(0编辑  收藏  举报