管道是用於相關過程之間的通信。 我們是否可以使用管道進行不相關的進程通信,比方說,我們要從一個終端執行客戶端程式,從另一個終端執行伺服器程式? 答案是否定的。那麼怎樣才能實現不相關的進程通信,簡單的答案就是使用 命名管道。 即使這適用於相關的進程,但是使用命名管道進行相關的進程通信沒有任何意義。
我們使用一個管道進行單向通信,兩個管道進行雙向通信。 命名管道是否適用相同的條件。 答案是否定的,我們可以使用單一命名管道作為命名管道支持雙向通信(伺服器和客戶端之間的通信,同時還有客戶端和服務器之間的通信)。
命名管道的另一個名稱是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)。
這個調用在成功時將返回0
,在失敗的情況下為-1
。 要知道失敗的原因,請檢查errno
變數或perror()
函數。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
這個庫函數創建一個FIFO專用檔,用於命名管道。 這個函數的參數是檔案名和模式。 檔案名可以是絕對路徑或相對路徑。 如果未提供完整路徑名(或絕對路徑),則將在執行過程的當前檔夾中創建該檔。 檔模式資訊如mknod()
系統調用中所述。
這個調用在成功時將返回0
,在失敗的情況下為-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步 - 伺服器進程執行以下操作 -
- 如果未創建,則在
/tmp
目錄中創建名為“fifo_twoway”
的命名管道(使用庫函數mkfifo()
)。 - 為讀寫目的打開命名管道。
- 在這裏,創建了具有對所有者的讀寫許可權的
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