1. 程式人生 > >Linux下的程序通訊方式: 管道通訊詳解

Linux下的程序通訊方式: 管道通訊詳解

管道是單向的、先進先出的、無結構的位元組流,它把一個程序的輸出和另一個程序的輸入連線在一起。
寫程序在管道的尾端寫入資料,讀程序在管道的首端讀出資料。資料讀出後將從管道中移走,其它讀程序都不能再讀到這些資料。

管道提供了簡單的流控制機制。程序試圖讀一個空管道時,在資料寫入管道前,程序將一直阻塞。同樣,管道已經滿時,程序再試圖寫管道,在其它程序從管道中讀走資料之前,寫程序將一直阻塞。
匿名管道具有的特點:

只能用於具有親緣關係的程序之間的通訊(也就是父子程序或者兄弟程序之間)。

一種半雙工的通訊模式,具有固定的讀端和寫端。

LINUX把管道看作是一種檔案,採用檔案管理的方法對管道進行管理,對於它的讀寫也可以使用普通的read()和write()等函式。但是它不是普通的檔案,並不屬於其他任何檔案系統,只存在於核心的記憶體空間中。
1.管道建立與關閉說明

管道是基於檔案描述符的通訊方式,當一個管道建立時,它會建立兩個檔案描述符fds[0]和fds[1]:

fds[0]固定用於讀管道;

fd[1]固定用於寫管道;

管道關閉時只需將這兩個檔案描述符關閉即可,可使用普通的close()函式逐個關閉各個檔案描述符。

2.管道建立函式
建立管道可以通過呼叫pipe()來實現:

3.管道讀寫說明
使用管道進行父子程序間通訊的步驟:

建立管道:父程序呼叫pipe()函式建立一個管道

此時,管道的讀端和寫端都在一個程序之中,這種管道是沒有多大用的。

父程序通過fork()函式建立一子程序

子程序會繼承父程序所建立的管道,這時,父子程序中管道的檔案描述符對應關係如圖8.4所示。

確定管道的傳輸方向:在父、子程序中根據需要的傳輸方向關閉無關的讀端或寫端檔案描述符

通訊:在寫程序中呼叫write()函式,在讀程序中呼叫read()函式

關閉管道:呼叫close()關閉管道相關的檔案描述符。

圖1:

圖2:

圖3:

關閉了父程序的寫端fd[1]和子程序的讀端fd[0],這樣就可以建立一條“子程序寫入父程序讀取”的通道。

也可以關閉父程序的讀端fd[0]和子程序的寫端fd[1],這樣就可以建立一條“父程序寫入子程序讀取”的通道。

父程序還可以建立多個子程序,各個子程序都繼承了相應的fd[0]和fd[1],這時,只需要關閉相應埠就可以建立起各子程序(兄弟程序)之間的通道。

4.管道使用例項
在本例中,首先建立管道,之後父程序使用fork()函式建立子程序,然後通過關閉父程序的讀描述符和子程序的寫描述符,建立起它們之間的管道通訊。

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define MAX_DATA_LEN   256
#define DELAY_TIME 1
 
int main()
{
    pid_t pid;
    int pipe_fd[2];
    char buf[MAX_DATA_LEN];
    const char data[] = "Pipe Test Program";
    int real_read, real_write;
    memset((void*)buf, 0, sizeof(buf));
    /* 建立管道 */
    if (pipe(pipe_fd) < 0)
    {
        printf("pipe create error\n");
        exit(1);
    }
    if ((pid = fork()) == 0)
    {
        /* 子程序關閉寫描述符,並通過使子程序暫停3s等待父程序已關閉相應的讀描述符 */
        close(pipe_fd[1]);
        sleep(DELAY_TIME * 3);        
        /* 子程序讀取管道內容 */
        if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
	{
		printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
	}
        	
       /* 關閉子程序讀描述符 */
        close(pipe_fd[0]);
        exit(0);
     }
    else if (pid > 0)
    {
        /* 父程序關閉讀描述符,並通過使父程序暫停1s等待子程序已關閉相應的寫描述符 */
      close(pipe_fd[0]);
      sleep(DELAY_TIME);
 
      if((real_write = write(pipe_fd[1], data, strlen(data))) !=  -1)
	{
		printf("Parent wrote %d bytes : '%s'\n", real_write, data);
	}
		
      close(pipe_fd[1]);     /*關閉父程序寫描述符*/
      waitpid(pid, NULL, 0); /*收集子程序退出資訊*/
      exit(0);
     }   
    return 0;
 }

執行結果:

5.管道讀寫注意點

只有在管道的讀端存在時,向管道寫入資料才有意義。否則,向管道寫入資料的程序將收到核心傳來的SIGPIPE訊號(通常為Broken pipe錯誤)。

向管道寫入資料時,Linux將不保證寫入的原子性,管道緩衝區一有空閒區域,寫程序就會試圖向管道寫入資料。如果讀程序不讀取管道緩衝區中的資料,那麼寫操作將會一直阻塞。

父子程序在執行時,它們的先後次序並不能保證,因此,在這裡為了保證父子程序已經關閉了相應的檔案描述符,可在兩個程序中呼叫sleep()函式,這不是很好的解決方法,應儘可能使用程序之間的同步與互斥機制解決父子程序之間的同步問題。

匿名管道的侷限性:

只支援單向資料流

只能用於具有親緣關係的程序之間通訊,沒有名字

緩衝區有限,管道只存在於主存中,大小為一個頁面

所傳送的是無格式位元組流

出處:https://blog.csdn.net/rl529014/article/details/51464363 

 

https://blog.csdn.net/JackLiu16/article/details/78934122