管道
管道是兩個或更多相關或相關進程之間的通信媒介。 它可以在一個進程內,也可以在子進程和父進程之間進行通信。 通信也可以是多層次的,如父進程,子進程和子進程之間的溝通等。通信是通過一個過程寫入管道和從管道讀取來實現的。 要實現管道系統調用,請創建兩個文件,一個寫入文件,另一個從文件讀取。
管道機制可以用一個實時的場景來看,比如用管子把水灌進一個容器裏。 填充過程可以理解爲是寫入管道,讀取過程只不過是從管道中取出。 這意味着一個輸出(水)是爲另一個(桶)輸入的。參考下圖 -
#include<unistd.h>
int pipe(int pipedes[2]);
這個系統調用將創建一個單向通信的管道,即它創建兩個描述符,第一個連接從管道讀取,另一個連接寫入管道。
描述符pipedes [0]
用於讀取,pipedes [1]
用於寫入。 無論寫入pipedes [1]
什麼都可以從pipedes [0]
中讀取。
這個調用在成功時將返回0
,在失敗的情況下爲-1
。 要知道失敗的原因,請檢查errno
變量或perror()
函數。
#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);
即使文件的基本操作是讀寫,在完成所需的操作之後,在執行操作和關閉文件之前,必須打開文件。 通常,默認情況下,爲每個進程打開3
個描述符,分別用於輸入(標準輸入 - 標準輸入),輸出(標準輸出 - 標準輸出)和錯誤(標準錯誤 - 標準錯誤),分別具有文件描述符0
,1
和2
。
這個系統調用將返回一個文件描述符,用於讀/寫/查找(lseek)的進一步文件操作。 通常文件描述符從3
開始,隨着打開的文件數量增加一個數字。
傳遞給開放系統調用的參數是路徑名(相對路徑或絕對路徑),標誌提到了打開文件的目的(比如打開O_RDONLY
進行讀取,O_RDONR
,讀寫O_RDWR
,以追加到現有的文件 O_APPEND
,創建文件,如果不存在與O_CREAT等)和所需的模式提供讀/寫/執行權限的用戶或所有者/組/其他人。 模式可以用符號來描述。
讀使用4
表示,寫使用2
表示和執行使用1
表示。
例如:八進制值(以0開頭),0764表示擁有者擁有讀取,寫入和執行權限,組擁有讀寫權限,其他擁有讀權限。 這也可以表示爲S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH
,表示或操作0700 | 0040 | 0004
→0764
。
這個系統調用在成功時返回新的文件描述符id
和-1
以防出錯。 錯誤的原因可以用errno
變量或perror()
函數來標識。
#include<unistd.h>
int close(int fd)
上面的系統調用關閉已經打開的文件描述符。 這意味着文件不再被使用,相關的資源可以被任何其他進程重用。 這個系統調用在成功時返回零,在出錯時返回-1
。 錯誤的原因可以用errno
變量或perror()
函數來標識。
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count)
上面的系統調用是從指定的文件中讀取文件描述符fd的參數,具有分配內存(靜態或動態)的正確緩衝區以及緩衝區的大小。
文件描述符ID用於標識在調用open()
或pipe()
系統調用之後返回的相應文件。 從文件中讀取文件之前,需要打開該文件。 它會在調用pipe()
系統調用的情況下自動打開。
這個調用將返回成功時讀取的字節數(或在遇到文件結尾的情況下爲零),在失敗的情況下返回-1
。 返回字節可以小於請求的字節數,以防萬一沒有數據可用或文件關閉。
要知道失敗的原因,請檢查errno
變量或perror()
函數。
#include<unistd.h>
ssize_t write(int fd, void *buf, size_t count)
上面的系統調用是使用文件描述符fd
的參數,具有已分配內存(靜態或動態)和緩衝區大小的適當緩衝區來寫入指定文件。
文件描述符ID用於標識在調用open()
或pipe()
系統調用之後返回的相應文件。
在寫入文件之前需要打開該文件。 它會在調用pipe()
系統調用的情況下自動打開。
這個調用將返回成功寫入的字節數(或者在沒有寫入的情況下爲零),如果失敗則返回-1
。 正確的錯誤碼設置在失敗的情況下。
要知道失敗的原因,請檢查errno
變量或perror()
函數。
示例程序
以下是一些示例程序。
示例程序1 - 使用管道編寫和讀取兩條消息的程序。
算法
第1步 - 創建一個管道。
第2步 - 發送消息到管道。
第3步 - 從管道中檢索消息並將其寫入標準輸出。
第4步 - 發送另一條消息到管道。
第5步 - 從管道中檢索消息並將其寫入標準輸出。
注 - 檢索消息也可以在發送所有消息之後完成。
源代碼: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;
}
注 - 理想情況下,每個系統調用都需要檢查返回狀態。 爲了簡化這個過程,所有的調用都沒有進行檢查。
執行步驟
首先編譯 -
$ gcc -o simplepipe simplepipe.c
執行輸出結果如下 -
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 - 使用父進程和子進程通過管道寫入和讀取兩條消息的程序。
算法
第1步 - 創建一個管道。
第2步 - 創建一個子進程。
第3步 - 父進程寫入管道。
第4步 - 子進程從管道中檢索消息並將其寫入標準輸出。
第5步 - 再次重複步驟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;
}
執行步驟
首先編譯 -
$ gcc pipewithprocesses.c –o pipewithprocesses
執行輸出結果如下 -
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
使用管道雙向通信
管道通信僅被視爲單向通信,即,父進程寫入和子進程讀取,或者相反,但不是兩者。 但是,如果父子進程雙方都需要同時寫入和讀取管道,那麼解決方案就是使用管道的雙向通信。 建立雙向通信需要兩根管道。
以下是實現雙向通信的步驟 -
第1步 - 創建兩個管道。 第一個是父進程寫入和子進程讀取,如:pipe1。 第二個是爲子進程寫入和父進程讀取,如:pipe2。
第2步 - 創建一個子進程。
第3步 - 關閉不需要的結束,因爲每個通信只需要一端。
第4步 - 在父進程中關閉不需要的結束,讀取pipe1
的結尾並寫入pipe2
的結尾。
第5步 - 關閉子進程中的不需要的結束,寫入pipe1
的結尾和讀取pipe2
的結束。
第6步 - 根據需要進行通訊。
示例程序
示例程序1 - 使用管道實現雙向通信。
算法
第1步 - 創建pipe1
爲父進程寫和子進程讀取。
第2步 - 爲要寫入的子進程和要讀取的父進程創建pipe2
。
第3步 - 關閉從父進程和子進程一側管道不需要的一端。
第4步 - 父進程寫一個消息和子進程讀取並顯示在屏幕上。
第5步 - 子進程寫入消息和父進程讀取並顯示在屏幕上。
源代碼: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;
}
執行步驟
首先編譯 -
$ gcc twowayspipe.c –o twowayspipe
執行輸出結果如下 -
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