Linux進程間通信通常使用的方式有很多種,其中比較常用的包括管道(pipe)和 FIFO(命名管道)。本文將介紹這兩種通信方式的基本概念,並用C語言編寫示例代碼,來說明如何在兩個進程之間使用這些IPC機制進行通信。 管道(pipe) 管道是一種半雙工的通信方式,用於父進程和子進程之間的通信。在 L ...
Linux進程間通信通常使用的方式有很多種,其中比較常用的包括管道(pipe)和 FIFO(命名管道)。本文將介紹這兩種通信方式的基本概念,並用C語言編寫示例代碼,來說明如何在兩個進程之間使用這些IPC機制進行通信。
管道(pipe)
管道是一種半雙工的通信方式,用於父進程和子進程之間的通信。在 Linux 中,管道是一種特殊的文件,有兩個端點,一個讀端和一個寫端。管道的基本操作包括創建管道、關閉文件描述符、讀取數據和寫入數據等。
創建管道
在 Linux 中,我們可以使用 pipe() 系統調用來創建管道。pipe() 函數的原型如下:
#include <unistd.h>
int pipe(int pipefd[2]);
其中,pipefd 是一個數組,用於存儲管道的讀端和寫端的文件描述符。pipe() 函數成功時返回 0,失敗時返回 -1。
下麵是一個創建管道的示例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int pipefd[2];
// 創建管道
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
printf("讀端文件描述符:%d\n", pipefd[0]);
printf("寫端文件描述符:%d\n", pipefd[1]);
exit(EXIT_SUCCESS);
}
- 編譯並運行,列印如下
讀端文件描述符:3
寫端文件描述符:4
管道的讀寫
在使用管道進行通信時,父進程和子進程可以通過管道進行數據的讀取和寫入。在 C 語言中,我們可以使用 read()函數和 write() 函數來讀取和寫入數據。read() 函數用於從管道中讀取數據,write() 函數用於向管道中寫入數據,使用 close() 函數關閉文件描述符。在管道的使用中,我們應該在不需要的時候關閉管道的讀端和寫端,以避免資源浪費。
這三個函數的原型分別如下:
#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);
下麵是一個父進程向子進程寫入數據並讀取返回結果的示例代碼:
#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;
// 創建管道
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 創建子進程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // 子進程
// 從管道中讀取數據
if (read(pipefd[0], buf, BUF_SIZE) == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("子進程收到消息:%s\n", buf);
// 發送消息給父進程
const char* message = "Hello, parent!";
if (write(pipefd[1], message, strlen(message) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
close(pipefd[1]); // 關閉寫端
exit(EXIT_SUCCESS);
} else { // 父進程
// 發送消息給子進程
const char* message = "Hello, child!";
if (write(pipefd[1], message, strlen(message) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
// 等待子進程退出
wait(&status);
if (WIFEXITED(status)) {
printf("子進程退出,返回值:%d\n", WEXITSTATUS(status));
}
// 從管道中讀取數據
if (read(pipefd[0], buf, BUF_SIZE) == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("父進程收到消息:%s\n", buf);
close(pipefd[0]); // 關閉讀端
exit(EXIT_SUCCESS);
}
}
在這個示例代碼中,父進程先向子進程發送一條消息,子進程收到消息後向父進程發送一條消息,並退出。父進程在等待子進程退出後再從管道中讀取子進程發送的消息。
- 編譯並運行,列印如下
子進程收到消息:Hello, child!
子進程退出,返回值:0
父進程收到消息:Hello, parent!
FIFO(命名管道)
FIFO(命名管道)是一種文件系統對象,與管道類似,也可以用於進程間通信。FIFO 是一種特殊類型的文件,它可以在文件系統中被創建,並且進程可以通過文件描述符來讀取和寫入數據。
與管道不同的是,FIFO 可以被多個進程打開,並且可以在文件系統中以路徑的形式存在,因此不像管道那樣只能在具有親緣關係的進程之間使用。任何進程只要有相應的許可權就可以打開 FIFO 併進行通信。
FIFO 的創建和使用
FIFO 的創建和使用也比較簡單。首先需要使用 mkfifo() 函數創建 FIFO 文件,其原型如下
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
其中,pathname 是 FIFO 文件的路徑名,mode 是文件的許可權。
創建 FIFO 文件後,就可以像使用普通文件一樣打開它,並使用 read() 和 write() 函數進行數據的讀寫。
下麵是一個使用 FIFO 進行進程間通信的示例代碼:
#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];
// 創建 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 中寫入數據
const char* message = "Hello, world!";
if (write(fd, message, strlen(message) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
// 從 FIFO 中讀取數據
if (read(fd, buf, BUF_SIZE) == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("收到消息:%s\n", buf);
// 關閉文件描述符並刪除 FIFO 文件
close(fd);
if (unlink(FIFO_PATH) == -1) {
perror("unlink");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
在這個示例代碼中,程式先創建了一個 FIFO 文件 /tmp/myfifo,然後打開該文件並向其中寫入一條消息。接下來從 FIFO 文件中讀取數據,並將其列印出來。最後關閉文件描述符並刪除 FIFO 文件。
- 編譯並運行,列印如下
收到消息:Hello, world!
小結
Linux 中管道和 FIFO 是進程間通信的重要方式。管道只能用於親緣關係的進程間通信,而 FIFO 可以被多個進程打開,不受進程之間關係的限制。無論是管道還是 FIFO,它們的使用方式都與普通文件類似,需要使用文件描述符和 read()、write() 函數來進行數據的讀寫。
在使用管道和 FIFO 進行進程間通信時,需要註意以下幾點:
- 管道和 FIFO 只能用於同一主機上的進程間通信,不能用於跨主機通信。
- 管道和 FIFO 的讀寫操作是阻塞的,這意味著當一個進程嘗試從一個空管道或 FIFO 中讀取數據時,它會被阻塞,直到有數據可用為止。同樣,當一個進程嘗試將數據寫入一個滿的管道或 FIFO 時,它也會被阻塞,直到有空閑空間為止。
- 在使用管道和 FIFO 進行進程間通信時,需要註意文件描述符的關閉順序。
- 管道和 FIFO 只能傳輸位元組流,不能傳輸其他類型的數據,如結構體或指針。
- 如果使用管道或 FIFO 進行進程間通信時,數據量較大,需要進行分段傳輸,否則可能會導致阻塞或緩衝區溢出等問題。
- 管道和 FIFO 都是單向的,如果需要雙向通信,則需要建立兩個管道或 FIFO。
以上,如果覺得對你有幫助,點個贊再走吧,這樣@知微之見也有更新下去的動力!
也歡迎私信我,一起交流!