鍍金池/ 教程/ Linux/ 管道
命名管道
消息隊(duì)列
進(jìn)程創(chuàng)建與終止
信號量
進(jìn)程組,會話和作業(yè)控制
共享內(nèi)存
進(jìn)程間通信簡介
子進(jìn)程監(jiān)視
其他進(jìn)程
覆蓋進(jìn)程映像
進(jìn)程信息
進(jìn)程映像
內(nèi)存映射
相關(guān)系統(tǒng)調(diào)用(System V)
進(jìn)程資源
System V & Posix
信號
進(jìn)程間通信教程
管道

管道

管道是兩個(gè)或更多相關(guān)或相關(guān)進(jìn)程之間的通信媒介。 它可以在一個(gè)進(jìn)程內(nèi),也可以在子進(jìn)程和父進(jìn)程之間進(jìn)行通信。 通信也可以是多層次的,如父進(jìn)程,子進(jìn)程和子進(jìn)程之間的溝通等。通信是通過一個(gè)過程寫入管道和從管道讀取來實(shí)現(xiàn)的。 要實(shí)現(xiàn)管道系統(tǒng)調(diào)用,請創(chuàng)建兩個(gè)文件,一個(gè)寫入文件,另一個(gè)從文件讀取。

管道機(jī)制可以用一個(gè)實(shí)時(shí)的場景來看,比如用管子把水灌進(jìn)一個(gè)容器里。 填充過程可以理解為是寫入管道,讀取過程只不過是從管道中取出。 這意味著一個(gè)輸出(水)是為另一個(gè)(桶)輸入的。參考下圖 -

#include<unistd.h>

int pipe(int pipedes[2]);

這個(gè)系統(tǒng)調(diào)用將創(chuàng)建一個(gè)單向通信的管道,即它創(chuàng)建兩個(gè)描述符,第一個(gè)連接從管道讀取,另一個(gè)連接寫入管道。

描述符pipedes [0]用于讀取,pipedes [1]用于寫入。 無論寫入pipedes [1]什么都可以從pipedes [0]中讀取。

這個(gè)調(diào)用在成功時(shí)將返回0,在失敗的情況下為-1。 要知道失敗的原因,請檢查errno變量或perror()函數(shù)。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

即使文件的基本操作是讀寫,在完成所需的操作之后,在執(zhí)行操作和關(guān)閉文件之前,必須打開文件。 通常,默認(rèn)情況下,為每個(gè)進(jìn)程打開3個(gè)描述符,分別用于輸入(標(biāo)準(zhǔn)輸入 - 標(biāo)準(zhǔn)輸入),輸出(標(biāo)準(zhǔn)輸出 - 標(biāo)準(zhǔn)輸出)和錯(cuò)誤(標(biāo)準(zhǔn)錯(cuò)誤 - 標(biāo)準(zhǔn)錯(cuò)誤),分別具有文件描述符0,12。

這個(gè)系統(tǒng)調(diào)用將返回一個(gè)文件描述符,用于讀/寫/查找(lseek)的進(jìn)一步文件操作。 通常文件描述符從3開始,隨著打開的文件數(shù)量增加一個(gè)數(shù)字。

傳遞給開放系統(tǒng)調(diào)用的參數(shù)是路徑名(相對路徑或絕對路徑),標(biāo)志提到了打開文件的目的(比如打開O_RDONLY進(jìn)行讀取,O_RDONR,讀寫O_RDWR,以追加到現(xiàn)有的文件 O_APPEND,創(chuàng)建文件,如果不存在與O_CREAT等)和所需的模式提供讀/寫/執(zhí)行權(quán)限的用戶或所有者/組/其他人。 模式可以用符號來描述。

讀使用4表示,寫使用2表示和執(zhí)行使用1表示。

例如:八進(jìn)制值(以0開頭),0764表示擁有者擁有讀取,寫入和執(zhí)行權(quán)限,組擁有讀寫權(quán)限,其他擁有讀權(quán)限。 這也可以表示為S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH,表示或操作0700 | 0040 | 00040764。

這個(gè)系統(tǒng)調(diào)用在成功時(shí)返回新的文件描述符id-1以防出錯(cuò)。 錯(cuò)誤的原因可以用errno變量或perror()函數(shù)來標(biāo)識。

#include<unistd.h>

int close(int fd)

上面的系統(tǒng)調(diào)用關(guān)閉已經(jīng)打開的文件描述符。 這意味著文件不再被使用,相關(guān)的資源可以被任何其他進(jìn)程重用。 這個(gè)系統(tǒng)調(diào)用在成功時(shí)返回零,在出錯(cuò)時(shí)返回-1。 錯(cuò)誤的原因可以用errno變量或perror()函數(shù)來標(biāo)識。

#include<unistd.h>

ssize_t read(int fd, void *buf, size_t count)

上面的系統(tǒng)調(diào)用是從指定的文件中讀取文件描述符fd的參數(shù),具有分配內(nèi)存(靜態(tài)或動態(tài))的正確緩沖區(qū)以及緩沖區(qū)的大小。

文件描述符ID用于標(biāo)識在調(diào)用open()pipe()系統(tǒng)調(diào)用之后返回的相應(yīng)文件。 從文件中讀取文件之前,需要打開該文件。 它會在調(diào)用pipe()系統(tǒng)調(diào)用的情況下自動打開。

這個(gè)調(diào)用將返回成功時(shí)讀取的字節(jié)數(shù)(或在遇到文件結(jié)尾的情況下為零),在失敗的情況下返回-1。 返回字節(jié)可以小于請求的字節(jié)數(shù),以防萬一沒有數(shù)據(jù)可用或文件關(guān)閉。

要知道失敗的原因,請檢查errno變量或perror()函數(shù)。

#include<unistd.h>

ssize_t write(int fd, void *buf, size_t count)

上面的系統(tǒng)調(diào)用是使用文件描述符fd的參數(shù),具有已分配內(nèi)存(靜態(tài)或動態(tài))和緩沖區(qū)大小的適當(dāng)緩沖區(qū)來寫入指定文件。

文件描述符ID用于標(biāo)識在調(diào)用open()pipe()系統(tǒng)調(diào)用之后返回的相應(yīng)文件。

在寫入文件之前需要打開該文件。 它會在調(diào)用pipe()系統(tǒng)調(diào)用的情況下自動打開。

這個(gè)調(diào)用將返回成功寫入的字節(jié)數(shù)(或者在沒有寫入的情況下為零),如果失敗則返回-1。 正確的錯(cuò)誤碼設(shè)置在失敗的情況下。

要知道失敗的原因,請檢查errno變量或perror()函數(shù)。

示例程序

以下是一些示例程序。

示例程序1 - 使用管道編寫和讀取兩條消息的程序。

算法

第1步 - 創(chuàng)建一個(gè)管道。
第2步 - 發(fā)送消息到管道。
第3步 - 從管道中檢索消息并將其寫入標(biāo)準(zhǔn)輸出。
第4步 - 發(fā)送另一條消息到管道。
第5步 - 從管道中檢索消息并將其寫入標(biāo)準(zhǔn)輸出。
注 - 檢索消息也可以在發(fā)送所有消息之后完成。

源代碼:simplepipe.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds[2];
   int returnstatus;
   char writemessages[2][20]={"Hi", "Hello"};
   char readmessage[20];
   returnstatus = pipe(pipefds);

   if (returnstatus == -1) {
      printf("Unable to create pipe\n");
      return 1;
   }

   printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
   write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
   read(pipefds[0], readmessage, sizeof(readmessage));
   printf("Reading from pipe – Message 1 is %s\n", readmessage);
   printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
   write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
   read(pipefds[0], readmessage, sizeof(readmessage));
   printf("Reading from pipe – Message 2 is %s\n", readmessage);
   return 0;
}

注 - 理想情況下,每個(gè)系統(tǒng)調(diào)用都需要檢查返回狀態(tài)。 為了簡化這個(gè)過程,所有的調(diào)用都沒有進(jìn)行檢查。

執(zhí)行步驟

首先編譯 -

$ gcc -o simplepipe simplepipe.c

執(zhí)行輸出結(jié)果如下 -

Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell

示例程序2 - 使用父進(jìn)程和子進(jìn)程通過管道寫入和讀取兩條消息的程序。

算法
第1步 - 創(chuàng)建一個(gè)管道。
第2步 - 創(chuàng)建一個(gè)子進(jìn)程。
第3步 - 父進(jìn)程寫入管道。
第4步 - 子進(jìn)程從管道中檢索消息并將其寫入標(biāo)準(zhǔn)輸出。
第5步 - 再次重復(fù)步驟3和步驟4。

源代碼:pipewithprocesses.c -

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds[2];
   int returnstatus;
   int pid;
   char writemessages[2][20]={"Hi", "Hello"};
   char readmessage[20];
   returnstatus = pipe(pipefds);
   if (returnstatus == -1) {
      printf("Unable to create pipe\n");
      return 1;
   }
   pid = fork();

   // Child process
   if (pid == 0) {
      read(pipefds[0], readmessage, sizeof(readmessage));
      printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
      read(pipefds[0], readmessage, sizeof(readmessage));
      printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
   } else { //Parent process
      printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
      write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
      printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
      write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
   }
   return 0;
}

執(zhí)行步驟

首先編譯 -

$ gcc pipewithprocesses.c –o pipewithprocesses

執(zhí)行輸出結(jié)果如下 -

Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello

使用管道雙向通信

管道通信僅被視為單向通信,即,父進(jìn)程寫入和子進(jìn)程讀取,或者相反,但不是兩者。 但是,如果父子進(jìn)程雙方都需要同時(shí)寫入和讀取管道,那么解決方案就是使用管道的雙向通信。 建立雙向通信需要兩根管道。

以下是實(shí)現(xiàn)雙向通信的步驟 -

第1步 - 創(chuàng)建兩個(gè)管道。 第一個(gè)是父進(jìn)程寫入和子進(jìn)程讀取,如:pipe1。 第二個(gè)是為子進(jìn)程寫入和父進(jìn)程讀取,如:pipe2。
第2步 - 創(chuàng)建一個(gè)子進(jìn)程。
第3步 - 關(guān)閉不需要的結(jié)束,因?yàn)槊總€(gè)通信只需要一端。
第4步 - 在父進(jìn)程中關(guān)閉不需要的結(jié)束,讀取pipe1的結(jié)尾并寫入pipe2的結(jié)尾。
第5步 - 關(guān)閉子進(jìn)程中的不需要的結(jié)束,寫入pipe1的結(jié)尾和讀取pipe2的結(jié)束。
第6步 - 根據(jù)需要進(jìn)行通訊。

示例程序
示例程序1 - 使用管道實(shí)現(xiàn)雙向通信。

算法
第1步 - 創(chuàng)建pipe1為父進(jìn)程寫和子進(jìn)程讀取。
第2步 - 為要寫入的子進(jìn)程和要讀取的父進(jìn)程創(chuàng)建pipe2。
第3步 - 關(guān)閉從父進(jìn)程和子進(jìn)程一側(cè)管道不需要的一端。
第4步 - 父進(jìn)程寫一個(gè)消息和子進(jìn)程讀取并顯示在屏幕上。
第5步 - 子進(jìn)程寫入消息和父進(jìn)程讀取并顯示在屏幕上。

源代碼:twowayspipe.c -

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds1[2], pipefds2[2];
   int returnstatus1, returnstatus2;
   int pid;
   char pipe1writemessage[20] = "Hi";
   char pipe2writemessage[20] = "Hello";
   char readmessage[20];
   returnstatus1 = pipe(pipefds1);

   if (returnstatus1 == -1) {
      printf("Unable to create pipe 1 \n");
      return 1;
   }
   returnstatus2 = pipe(pipefds2);

   if (returnstatus2 == -1) {
      printf("Unable to create pipe 2 \n");
      return 1;
   }
   pid = fork();

   if (pid != 0) // Parent process {
      close(pipefds1[0]); // Close the unwanted pipe1 read side
      close(pipefds2[1]); // Close the unwanted pipe2 write side
      printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
      write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
      read(pipefds2[0], readmessage, sizeof(readmessage));
      printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
   } else { //child process
      close(pipefds1[1]); // Close the unwanted pipe1 write side
      close(pipefds2[0]); // Close the unwanted pipe2 read side
      read(pipefds1[0], readmessage, sizeof(readmessage));
      printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
      printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
      write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
   }
   return 0;
}

執(zhí)行步驟

首先編譯 -

$ gcc twowayspipe.c –o twowayspipe

執(zhí)行輸出結(jié)果如下 -

In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello