Linux中的有名管道和無名管道
今天帶小伙伴們一起學習一下Linux中的有名管道和無名管道的使用,先大致介紹下有名管道和無名管道相關的內容,接著進行函數的學習,最后擼代碼!
1 無名管道與有名管道的區別
管道分為無名管道(pipe)和有名管道(FIFO)兩種。
無名管道只能用于公共祖先的兩個進程間的通信,原因是自己創建的管道在別的進程中并不可見。
有名管道可用于同一系統中的任意兩個進程間的通信。
1)無名管道
無名管道創建完成后,等同于操作文件。
無名管道的讀端被視作一個文件,寫端也被視作一個文件。
創建用pipe,操作用read、write、close。
① 無名管道通信是單向的,有固定的讀端和寫端。
② 數據被進程從管道讀出后,管道中的數據就不存在了。
③ 進程讀取空管道時,進程會阻塞。
④ 進程往滿的管道寫入數據時,進程會阻塞。
⑤ 管道容量最大為64KB,可以通過宏PIPE_BUFFERS進行設置。
下面補充兩個無名管道的兩個說明。
<1 關于公共祖先進程的說明。
管道可以用于任意兩個或更多相關進程之間的通信,只要在創建子進程的系列調用之前通過一個共同的祖先進程創建管道即可。
如管道可用于一個進程和其子孫進程之間的通信。第一個進程創建管道,然后創建子進程,接著子進程再創建第一個進程的孫子進程。
管道通常用于兩個兄弟進程之間的通信——它們的父進程創建了管道,并創建兩個子進程。
<2 關于無名管道半雙工的說明。
一般來說管道是半雙工的,有些系統實現了全雙工的功能,但為了方便移植盡量使用半雙工方式使用管道。
半雙工情況下,管道的兩端在一個進程中相互連接,數據需要通過內核在管道中流動。
單個進程對管道進行讀寫是沒有問題的,但應用比較多的是兩個進程之間的通信,但此時存在競爭的問題。
如果創建兩個管道,需要注意死鎖的問題。如果兩個進程都試圖從空管道中讀取數據或嘗試向已滿的管道中寫入數據就可能會發生死鎖。
此時由于半雙工的原因,就需要關閉不需要的讀端和寫端,下圖是父子進程進行通信的示意圖。
圖片
2)有名管道
有名管道又叫FIFO文件,它的操作與文件操作類似,需要創建、打開和關閉等操作。
創建用mkfifo,刪除用unlink,讀寫用read、write,打開關閉用open、close。
FIFO文件的操作與普通文件的操作差異如下:
① 讀取FIFO文件的進程只能以“RDONLY”方式打開FIFO文件。
② 寫FIFO文件的進程只能以“WRONLY”方式打開FIFO文件。
③ FIFO文件里面的內容被讀取后,就消失了。但是普通文件里面的內容讀取后還存在。
2 Linux下的無名管道
1)pipe函數
pipe函數用于創建無名管道。
① 函數原型。
- int pipe(int pipefd[2])
② 頭文件。
- include <unistd.h>
③ 參數。
pipefd[0]:讀管道。
pipefd[1]:寫管道。
④ 返回值。
成功:0。
失敗:-1。
3 Linux下的有名管道
1)mkfifo函數
mkfifo函數用于創建有名管道。
① 函數原型。
- int mkfifo(const char* pathname, mode_t mode)
② 頭文件。
- include <sys/type.h>
- include <sys/stat.h>
③ 參數。
pathname:要創建的fifo文件的名字,帶路徑。
mode:要創建的fifo文件的訪問權限。
④ 返回值。
成功:0。
失敗:-1。
2)unlink函數
unlink函數用于刪除有名管道,其實它可以刪除FIFO文件,也可以刪除普通文件。
① 函數原型。
- int unlink(const char* pathname)
② 頭文件。
- include <unistd.h>
③ 參數。
pathname:要創建的fifo文件的名字,帶路徑。
④ 返回值。
成功:0。
失敗:-1。
3)open函數
open函數用于打開一個有名管道。
① 函數原型。
- int open(constchar *pathname, int flags)
- int open(constchar *pathname, int flags, mode_t mode)
② 頭文件。
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
③ 參數。
pathname: 要打開的文件名(含路徑)。
flags: 文件打開的標志(這里只介紹兩種,更多的可以man 2 open查看)。
O_APPEND: 以追加的方式打開文件。
O_CREAT: 當打開的文件不存在的時候,創建該文件。
mode: 一定是在flags中使用了O_CREAT標志,mode記錄待創建的訪問權限。
④ 返回值。
成功:返回文件描述符。
失敗:返回-1。
4)read函數
read函數用于讀有名管道。
① 函數原型。
- ssize_t read(int fd, void *buf, size_t count)
② 頭文件。
- #include <unistd.h>
③ 參數。
fd:要讀取文件的文件描述符。
buf:讀緩沖的起始地址(讀取來的數據存到buf指向的空間)。
count:讀到的字節數。
④ 返回值。
成功:讀到的字節數。
失敗:返回-1。
5)write函數
write函數用于寫有名管道。
① 函數原型。
- ssize_t write(int fd, const void *buf, size_t count)
② 頭文件。
- #include <unistd.h>
③ 參數。
fd:文件描述符。
buf:文件數據緩沖區。
count:所要寫的數據字節數。
④ 返回值。
成功:寫入的字節數。
失敗:返回-1。
6)close函數
close函數用于關閉有名管道。
① 函數原型。
- int close(int fd)
② 頭文件。
- #include <unistd.h>
③ 參數。
fd:待關閉的文件描述符。
④ 返回值。
成功:返回0。
失敗:返回-1。
4 實例代碼
下面用兩個小程序用一下上面介紹的有名管道和無名管道。
1)無名管道的使用實例
實例代碼如下,說明都在代碼注釋中了,圖片。
PipeTest.c。
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- void main(void)
- {
- pid_t pid = 0; // 進程的pid
- int pipefd[2]; // 讀寫管道的文件描述符
- char buf[20]; // 數據緩沖
- pipe(pipefd); // 創建管道
- pid = fork(); // 創建子進程
- if(pid > 0) // 父進程
- {
- close(pipefd[0]); // 這是一個習慣,關閉不用的管道
- write(pipefd[1],"Test pipe!",11); // 將數據寫入管道
- wait(); // 等待子進程執行完退出
- close(pipefd[1]); // 用完關掉文件
- exit(0); // 進程退出
- }
- if(pid == 0) // 子進程
- {
- close(pipefd[1]); // 這是一個習慣,關閉不用的管道
- read(pipefd[0],buf,11); // 讀取管道內容,并打印
- printf("\nThe pipe's context is %s\n\n",buf); //子進程可以讀到父進程寫入管道的內容Test!
- close(pipefd[0]); // 關閉管道
- exit(0); // 進程退出
- }
- }
運行結果如下:
2)有名管道的使用實例
實例代碼如下,說明都在代碼注釋中了。
WriteFIFO.c。
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- int main(void)
- {
- int fifofd; // FIFO文件描述符
- int ret;
- mkfifo("/tmp/fifo",0666); // 創建FIFO文件
- fifofd = open("/tmp/fifo",O_WRONLY); // 打開FIFO文件
- ret = write(fifofd,"Test FIFO!",11); // 寫數據到FIFO文件,等待讀成功才返回
- close(fifofd); // 關閉FIFO文件
- return 0;
- }
ReadFIFO.c。
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- void main(void)
- {
- int fifofd; // 管道文件描述符
- char buf[20]; // 數據緩沖
- mkfifo("/tmp/fifo",0666); // 創建FIFO文件
- fifofd = open("/tmp/fifo",O_RDONLY); // 打開FIFO文件
- read(fifofd,buf,11); // 讀FIFO文件
- printf("\nThe FIFO's content is %s\n\n",buf); // 顯示讀到FIFO文件的內容
- unlink("/tmp/fifo"); // 刪除FIFO文件
- }
運行結果如下:
先運行WriteFIFO操作,此時會阻塞,接著運行ReadFIFO,打印并解除阻塞。
本文轉載自微信公眾號「嵌入式雜牌軍」,可以通過以下二維碼關注。轉載本文請聯系嵌入式雜牌軍公眾號。