關於pipe管道的讀寫端關閉問題


 

 

 

 

知識概述

  通過pipe在內核中創建一個文件,然后可以實現兩個進程通信

管道是一種最基本的IPC機制,由 pipe 函數創建:

1 #include <unistd.h>
2 int pipe(int filedes[2]);

 

調用 pipe 函數時在內核中開辟一塊緩沖區(稱為管道)用於通信,它有一個讀端一個寫端,然后通過 filedes 參數傳出給用戶程序兩個文件描述符,

filedes[0] 指向管道的讀端, filedes[1] 指向管道的寫端(很好記,就像0是標准輸入1是標准輸出一樣)。所以管道在用戶程序看起來就像一個打開的文件,

通過 read(filedes[0]); 或者 write(filedes[1]); 向這個文件讀寫數據其實是在讀寫內核緩沖區。 pipe 函數調用成功返回0,調用失敗返回-1。

 

開辟了管道之后如何實現兩個進程間的通信呢?比如可以按下面的步驟通信。

1. 父進程調用 pipe 開辟管道,得到兩個文件描述符指向管道的兩端。
2. 父進程調用 fork 創建子進程,那么子進程也有兩個文件描述符指向同一管道。

3. 父進程關閉管道讀端,子進程關閉管道寫端。父進程可以往管道里寫,子進程可以從管道里讀,
管道是用環形隊列實現的,數據從寫端流入從讀端流出,這樣就實現了進程間通信。

 

#include <stdlib.h>
#include <unistd.h>
#define MAXLINE 80
int main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    char line[MAXLINE];
    if (pipe(fd) < 0) {
    perror("pipe");
    exit(1);
    }
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }
    if (pid > 0) { /* parent */
        close(fd[0]);
        write(fd[1], "hello world\n", 12);
        wait(NULL);
    } 
    else { /* child */
        close(fd[1]);
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }
    return 0;
}    

 

問題

父進程只用到寫端,因而把讀端關閉,子進程只用到讀端,因而把寫端關閉,然后互相通信,不使用的讀端或寫端必須關閉,請讀者想一想如果不關閉會有什么問題。

思考

 

1. 如果所有指向管道寫端的文件描述符都關閉了(管道寫端的引用計數等於0),
而仍然有進程從管道的讀端讀數據,那么管道中剩余的數據都被讀取后,
再次 read 會返回0,就像讀到文件末尾一樣。

 

2. 如果有指向管道寫端的文件描述符沒關閉(管道寫端的引用計數大於0),
而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,
那么管道中剩余的數據都被讀取后,再次 read 會阻塞,
直到管道中有數據可讀了才讀取數據並返回。

 

 考慮到如下代碼

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(void)
{
    int n;
    char buff[128];
    pid_t pid;
    int fd[2];

    if(pipe(fd)<0)    {
        perror("pipe");
        exit(0);
    }
    
    if((pid=fork())<0)
    {
        perror("fork");
        exit(0);
    }
    
    if(pid>0)
    {
        /* parent */    
        printf("+++++++++++++\n");
        close(fd[0]);        
        write(fd[1],"hello world",11);
        //sleep(5);
        //write(fd[1],"I am a Student",14);
        printf("+++++++++++++\n");
    }
    else
    {
        printf("--------------\n");
        //close(fd[1]);
        memset(buff,0,128);
        n = read(fd[0],buff,20);
        printf("buff=%s\n",buff);
        memset(buff,0,128);
        printf("read twice\n");
        n = read(fd[0],buff,20);
        printf("buff=%s\n",buff);
        printf("--------------\n");
    }
    return 0;
}

父進程關閉了讀端口,通過寫端口向pipe中寫入了hello world。然后父進程結束。關閉相關文件(讀寫)描述符

  子進程在關閉寫端口的時候,父進程結束時候,寫文件描述符引用計數為0。所以子進程再次讀取后返回0。子進程結束退出。

  子進程在不關閉寫端口的時候,父進程結束時候,寫文件描述符引用計數為1(自己的沒關閉)。所以子進程再次讀取時候陷入阻塞狀態。

 

  因為父進程是在SHELL下執行的。所以當父進程結束時候,Shell進程認為命令執行結束了,於是打印Shell提示符,而子進程等待讀取輸入。

父進程已經結束,不會給他輸入數據,而子進程本身只是為了讀取而不是向管道寫數據。所以子進程一直在后台運行,通過ps命令可以查看到子進程信息。

  所以,子進程只用到讀端,因而把寫端關閉。防止造成子進程做無用功。。。

 


免責聲明!

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



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