Linux進(jìn)程間通信通常使用的方式有很多種别惦,其中比較常用的包括管道(pipe)和 FIFO(命名管道)潭枣。本文將介紹這兩種通信方式的基本概念绞蹦,并用C語言編寫示例代碼拳恋,來說明如何在兩個(gè)進(jìn)程之間使用這些IPC機(jī)制進(jìn)行通信氮发。
管道(pipe)
管道是一種半雙工的通信方式渴肉,用于父進(jìn)程和子進(jìn)程之間的通信。在 Linux 中爽冕,管道是一種特殊的文件仇祭,有兩個(gè)端點(diǎn),一個(gè)讀端和一個(gè)寫端颈畸。管道的基本操作包括創(chuàng)建管道乌奇、關(guān)閉文件描述符、讀取數(shù)據(jù)和寫入數(shù)據(jù)等眯娱。
創(chuàng)建管道
在 Linux 中礁苗,我們可以使用 pipe() 系統(tǒng)調(diào)用來創(chuàng)建管道。pipe() 函數(shù)的原型如下:
#include <unistd.h>
int pipe(int pipefd[2]);
其中徙缴,pipefd 是一個(gè)數(shù)組试伙,用于存儲(chǔ)管道的讀端和寫端的文件描述符。pipe() 函數(shù)成功時(shí)返回 0,失敗時(shí)返回 -1疏叨。
下面是一個(gè)創(chuàng)建管道的示例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int pipefd[2];
// 創(chuàng)建管道
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
printf("讀端文件描述符:%d\n", pipefd[0]);
printf("寫端文件描述符:%d\n", pipefd[1]);
exit(EXIT_SUCCESS);
}
- 編譯并運(yùn)行吱抚,打印如下
讀端文件描述符:3
寫端文件描述符:4
管道的讀寫
在使用管道進(jìn)行通信時(shí),父進(jìn)程和子進(jìn)程可以通過管道進(jìn)行數(shù)據(jù)的讀取和寫入考廉。在 C 語言中秘豹,我們可以使用 read()函數(shù)和 write() 函數(shù)來讀取和寫入數(shù)據(jù)。read() 函數(shù)用于從管道中讀取數(shù)據(jù)昌粤,write() 函數(shù)用于向管道中寫入數(shù)據(jù)既绕,使用 close() 函數(shù)關(guān)閉文件描述符。在管道的使用中涮坐,我們應(yīng)該在不需要的時(shí)候關(guān)閉管道的讀端和寫端凄贩,以避免資源浪費(fèi)。
這三個(gè)函數(shù)的原型分別如下:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int close(int fd);
下面是一個(gè)父進(jìn)程向子進(jìn)程寫入數(shù)據(jù)并讀取返回結(jié)果的示例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define BUF_SIZE 1024
int main()
{
int pipefd[2];
pid_t pid;
char buf[BUF_SIZE];
int status;
// 創(chuàng)建管道
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 創(chuàng)建子進(jìn)程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // 子進(jìn)程
// 從管道中讀取數(shù)據(jù)
if (read(pipefd[0], buf, BUF_SIZE) == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("子進(jìn)程收到消息:%s\n", buf);
// 發(fā)送消息給父進(jìn)程
const char* message = "Hello, parent!";
if (write(pipefd[1], message, strlen(message) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
close(pipefd[1]); // 關(guān)閉寫端
exit(EXIT_SUCCESS);
} else { // 父進(jìn)程
// 發(fā)送消息給子進(jìn)程
const char* message = "Hello, child!";
if (write(pipefd[1], message, strlen(message) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
// 等待子進(jìn)程退出
wait(&status);
if (WIFEXITED(status)) {
printf("子進(jìn)程退出袱讹,返回值:%d\n", WEXITSTATUS(status));
}
// 從管道中讀取數(shù)據(jù)
if (read(pipefd[0], buf, BUF_SIZE) == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("父進(jìn)程收到消息:%s\n", buf);
close(pipefd[0]); // 關(guān)閉讀端
exit(EXIT_SUCCESS);
}
}
在這個(gè)示例代碼中疲扎,父進(jìn)程先向子進(jìn)程發(fā)送一條消息,子進(jìn)程收到消息后向父進(jìn)程發(fā)送一條消息捷雕,并退出椒丧。父進(jìn)程在等待子進(jìn)程退出后再從管道中讀取子進(jìn)程發(fā)送的消息。
- 編譯并運(yùn)行救巷,打印如下
子進(jìn)程收到消息:Hello, child!
子進(jìn)程退出壶熏,返回值:0
父進(jìn)程收到消息:Hello, parent!
FIFO(命名管道)
FIFO(命名管道)是一種文件系統(tǒng)對(duì)象,與管道類似浦译,也可以用于進(jìn)程間通信棒假。FIFO 是一種特殊類型的文件,它可以在文件系統(tǒng)中被創(chuàng)建精盅,并且進(jìn)程可以通過文件描述符來讀取和寫入數(shù)據(jù)帽哑。
與管道不同的是,F(xiàn)IFO 可以被多個(gè)進(jìn)程打開叹俏,并且可以在文件系統(tǒng)中以路徑的形式存在妻枕,因此不像管道那樣只能在具有親緣關(guān)系的進(jìn)程之間使用。任何進(jìn)程只要有相應(yīng)的權(quán)限就可以打開 FIFO 并進(jìn)行通信她肯。
FIFO 的創(chuàng)建和使用
FIFO 的創(chuàng)建和使用也比較簡(jiǎn)單佳头。首先需要使用 mkfifo() 函數(shù)創(chuàng)建 FIFO 文件鹰贵,其原型如下
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
其中晴氨,pathname 是 FIFO 文件的路徑名,mode 是文件的權(quán)限碉输。
創(chuàng)建 FIFO 文件后籽前,就可以像使用普通文件一樣打開它,并使用 read() 和 write() 函數(shù)進(jìn)行數(shù)據(jù)的讀寫。
下面是一個(gè)使用 FIFO 進(jìn)行進(jìn)程間通信的示例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/myfifo"
#define BUF_SIZE 1024
int main()
{
int fd;
char buf[BUF_SIZE];
// 創(chuàng)建 FIFO 文件
if (mkfifo(FIFO_PATH, 0666) == -1) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
// 打開 FIFO 文件
fd = open(FIFO_PATH, O_RDWR);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 向 FIFO 中寫入數(shù)據(jù)
const char* message = "Hello, world!";
if (write(fd, message, strlen(message) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
// 從 FIFO 中讀取數(shù)據(jù)
if (read(fd, buf, BUF_SIZE) == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("收到消息:%s\n", buf);
// 關(guān)閉文件描述符并刪除 FIFO 文件
close(fd);
if (unlink(FIFO_PATH) == -1) {
perror("unlink");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
在這個(gè)示例代碼中枝哄,程序先創(chuàng)建了一個(gè) FIFO 文件 /tmp/myfifo肄梨,然后打開該文件并向其中寫入一條消息。接下來從 FIFO 文件中讀取數(shù)據(jù)挠锥,并將其打印出來众羡。最后關(guān)閉文件描述符并刪除 FIFO 文件。
- 編譯并運(yùn)行蓖租,打印如下
收到消息:Hello, world!
小結(jié)
Linux 中管道和 FIFO 是進(jìn)程間通信的重要方式粱侣。管道只能用于親緣關(guān)系的進(jìn)程間通信,而 FIFO 可以被多個(gè)進(jìn)程打開蓖宦,不受進(jìn)程之間關(guān)系的限制齐婴。無論是管道還是 FIFO,它們的使用方式都與普通文件類似稠茂,需要使用文件描述符和 read()柠偶、write() 函數(shù)來進(jìn)行數(shù)據(jù)的讀寫。
在使用管道和 FIFO 進(jìn)行進(jìn)程間通信時(shí)睬关,需要注意以下幾點(diǎn):
- 管道和 FIFO 只能用于同一主機(jī)上的進(jìn)程間通信诱担,不能用于跨主機(jī)通信。
- 管道和 FIFO 的讀寫操作是阻塞的电爹,這意味著當(dāng)一個(gè)進(jìn)程嘗試從一個(gè)空管道或 FIFO 中讀取數(shù)據(jù)時(shí)该肴,它會(huì)被阻塞,直到有數(shù)據(jù)可用為止藐不。同樣匀哄,當(dāng)一個(gè)進(jìn)程嘗試將數(shù)據(jù)寫入一個(gè)滿的管道或 FIFO 時(shí),它也會(huì)被阻塞雏蛮,直到有空閑空間為止涎嚼。
- 在使用管道和 FIFO 進(jìn)行進(jìn)程間通信時(shí),需要注意文件描述符的關(guān)閉順序挑秉。
- 管道和 FIFO 只能傳輸字節(jié)流法梯,不能傳輸其他類型的數(shù)據(jù),如結(jié)構(gòu)體或指針犀概。
- 如果使用管道或 FIFO 進(jìn)行進(jìn)程間通信時(shí)立哑,數(shù)據(jù)量較大,需要進(jìn)行分段傳輸姻灶,否則可能會(huì)導(dǎo)致阻塞或緩沖區(qū)溢出等問題铛绰。
- 管道和 FIFO 都是單向的,如果需要雙向通信产喉,則需要建立兩個(gè)管道或 FIFO捂掰。
以上敢会,如果覺得對(duì)你有幫助,點(diǎn)個(gè)贊再走吧这嚣,這樣@知微之見也有更新下去的動(dòng)力鸥昏!
也歡迎私信我,一起交流姐帚!