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,就像讀到⽂件末尾⼀樣。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM