前文中筆者介紹了管道,本文接着介紹命名管道。文中演示所用環境爲 Ubuntu 18.04 desktop。html
命名管道(named pipe)又被稱爲先進先出隊列(FIFO),是一種特殊的管道,存在於文件系統中。命名管道與管道很是相似,可是又有自身的顯著特徵:編程
和管道同樣,命名管道也只能用於數據的單向傳輸,若是要用命名管道實現兩個進程間數據的雙向傳輸,建議使用兩個單向的命名管道。函數
在命令行上建立命名管道
能夠經過命令行命令 mkfifo 或 mknod 建立命名管道:spa
$ mkfifo /tmp/testp $ mknod /tmp/testp p
能夠經過 ls 命令查看命名管道的文件屬性:命令行
輸出中的第一個字符爲 p,表示這個文件的類型爲管道。最後的 | 符號是有 ls 命令的 -F 選項添加的,也表示這個一個管道。設計
在程序中建立命名管道
在程序中建立命名管道,能夠使用 mkfifo 函數,其簽名以下:3d
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
參數 pathname 是一個字符串指針,用於存放命名管道的文件路徑。參數 mode 用於表示指定所建立文件的權限。該函數調用成功時返回 0;調用失敗時返回 -1。
mkfifo 函數是一個專門用來建立命名管道的函數,而另一個函數 mknod 卻能夠兼職建立命名文件,其函數簽名以下:指針
#include <sys/types.h> #include <sys/stat.h> int mknod(char *pathname, mode_t mode, dev_t dev);
建立命名管道只是 mknod 函數的功能之一,它的前兩個參數和 mkfifo 函數相同。在建立命名管道時,爲第三個參數 dev 傳遞 0 就能夠了。該函數調用成功時返回 0;調用失敗時返回 -1。code
下面的 demo 模擬一個生產者進程和消費者進程,兩者經過命名管道傳輸數據。生產者的代碼以下:htm
#include <limits.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define FIFO_NAME "/tmp/testp" #define BUFFER_SIZE 4096 #define TEN_MEG (1024 * 1024 * 10) int main(void) { int pipe_fd; int res; int open_mode = O_WRONLY; int bytes_sent = 0; char buffer[BUFFER_SIZE + 1]; if(access(FIFO_NAME, F_OK) == -1) { res = mkfifo(FIFO_NAME, 0777); if(res != 0) { fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME); exit(EXIT_FAILURE); } } printf("Process %d opening FIFO O_WRONLY\n", getpid()); pipe_fd = open(FIFO_NAME, open_mode); printf("Process %d opened fd %d\n", getpid(), pipe_fd); if(pipe_fd != -1) { while(bytes_sent < TEN_MEG) { res = write(pipe_fd, buffer, BUFFER_SIZE); if(res == -1) { fprintf(stderr, "Write error on pipe\n"); exit(EXIT_FAILURE); } bytes_sent += res; } (void)close(pipe_fd); } else { exit(EXIT_FAILURE); } printf("Process %d finished\n", getpid()); exit(EXIT_SUCCESS); }
把上面的代碼保存到文件 namedpipedemo.c 中。
消費者的代碼以下:
#include <limits.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define FIFO_NAME "/tmp/testp" #define BUFFER_SIZE 4096 int main(void) { int pipe_fd; int res; int open_mode = O_RDONLY; int bytes_read = 0; char buffer[BUFFER_SIZE + 1]; memset(buffer, '\0', sizeof(buffer)); printf("Process %d opening FIFO O_RDONLY\n", getpid()); pipe_fd = open(FIFO_NAME, open_mode); printf("Process %d opened fd %d\n", getpid(), pipe_fd); if(pipe_fd != -1) { do { res = read(pipe_fd, buffer, BUFFER_SIZE); bytes_read += res; } while (res > 0); (void)close(pipe_fd); } else { exit(EXIT_FAILURE); } printf("Process %d finished, %d bytes read\n", getpid(), bytes_read); exit(EXIT_SUCCESS); }
把上面的代碼保存到文件 namedpipedemo2.c 中。並分別編譯這兩個程序:
$ gcc -Wall namedpipedemo.c -o pipe1 $ gcc -Wall namedpipedemo2.c -o pipe2
先在一個終端中執行生產者:
而後在另外一個終端中執行消費者:
結果是兩者完成數據傳輸後都返回了:
刪除命名管道和刪除一個普通文件沒有什麼區別:
$ rm /tmp/testp
這就能夠了!
參考:
《Linux 程序設計》
《Linux 環境下 C 編程指南》