IPC(InterProcess Communication)的方式通常有管道(包括無名管道和命名管道)、消息隊列、信號量、共享存儲、Socket、Streams等。其中Socket和Stream支持不同主機上的兩個進程IPC。
-
管道(Pipes):管道是一種半雙工的通信方式,用于具有親緣關系的進程間通信。它通常用于父子進程或者兄弟進程之間。管道可以是匿名管道,也可以是命名管道。
-
消息隊列(Message Queues):消息隊列是一種通過消息傳遞進行通信的方式。發送方將消息發送到隊列中,接收方從隊列中接收消息。消息隊列可以實現進程間的異步通信。
-
信號量(Semaphores):信號量是一種計數器,用于控制對共享資源的訪問。它通常用于同步進程之間的操作,以避免競爭條件。
-
共享內存(Shared Memory):共享內存是一種允許多個進程訪問同一塊內存區域的方式。這種方式通常比較高效,但需要處理進程間的同步和互斥。
-
套接字(Sockets):套接字是一種網絡編程接口,不僅可以用于不同主機間的進程通信,也可以用于同一主機上的進程通信。套接字可以基于網絡協議(如TCP/IP)或本地協議(如UNIX域套接字)實現。
一、管道
管道(Pipes)是一種在Unix和類Unix系統中常見的進程間通信(IPC)機制,用于在具有親緣關系的進程之間傳遞數據。管道是一個單向通道,允許一個進程將輸出直接發送到另一個進程的輸入。它是一種半雙工通信方式,即數據只能單向流動,不能雙向傳輸。
類型
-
匿名管道(Anonymous Pipes):匿名管道是最簡單的管道形式,它只存在于內存中,并且通常用于父子進程之間的通信。在Unix系統中,可以使用
pipe()
系統調用創建匿名管道。 -
命名管道(Named Pipes):命名管道是一種具有持久性的管道,它以文件的形式存在于文件系統中,并允許無關進程之間進行通信。命名管道通常用于不具有親緣關系的進程之間的通信。
特點
-
單向通信:管道是單向的,數據只能沿著管道的方向流動,不能雙向傳輸。
-
半雙工:管道是半雙工的,即數據只能在一個方向上傳輸。如果需要雙向通信,通常需要創建兩個管道。
-
FIFO(先進先出):管道遵循FIFO的原則,即數據按照寫入的順序從管道中讀取出來。
使用
在Unix系統中,可以使用pipe()
系統調用創建匿名管道,它返回兩個文件描述符,一個用于讀取,一個用于寫入。然后可以使用fork()
創建一個新的進程,在父子進程之間共享管道,并使用dup2()
系統調用將管道文件描述符重定向到標準輸入或標準輸出。接著,一個進程可以通過寫入管道的方式向另一個進程發送數據,另一個進程則可以通過讀取管道來接收數據。
示例
下面是一個簡單的C語言示例,演示了如何在父子進程之間使用匿名管道進行通信:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>int main()
{int fd[2];int pid;char buf[128];//創建管道if(pipe(fd) == -1) { printf("creat pipe failed\n");}//創建子進程pid = fork();if(pid < 0) {printf("creat child faild\n");} else if(pid > 0) { //父進程printf("this is father\n");close(fd[0]); //關閉讀取端//向管道寫數據write(fd[1], "hello from father", strlen("hello from father"));wait(NULL);} else {printf("this is child\n");close(fd[1]); //關閉寫入端read(fd[0], buf, sizeof(buf)); //從管道讀數據printf("child print: %s\n", buf);exit(1);}return 0;
}
程序執行結果如下:?
?
命名管道(Named Pipes)的使用:
命名管道是一種具有持久性的管道,它以文件的形式存在于文件系統中,并允許無關進程之間進行通信。相比于匿名管道,命名管道允許不具有親緣關系的進程之間進行通信。
創建命名管道
在Unix/Linux系統中,可以使用mkfifo()
函數創建命名管道。命名管道創建后,會在文件系統中生成一個特殊類型的文件,它可以像普通文件一樣被打開、讀取和寫入。
使用命名管道
使用命名管道和使用普通文件一樣,可以使用文件I/O操作來讀取和寫入數據。不同的是,命名管道的數據讀取和寫入是以先進先出(FIFO)的方式進行的,即寫入的數據按照寫入的順序從管道中讀取出來。
特點
- 命名管道是持久性的,創建后會一直存在于文件系統中,直到被顯式刪除。
- 允許不具有親緣關系的進程之間進行通信。
- 數據按照寫入的順序從管道中讀取出來,具有先進先出(FIFO)的特性。
?
示例
下面是一個簡單的C語言示例,演示了如何創建和使用命名管道:
先介紹一下mkfifo:mkfifo?是用于創建命名管道(FIFO)的系統調用。在 Unix 和類 Unix 系統中,命名管道以文件的形式存在,可以用于不同進程之間進行通信。
mkfifo
函數原型
#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);
參數
pathname
:要創建的命名管道的路徑。mode
:管道的權限,類似于open
或chmod
中使用的權限位。常用權限包括0666
(表示管道文件可讀可寫)。
返回值
- 成功時返回
0
。 - 失敗時返回
-1
,并設置errno
以指示錯誤。
?注意:
命名管道的讀寫操作是同步的,這意味著:
- 寫入進程會等待直到有讀取進程打開管道進行讀取。
- 讀取進程會等待直到有寫入進程向管道寫入數據。
這導致如果你先運行寫入程序而沒有相應的讀取程序在運行,寫入程序會阻塞,等待讀取程序打開管道讀取數據。同樣地,如果你先運行讀取程序而沒有寫入程序在運行,讀取程序會阻塞,等待寫入程序向管道寫入數據。
為了避免這個問題,可以按以下步驟運行程序:
- 先運行讀取程序
reader
,使其準備好從管道讀取數據。 - 然后運行寫入程序
writer
,向管道寫入數據。
writer.c (寫入程序)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>#define FIFO_FILE "/tmp/my_fifo"int main() {// 創建命名管道,權限模式為 0600if (mkfifo(FIFO_FILE, 0600) == -1) {perror("mkfifo failed");exit(EXIT_FAILURE);}// 打開命名管道以寫入數據int fd = open(FIFO_FILE, O_WRONLY);if (fd == -1) {perror("open failed");exit(EXIT_FAILURE);}// 寫入數據到命名管道const char *message = "Hello, Named Pipe!";write(fd, message, sizeof(message));// 關閉文件描述符close(fd);return 0;
}
reader.c (讀取程序)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>#define FIFO_FILE "/tmp/my_fifo"int main() {char buffer[BUFSIZ];// 打開命名管道以讀取數據int fd = open(FIFO_FILE, O_RDONLY);if (fd == -1) {perror("open failed");exit(EXIT_FAILURE);}// 從命名管道讀取數據read(fd, buffer, sizeof(buffer));printf("Received: %s\n", buffer);// 關閉文件描述符close(fd);// 刪除命名管道文件unlink(FIFO_FILE);return 0;
}
程序運行結果:
?
?