linux程式間通訊--管道(PIPE & FIFO)
linux程式間通訊–管道(PIPE & FIFO)
參考資料:
- overview of pipes and FIFOs:
man 7 pipe
- 《The Linux Programming inTerface》
管道一般分為無名管道pipe
和有名管道mkfifo
.都用於程式之間的通訊。下面將一一介紹它們。
無名管道
無名管道
(pipe)一般用於關聯程式(如父子程式)之間的通訊。它的使用類似檔案,但他不是普通檔案,不屬於某種檔案系統。我們最常見的pipe使用是shell命令,如ls | wc -l
。pipe有如下特點:
半雙工
:資料只能向一個方向流動.需要雙方通訊時,建立兩個管道.關聯程式
:只能用於關聯程式之間。如父子程式或者兄弟程式之間通訊。面向位元組流
:從管道可讀取任意大小的資料塊。且只能順序的讀取,即不能使用lseek()隨機訪問管道中的資料。原子操作
:如果多個程式向一個管道寫入資料,如果它們一次寫入的位元組不超過PIPE_BUF(在limits.h),就可以保證它們的資料不會混雜在一起。
管道在讀寫時的行為如下:
寫入達到上限
:當寫入管道的資料達到上限,則阻塞.直到管道資料被讀走。然後在繼續寫入。上限預設為65536.可通過fcntl(fd, F_SETPIPE_SZ, size)
修改。讀出為空
:從空的管道中讀取資料,程式阻塞。直到至少有一個位元組被寫入該管道。如果管道的寫端被關閉,那麼讀取資料的程式在讀取完管道中剩餘資料後將看到檔案結束(end-of-file)。
使用函式pipe()建立管道,需要包含標頭檔案unistd.h
.:
int pipe(int fildes[2])
- 建立一個管道,並將管道的讀寫端檔案描述符(分別)放入filedes[0]和filedes[1]中。有了檔案操作符後,我們就可以使用
write()
,read()
進行讀寫了。 - fildes:檔案描述符陣列,
fildes[0]
儲存讀檔案描述符。fildes[0]
儲存寫檔案描述符。 - return:if true:0,if false:-1.errno如下:
- EMFILE:程式開啟檔案過多
- ENFILE:整個系統中開啟的檔案太多了。
- 建立一個管道,並將管道的讀寫端檔案描述符(分別)放入filedes[0]和filedes[1]中。有了檔案操作符後,我們就可以使用
linux提供了非標準的pipe2()函式。功能比pipe()豐富。可自行查詢瞭解。
利用管道進行程式間通訊的原理如下圖:
下面是一個pipe使用的例程,展示了父子程式之間的通訊:
#include <unistd.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
void main(void)
{
int res;
int pipe_fd[2];
//1. 建立管道
res = pipe(pipe_fd);
if(res == -1){
printf("pipe error\n");
exit(1);
}
//2. 建立執行緒
pid_t pid = fork();
if(pid == 0){//子程式
char buf[24];
//3. 子程式用於讀,所以關掉寫檔案描述符
if (close(pipe_fd[1]) == -1){
printf("pipe pipe_fd[1] close error\n");
exit(1);
}
//4. 等待接收父程式的資料
while (1){
ssize_t num = read(pipe_fd[0], buf, 24);
if(num == 0){
break;
}
else if (num == -1){
printf("read error\n");
exit(1);
}
write(STDOUT_FILENO, buf, num);//列印到控制檯
}
write(STDOUT_FILENO, "\n", 1);
//5. 關掉讀檔案描述符
if (close(pipe_fd[0]) == -1){
printf("pipe pipe_fd[0] close error\n");
exit(1);
}
exit(0);
}
else{//父程式
//6. 父程式用於寫,所以關掉讀檔案描述符
if (close(pipe_fd[0]) == -1){
printf("pipe pipe_fd[0] close error\n");
exit(1);
}
//7. 傳送相關資料
char write_buff[] = "hello word";
write(pipe_fd[1], write_buff,sizeof(write_buff));
//8. 關掉寫檔案描述符
if (close(pipe_fd[1]) == -1){
printf("pipe pipe_fd[1] close error\n");
exit(1);
}
wait(NULL);
exit(0);
}
}
有名管道
有名管道
(FIFO)是無名管道(pipe)的一種升級版。主要區別如下:
- 有名管道有檔名稱且在檔案系統中可見。
- 由於有名稱,所以可以在任意程式下建立通訊。
有名管道在讀寫時的行為如下:
- 讀取FIFO檔案的程式只能以
RDONLY
方式開啟FIFO檔案. - 寫FIFO檔案的程式只能以
WRONLY
方式開啟FIFO.
其他與無名管道相似,例如:
- 不允許檔案定位,只能順序訪問。
- 面向位元組流。
管道在進行讀寫時必須都處於開啟狀態
:這句話說的比較模糊需要解釋一下。筆者測試後得到如下結論:在open()函式開啟fifo檔案階段
:寫方式的open()會阻塞,等待讀方式的open()開啟fifo檔案。這個階段後管道的讀寫端便處於同時開啟狀態。即滿足上邊的條件。使用read(),write()操作fifo階段
:在fifo讀寫端都開啟之後(即滿足第一個條件後),便可以進行讀寫操作了。行為與pipe相同寫入達到上限
:當寫入管道的資料達到上限,則阻塞.直到管道資料被讀走。然後在繼續寫入。上限預設為65536.讀出為空
:從空的管道中讀取資料,程式阻塞。直到至少有一個位元組被寫入該管道。如果管道的寫端被關閉,那麼讀取資料的程式在讀取完管道中剩餘資料後將看到檔案結束(end-of-file)。
在讀寫過程中,如果close()了讀取管道的檔案操作符
:此時進行寫操作會觸發SIGPIPE
訊號,如果訊號被處理或阻塞,它會以錯誤程式碼EPIPE失敗。在讀寫過程中,如果close()了寫入管道的檔案操作符
:讀取資料的程式在讀取完管道中剩餘資料後將看到檔案結束(end-of-file)。
使用mkfifo()函式建立一個命名管道。使用時需要包含標頭檔案sys/stat.h
。函式如下:
int mkfifo (const char *filename, mode t mode)
- 建立一個命名管道.
- filename:檔名
- mode:檔案許可權。如
0666
- return:if true:0,if false:-1.errno如下:
- EEXIST:名稱已經存在
其餘的函式和檔案操作函式相同。
演示例程如下:
#include <unistd.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void main(void)
{
int res;
//1. 建立fifo
res = mkfifo("/tmp/fifo", 0666);
if(res == -1 && errno != EEXIST){
printf("mkfifo error\n");
exit(1);
}
//2. 程式之間使用fifo通訊,為了方便直接使用父子程式
pid_t pid = fork();
if(pid == 0){//子程式
//3. 開啟fifo
int fd = open("/tmp/fifo", O_RDONLY);
if(fd == -1){
printf("(%d)open error\n",getpid());
exit(1);
}
char buff[32];
while(1){
//4. 讀取fifo的內容
ssize_t num = read(fd, buff, sizeof(buff));
if(num == 0){
break;
}
else if (num == -1){
printf("read error\n");
exit(1);
}
write(STDOUT_FILENO,buff,num);
}
write(STDOUT_FILENO, "\n", 1);
//5. 關掉讀檔案描述符
if (close(fd) == -1){
printf("(%d) close error\n",getpid());
exit(1);
}
}
else{
//6. 父程式以只寫方式開啟
int fd = open("/tmp/fifo", O_WRONLY);
if (fd == -1){
printf("(%d)open error\n",getpid());
exit(1);
}
//7. 傳送相關資料
char write_buff[] = "hello word";
write(fd, write_buff,sizeof(write_buff));
//8. 關掉寫檔案描述符
if (close(fd) == -1){
printf("(%d) close error\n",getpid());
exit(1);
}
wait(NULL);
exit(0);
}
}
buff,sizeof(write_buff));
//8. 關掉寫檔案描述符
if (close(fd) == -1){
printf("(%d) close error\n",getpid());
exit(1);
}
wait(NULL);
exit(0);
}
}
關於技術交流
此處後的文字已經和題目內容無關,可以不看。
qq群:825695030
微信公眾號:嵌入式的日常
如果上面的文章對你有用,歡迎打賞、點贊、評論。
相關文章
- Linux程式間通訊②:有名管道FIFOLinux
- linux 程式間通訊之FIFOLinux
- linux 程式間通訊之管道Linux
- Linux 的程式間通訊:管道Linux
- linux環境程式設計(2): 使用pipe完成程式間通訊Linux程式設計
- Linux 下的程式間通訊:使用管道和訊息佇列Linux佇列
- Linux系統程式設計之程式間通訊方式:管道(二)Linux程式設計
- Linux系統程式設計之程式間通訊方式:管道(一)Linux程式設計
- Linux系統程式設計之程式間通訊方式:命名管道(二)Linux程式設計
- Linux系統程式設計之程式間通訊方式:命名管道(一)Linux程式設計
- 溫故之.NET程式間通訊——管道
- 程序間通訊(1)-管道
- 管道流間的通訊
- 程式間通訊——LINUXLinux
- Linux程式間通訊Linux
- 20.2、python程式間通訊——佇列和管道Python佇列
- 3|程式間通訊--有名管道學習筆記筆記
- Linux程式間通訊-eventfdLinux
- Linux程式之間如何通訊?Linux
- 什麼是程式間通訊?Linux程式間通訊有幾種方式?Linux
- 程式間通訊是什麼?Linux程式間通訊有幾種方式?Linux
- 利用管道Pipelines做程序間的通訊
- linux程式間通訊(IPC)小結Linux
- 系統程式設計——管道通訊程式設計
- Linux 核心最新高危提權漏洞:髒管道 (Dirty Pipe)Linux
- Linux 程式間通訊之System V 訊號量Linux
- 在Linux中,管道(pipe)和重定向(redirection)的是什麼?Linux
- 對於Pipe管道之愛 - jessfraz
- Linux 程式間通訊的六種機制Linux
- Linux 下的程式間通訊:共享儲存Linux
- Linux程式間通訊之共享記憶體Linux記憶體
- linux 程式間通訊之System V 訊息佇列Linux佇列
- Linux 下的程式間通訊:套接字和訊號Linux
- Linux程式間的通訊方式有哪些?Linux入門教程Linux
- 利用windows api實現程式通訊(命名管道)WindowsAPI
- Windows程式通訊之一看就懂的匿名管道通訊Windows
- Linux作業系統 程式之間的通訊Linux作業系統
- OpenCL中的管道cl::Pipe的使用