【Linux系统编程】进程间通信--有名管道
命名管道的概述
無名管道,由于沒有名字,只能用于親緣關(guān)系的進(jìn)程間通信(更多詳情,請看《無名管道》)。為了克服這個缺點,提出了命名管道(FIFO),也叫有名管道、FIFO 文件。
命名管道(FIFO)不同于無名管道之處在于它提供了一個路徑名與之關(guān)聯(lián),以 FIFO 的文件形式存在于文件系統(tǒng)中,這樣,即使與 FIFO 的創(chuàng)建進(jìn)程不存在親緣關(guān)系的進(jìn)程,只要可以訪問該路徑,就能夠彼此通過 FIFO 相互通信,因此,通過 FIFO 不相關(guān)的進(jìn)程也能交換數(shù)據(jù)。
命名管道(FIFO)和無名管道(pipe)有一些特點是相同的,不一樣的地方在于:
1、FIFO 在文件系統(tǒng)中作為一個特殊的文件而存在,但 FIFO 中的內(nèi)容卻存放在內(nèi)存中。
2、當(dāng)使用 FIFO 的進(jìn)程退出后,FIFO 文件將繼續(xù)保存在文件系統(tǒng)中以便以后使用。
3、FIFO 有名字,不相關(guān)的進(jìn)程可以通過打開命名管道進(jìn)行通信。
命名管道的創(chuàng)建
所需頭文件:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *pathname, mode_t mode);
功能:
命名管道的創(chuàng)建。
參數(shù):
pathname:?普通的路徑名,也就是創(chuàng)建后 FIFO 的名字。
mode: 文件的權(quán)限,與打開普通文件的 open() 函數(shù)中的 mode 參數(shù)相同。
返回值:
成功:0
失敗:如果文件已經(jīng)存在,則會出錯且返回 -1。
示例代碼如下:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h>int main(int argc, char *argv[]) {int ret;ret = mkfifo("my_fifo", 0666); // 創(chuàng)建命名管道if(ret != 0){ // 出錯perror("mkfifo");}return 0; }
運行結(jié)果如下:
命名管道的默認(rèn)操作
后期的操作,把這個命名管道當(dāng)做普通文件一樣進(jìn)行操作:open()、write()、read()、close()。但是,和無名管道一樣,操作命名管道肯定要考慮默認(rèn)情況下其阻塞特性。
下面驗證的是默認(rèn)情況下的特點,即 open() 的時候沒有指定非阻塞標(biāo)志( O_NONBLOCK )。
1)
open() 以只讀方式打開 FIFO 時,要阻塞到某個進(jìn)程為寫而打開此 FIFO
open() 以只寫方式打開 FIFO 時,要阻塞到某個進(jìn)程為讀而打開此 FIFO。
簡單一句話,只讀等著只寫,只寫等著只讀,只有兩個都執(zhí)行到,才會往下執(zhí)行。
只讀端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_RDONLY);//等著只寫if(fd < 0){perror("open fifo");}printf("after open\n");return 0; }
只寫端代碼如下
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_WRONLY); //等著只讀if(fd < 0){perror("open fifo");}printf("after open\n");return 0; }
大家開啟兩個終端,分別編譯以上代碼,讀端程序和寫端程序各自運行,如下圖,大家自行驗證其特點,因為光看結(jié)果圖是沒有效果,大家需要分析其運行過程是如何變化。
如果大家不想在 open() 的時候阻塞,我們可以以可讀可寫方式打開 FIFO 文件,這樣 open() 函數(shù)就不會阻塞。
// 可讀可寫方式打開 int fd = open("my_fifo", O_RDWR);2)假如 FIFO 里沒有數(shù)據(jù),調(diào)用 read() 函數(shù)從 FIFO 里讀數(shù)據(jù)時 read() 也會阻塞。這個特點和無名管道是一樣的。
寫端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);//創(chuàng)建命名管道if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_WRONLY); //等著只讀if(fd < 0){perror("open fifo");}printf("after open\n");printf("before write\n");// 5s后才往命名管道寫數(shù)據(jù),沒數(shù)據(jù)前,讀端read()阻塞sleep(5);char send[100] = "Hello Mike";write(fd, send, strlen(send));printf("write to my_fifo buf=%s\n", send);return 0; }
讀端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); //創(chuàng)建命名管道if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_RDONLY);//等著只寫if(fd < 0){perror("open fifo");}printf("after open\n");printf("before read\n");char recv[100] = {0};//讀數(shù)據(jù),命名管道沒數(shù)據(jù)時會阻塞,有數(shù)據(jù)時就取出來read(fd, recv, sizeof(recv)); printf("read from my_fifo buf=[%s]\n", recv);return 0; }請根據(jù)下圖自行編譯運行驗證:
3)通信過程中若寫進(jìn)程先退出了,就算命名管道里沒有數(shù)據(jù),調(diào)用 read() 函數(shù)從 FIFO 里讀數(shù)據(jù)時不阻塞;若寫進(jìn)程又重新運行,則調(diào)用 read() 函數(shù)從 FIFO 里讀數(shù)據(jù)時又恢復(fù)阻塞。
寫端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); // 創(chuàng)建命名管道if(ret != 0){perror("mkfifo");}fd = open("my_fifo", O_WRONLY); // 等著只讀if(fd < 0){perror("open fifo");}char send[100] = "Hello Mike";write(fd, send, strlen(send)); //寫數(shù)據(jù)printf("write to my_fifo buf=%s\n",send);while(1); // 阻塞,保證讀寫進(jìn)程保持著通信過程return 0; }
讀端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);// 創(chuàng)建命名管道if(ret != 0){perror("mkfifo");}fd = open("my_fifo", O_RDONLY);// 等著只寫if(fd < 0){perror("open fifo");}while(1){char recv[100] = {0};read(fd, recv, sizeof(recv)); // 讀數(shù)據(jù)printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0; }請根據(jù)下圖自行編譯運行驗證:
5)通信過程中,讀進(jìn)程退出后,寫進(jìn)程向命名管道內(nèi)寫數(shù)據(jù)時,寫進(jìn)程也會(收到 SIGPIPE 信號)退出。
6)調(diào)用 write() 函數(shù)向 FIFO 里寫數(shù)據(jù),當(dāng)緩沖區(qū)已滿時 write() 也會阻塞。
5)和?6)這兩個特點和無名管道是一樣的,這里不再驗證,詳情請看《無名管道》。
命名管道非阻塞標(biāo)志操作
命名管道可以以非阻塞標(biāo)志(O_NONBLOCK)方式打開:RDONLY|O_NONBLOCK); ?
fd = open("my_fifo", O_WRONLY|O_NONBLOCK); fd = open("my_fifo", O_RDONLY|O_NONBLOCK);
1、先以只讀方式打開,如果沒有進(jìn)程已經(jīng)為寫而打開一個 FIFO, 只讀 open() 成功,并且 open() 不阻塞。
2、先以只寫方式打開,如果沒有進(jìn)程已經(jīng)為讀而打開一個 FIFO,只寫 open() 將出錯返回 -1。
3、read()、write() 讀寫命名管道中讀數(shù)據(jù)時不阻塞。
請根據(jù)以下代碼自行編譯運行驗證。
寫端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); // 創(chuàng)建命名管道if(ret != 0){perror("mkfifo");}// 只寫并指定非阻塞方式打開fd = open("my_fifo", O_WRONLY|O_NONBLOCK);if(fd<0){perror("open fifo");}char send[100] = "Hello Mike";write(fd, send, strlen(send));printf("write to my_fifo buf=%s\n",send);while(1);return 0; }讀端代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); // 創(chuàng)建命名管道if(ret != 0){perror("mkfifo");}// 只讀并指定非阻塞方式打開fd = open("my_fifo", O_RDONLY|O_NONBLOCK);if(fd < 0){perror("open fifo");}while(1){char recv[100] = {0};read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0; }
總結(jié)
以上是生活随笔為你收集整理的【Linux系统编程】进程间通信--有名管道的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】进程间通信--无名
- 下一篇: 【Linux系统编程】进程间通信--消息