程序間通訊 - 命名管道
管道旨在用於相關程序之間的通訊。我們能否將管道用於不相關程序的通訊,例如,我們想從一個終端執行客戶端程式,從另一個終端執行伺服器程式?答案是不行。那麼我們如何實現不相關程序的通訊呢?簡單的答案是命名管道。儘管這適用於相關程序,但對於相關程序的通訊使用命名管道毫無意義。
我們使用一個管道進行單向通訊,使用兩個管道進行雙向通訊。命名管道是否也適用相同的條件?答案是否定的,我們可以使用單個命名管道進行雙向通訊(伺服器和客戶端之間的通訊,以及客戶端和伺服器同時的通訊),因為命名管道支援雙向通訊。
命名管道的另一個名稱是FIFO(先進先出)。讓我們看看建立命名管道的系統呼叫 (mknod()),它是一種特殊檔案。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int mknod(const char *pathname, mode_t mode, dev_t dev);
此係統呼叫將建立一個特殊檔案或檔案系統節點,例如普通檔案、裝置檔案或 FIFO。系統呼叫的引數為路徑名、模式和裝置。路徑名以及模式和裝置資訊的屬性。路徑名是相對的,如果未指定目錄,則將在當前目錄中建立。指定的模式是檔案的模式,它指定檔案型別,例如檔案型別和檔案模式,如以下表格中所述。dev 欄位用於指定裝置資訊,例如主裝置號和次裝置號。
| 檔案型別 | 描述 | 檔案型別 | 描述 |
|---|---|---|---|
| S_IFBLK | 塊特殊檔案 | S_IFREG | 普通檔案 |
| S_IFCHR | 字元特殊檔案 | S_IFDIR | 目錄 |
| S_IFIFO | FIFO 特殊檔案 | S_IFLNK | 符號連結 |
| 檔案模式 | 描述 | 檔案模式 | 描述 |
|---|---|---|---|
| S_IRWXU | 所有者可讀、可寫、可執行/搜尋 | S_IWGRP | 組可寫許可權 |
| S_IRUSR | 所有者可讀許可權 | S_IXGRP | 組可執行/搜尋許可權 |
| S_IWUSR | 所有者可寫許可權 | S_IRWXO | 其他使用者可讀、可寫、可執行/搜尋 |
| S_IXUSR | 所有者可執行/搜尋許可權 | S_IROTH | 其他使用者可讀許可權 |
| S_IRWXG | 組可讀、可寫、可執行/搜尋 | S_IWOTH | 其他使用者可寫許可權 |
| S_IRGRP | 組可讀許可權 | S_IXOTH | 其他使用者可執行/搜尋許可權 |
檔案模式也可以用八進位制表示法表示,例如 0XYZ,其中 X 表示所有者,Y 表示組,Z 表示其他使用者。X、Y 或 Z 的值範圍為 0 到 7。讀、寫和執行的值分別為 4、2、1。如果需要讀、寫和執行的組合,則相應地新增這些值。
例如,如果我們寫 0640,則表示所有者可讀可寫 (4 + 2 = 6),組可讀 (4),其他使用者無許可權 (0)。
如果成功,此呼叫將返回零,如果失敗則返回 -1。要了解失敗的原因,請檢查 errno 變數或 perror() 函式。
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode)
此庫函式建立一個 FIFO 特殊檔案,用於命名管道。此函式的引數為檔名和模式。檔名可以是絕對路徑或相對路徑。如果沒有給出完整路徑名(或絕對路徑),則將在正在執行程序的當前資料夾中建立檔案。檔案模式資訊如 mknod() 系統呼叫中所述。
如果成功,此呼叫將返回零,如果失敗則返回 -1。要了解失敗的原因,請檢查 errno 變數或 perror() 函式。
讓我們考慮一個在一個終端執行伺服器並在另一個終端執行客戶端的程式。該程式只會執行單向通訊。客戶端接受使用者輸入並將訊息傳送到伺服器,伺服器在輸出上列印訊息。該過程將持續進行,直到使用者輸入字串“end”。
讓我們透過一個例子來理解這一點:
步驟 1 - 建立兩個程序,一個是 fifoserver,另一個是 fifoclient。
步驟 2 - 伺服器程序執行以下操作:
建立名為“MYFIFO”的命名管道(使用系統呼叫 mknod()),如果尚未建立。
以只讀方式開啟命名管道。
此處,建立的 FIFO 具有所有者可讀可寫許可權。組可讀,其他使用者無許可權。
無限期地等待來自客戶端的訊息。
如果從客戶端接收到的訊息不是“end”,則列印該訊息。如果訊息是“end”,則關閉 fifo 並結束程序。
步驟 3 - 客戶端程序執行以下操作:
以只寫方式開啟命名管道。
接受來自使用者的字串。
檢查使用者輸入的是“end”還是其他字串。無論哪種方式,它都會將訊息傳送到伺服器。但是,如果字串是“end”,則這將關閉 FIFO 並結束程序。
無限期地重複,直到使用者輸入字串“end”。
現在讓我們看看 FIFO 伺服器檔案。
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
編譯和執行步驟
Received string: "this is string 1" and length is 16 Received string: "fifo test" and length is 9 Received string: "fifo client and server" and length is 22 Received string: "end" and length is 3
現在,讓我們看看 FIFO 客戶端示例程式碼。
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
讓我們看看到達的輸出。
編譯和執行步驟
FIFO_CLIENT: Send messages, infinitely, to end enter "end" Enter string: this is string 1 Sent string: "this is string 1" and string length is 16 Enter string: fifo test Sent string: "fifo test" and string length is 9 Enter string: fifo client and server Sent string: "fifo client and server" and string length is 22 Enter string: end Sent string: "end" and string length is 3
使用命名管道的雙向通訊
管道之間的通訊旨在為單向通訊。通常,管道僅限於單向通訊,並且需要至少兩個管道才能進行雙向通訊。管道僅用於程序間通訊。管道不能用於不相關程序的通訊,例如,如果我們想在一個終端執行一個程序,在另一個終端執行另一個程序,那麼使用管道是不可能的。我們是否有簡單的方法來在兩個程序之間進行通訊,例如以簡單的方式進行不相關程序之間的通訊?答案是肯定的。命名管道旨在用於兩個或多個不相關程序之間的通訊,並且還可以進行雙向通訊。
我們已經看到了命名管道之間的單向通訊,即從客戶端到伺服器的訊息。現在,讓我們看看雙向通訊,即客戶端向伺服器傳送訊息,伺服器接收訊息並使用相同的命名管道將另一條訊息傳送回客戶端。
以下是一個示例:
步驟 1 - 建立兩個程序,一個是 fifoserver_twoway,另一個是 fifoclient_twoway。
步驟 2 - 伺服器程序執行以下操作:
建立名為“fifo_twoway”的命名管道(使用庫函式 mkfifo())在 /tmp 目錄中,如果尚未建立。
以讀寫方式開啟命名管道。
此處,建立的 FIFO 具有所有者可讀可寫許可權。組可讀,其他使用者無許可權。
無限期地等待來自客戶端的訊息。
如果從客戶端接收到的訊息不是“end”,則列印該訊息並反轉字串。反轉後的字串將傳送回客戶端。如果訊息是“end”,則關閉 fifo 並結束程序。
步驟 3 - 客戶端程序執行以下操作:
以讀寫方式開啟命名管道。
接受來自使用者的字串。
檢查使用者輸入的是“end”還是其他字串。無論哪種方式,它都會將訊息傳送到伺服器。但是,如果字串是“end”,則這將關閉 FIFO 並結束程序。
如果傳送的訊息不是“end”,它將等待來自客戶端的訊息(反轉後的字串)並列印反轉後的字串。
無限期地重複,直到使用者輸入字串“end”。
現在,讓我們看看 FIFO 伺服器示例程式碼。
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
編譯和執行步驟
FIFOSERVER: Received string: "LINUX IPCs" and length is 10 FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10 FIFOSERVER: Received string: "Inter Process Communication" and length is 27 FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27 FIFOSERVER: Received string: "end" and length is 3
現在,讓我們看看 FIFO 客戶端示例程式碼。
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
編譯和執行步驟
FIFO_CLIENT: Send messages, infinitely, to end enter "end" Enter string: LINUX IPCs FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10 FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10 Enter string: Inter Process Communication FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27 FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27 Enter string: end FIFOCLIENT: Sent string: "end" and string length is 3