日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

【Linux系统编程学习】匿名管道pipe与有名管道fifo

發(fā)布時(shí)間:2023/11/30 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Linux系统编程学习】匿名管道pipe与有名管道fifo 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

此為牛客Linux C++和黑馬Linux系統(tǒng)編程課程筆記。

0. 關(guān)于進(jìn)程通信

Linux環(huán)境下,進(jìn)程地址空間相互獨(dú)立,每個(gè)進(jìn)程各自有不同的用戶地址空間。任何一個(gè)進(jìn)程的全局變量在另一個(gè)進(jìn)程中都看不到,所以進(jìn)程和進(jìn)程之間不能相互訪問(wèn),要交換數(shù)據(jù)必須通過(guò)內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進(jìn)程1把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進(jìn)程2再?gòu)膬?nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機(jī)制稱為進(jìn)程間通信(IPC,InterProcess Communication)。

在進(jìn)程間完成數(shù)據(jù)傳遞需要借助操作系統(tǒng)提供特殊的方法,如:文件、管道、信號(hào)、共享內(nèi)存、消息隊(duì)列、套接字、命名管道等。隨著計(jì)算機(jī)的蓬勃發(fā)展,一些方法由于自身設(shè)計(jì)缺陷被淘汰或者棄用。現(xiàn)今常用的進(jìn)程間通信方式有:
① 管道 (使用最簡(jiǎn)單)
② 信號(hào) (開銷最小)
③ 共享映射區(qū) (無(wú)血緣關(guān)系)
④ 本地套接字 (最穩(wěn)定)

1. 匿名管道

管道是一種最基本的IPC機(jī)制,作用于有血緣關(guān)系的進(jìn)程之間,完成數(shù)據(jù)傳遞。調(diào)用pipe系統(tǒng)函數(shù)即可創(chuàng)建一個(gè)管道。有如下特點(diǎn):


匿名管道采用了循環(huán)隊(duì)列,可將寫指針看作隊(duì)列頭,讀指針看作隊(duì)列尾:

2. pipe函數(shù)

Linux中使用pipe函數(shù)創(chuàng)建管道:

#include <unistd.h> int pipe(int pipefd[2]);

功能:
創(chuàng)建一個(gè)匿名管道,用于進(jìn)程間通信。

參數(shù):
int pipefd[2] 這個(gè)數(shù)組是一個(gè)傳出參數(shù)。
pipefd[0] 對(duì)應(yīng)的是管道的讀端
pipefd[1] 對(duì)應(yīng)的是管道的寫端

返回值:
成功 0
失敗 -1

注意: 管道默認(rèn)是阻塞的:如果管道中沒有數(shù)據(jù),read阻塞,如果管道滿了,write阻塞匿名管道只能用于具有關(guān)系的進(jìn)程之間的通信(父子進(jìn)程,兄弟進(jìn)程)

當(dāng)調(diào)用pipe后,當(dāng)前進(jìn)程的文件描述符表中就已經(jīng)有兩個(gè)文件描述符分別指向管道的讀端和寫端,pipefd[0]和pipefd[1]返回這兩個(gè)文件描述符。

如下示例程序能夠?qū)崿F(xiàn):子進(jìn)程發(fā)送數(shù)據(jù)給父進(jìn)程,父進(jìn)程讀取到數(shù)據(jù)輸出到終端。

#include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h>int main() {// 子進(jìn)程發(fā)送數(shù)據(jù)給父進(jìn)程,父進(jìn)程讀取到數(shù)據(jù)輸出int pipefd[2];int ret = pipe(pipefd);if(ret == -1) {perror("pipe error");exit(0);}pid_t pid = fork();if(pid > 0) {char buffer[1024] = {0};read(pipefd[0], buffer, sizeof(buffer)); // 如果管道為空,此處阻塞printf("recieved : %s", buffer);} else if(pid == 0) {char* content = "hello, im child process";write(pipefd[1], content, strlen(content));}return 0; }

創(chuàng)建管道,使用read和write分別在pipefd[0]中讀數(shù)據(jù),在pipefd[1]中寫數(shù)據(jù)。

運(yùn)行結(jié)果如下:

可見父進(jìn)程中收到了子進(jìn)程中傳遞的消息。

3. pipe管道的讀寫特點(diǎn):

使用管道時(shí),需要注意以下幾種特殊的情況(假設(shè)都是阻塞I/O操作)

1.所有的指向管道寫端的文件描述符都關(guān)閉了(管道寫端引用計(jì)數(shù)為0),有進(jìn)程從管道的讀端
讀數(shù)據(jù),那么管道中剩余的數(shù)據(jù)被讀取以后,再次read會(huì)返回0,就像讀到文件末尾一樣。

2.如果有指向管道寫端的文件描述符沒有關(guān)閉(管道的寫端引用計(jì)數(shù)大于0),而持有管道寫端的進(jìn)程
也沒有往管道中寫數(shù)據(jù),這個(gè)時(shí)候有進(jìn)程從管道中讀取數(shù)據(jù),那么管道中剩余的數(shù)據(jù)被讀取后,
再次read會(huì)阻塞,直到管道中有數(shù)據(jù)可以讀了才讀取數(shù)據(jù)并返回。

3.如果所有指向管道讀端的文件描述符都關(guān)閉了(管道的讀端引用計(jì)數(shù)為0),這個(gè)時(shí)候有進(jìn)程
向管道中寫數(shù)據(jù),那么該進(jìn)程會(huì)收到一個(gè)信號(hào)SIGPIPE, 通常會(huì)導(dǎo)致進(jìn)程異常終止。

4.如果有指向管道讀端的文件描述符沒有關(guān)閉(管道的讀端引用計(jì)數(shù)大于0),而持有管道讀端的進(jìn)程
也沒有從管道中讀數(shù)據(jù),這時(shí)有進(jìn)程向管道中寫數(shù)據(jù),那么在管道被寫滿的時(shí)候再次write會(huì)阻塞,
直到管道中有空位置才能再次寫入數(shù)據(jù)并返回。

總結(jié):
????讀管道:
???????? 管道中有數(shù)據(jù),read返回實(shí)際讀到的字節(jié)數(shù)。
???????? 管道中無(wú)數(shù)據(jù):
???????????????? 寫端被全部關(guān)閉,read返回0(相當(dāng)于讀到文件的末尾)
??????????????? ?寫端沒有完全關(guān)閉,read阻塞等待
????寫管道:
????????管道讀端全部被關(guān)閉,進(jìn)程異常終止(進(jìn)程收到SIGPIPE信號(hào))
????????管道讀端沒有全部關(guān)閉:
????????????????管道已滿,write阻塞
????????????????管道沒有滿,write將數(shù)據(jù)寫入,并返回實(shí)際寫入的字節(jié)數(shù)

4. 有名管道



5. mkfifo函數(shù)

#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);

參數(shù):

  • pathname: 管道名稱的路徑
  • mode: 文件的權(quán)限 和 open 的 mode 是一樣的
    是一個(gè)八進(jìn)制的數(shù)

返回值:成功返回0,失敗返回-1,并設(shè)置錯(cuò)誤號(hào)

6. 有名管道的注意事項(xiàng):

1.一個(gè)為只讀而打開一個(gè)管道的進(jìn)程會(huì)阻塞,直到另外一個(gè)進(jìn)程為只寫打開管道;
2.一個(gè)為只寫而打開一個(gè)管道的進(jìn)程會(huì)阻塞,直到另外一個(gè)進(jìn)程為只讀打開管道

讀管道:
????管道中有數(shù)據(jù),read返回實(shí)際讀到的字節(jié)數(shù)
????管道中無(wú)數(shù)據(jù):
????????管道寫端被全部關(guān)閉,read返回0,(相當(dāng)于讀到文件末尾)
????????寫端沒有全部被關(guān)閉,read阻塞等待

寫管道:
????管道讀端被全部關(guān)閉:進(jìn)行異常終止(收到一個(gè)SIGPIPE信號(hào))
????管道讀端沒有全部關(guān)閉:
????????管道已經(jīng)滿了,write會(huì)阻塞
????????管道沒有滿,write將數(shù)據(jù)寫入,并返回實(shí)際寫入的字節(jié)數(shù)。

7. 使用FIFO實(shí)現(xiàn)簡(jiǎn)單的聊天功能

chatterA.c:

#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <string.h>int main() {// 第一步,首先判斷有名管道是否存在int ret = access("fifo1", F_OK);if(ret == -1) {// 說(shuō)明管道文件不存在,則創(chuàng)建管道printf("管道不存在,創(chuàng)建管道\n");ret = mkfifo("fifo1", 0664);if(ret == -1) {// 如果創(chuàng)建管道失敗perror("mkfifo");exit(0);}}ret = access("fifo2", F_OK);if(ret == -1) {printf("管道不存在,創(chuàng)建管道\n");ret = mkfifo("fifo2", 0664);if(ret == -1) {perror("mkfifo");exit(0);}}// 第二步,以只寫的方式打開fifo1,以只讀的方式打開fifo2// fifo1管道負(fù)責(zé)chatter1寫chatter2讀// fifo2管道負(fù)責(zé)chatter1讀chatter2寫int fd1 = open("fifo1", O_WRONLY);if(fd1 == -1) {perror("open");exit(0);}printf("打開fifo1成功,等待寫入...\n");int fd2 = open("fifo2", O_RDONLY);if(fd2 == -1) {perror("open");exit(0);}printf("打開fifo2成功,等待讀取...\n");// 第三步,循環(huán)地往管道fifo1里寫入數(shù)據(jù)char buffer[1024] = {0};while(1) {// 把buffer置空以便重復(fù)寫入memset(buffer, 0, 1024); // 獲取標(biāo)準(zhǔn)輸入的數(shù)據(jù)fgets(buffer, 1024, stdin);ret = write(fd1, buffer, strlen(buffer));if(ret == -1) {perror("write");exit(0);}// 第四步,循環(huán)地從管道fifo2中讀出數(shù)據(jù)memset(buffer, 0, 1024);ret = read(fd2, buffer, 1024);if(ret <= 0) {perror("read");exit(0);}printf("chatterB傳來(lái)消息: %s\n", buffer);}close(fd1);close(fd2);return 0; }

chatterB:

#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <string.h>int main() {// 與chatterA完全對(duì)稱,往fifo2中寫,從fifo1中讀int ret = access("fifo1", F_OK);if(ret == -1) {printf("管道不存在,創(chuàng)建管道\n");ret = mkfifo("fifo1", 0664);if(ret == -1) {perror("mkfifo");exit(0);}}ret = access("fifo2", F_OK);if(ret == -1) {printf("管道不存在,創(chuàng)建管道\n");ret = mkfifo("fifo2", 0664);if(ret == -1) {perror("mkfifo");exit(0);}}int fd1 = open("fifo1", O_RDONLY);if(fd1 == -1) {perror("open");exit(0);}printf("打開fifo1成功,等待讀取...\n");int fd2 = open("fifo2", O_WRONLY);if(fd2 == -1) {perror("open");exit(0);}printf("打開fifo2成功,等待寫入...\n");char buffer[1024] = {0};while(1) {memset(buffer, 0, 1024);ret = read(fd1, buffer, 1024);if(ret <= 0) {perror("read");exit(0);}printf("chatterA傳來(lái)消息: %s\n", buffer);memset(buffer, 0, 1024); fgets(buffer, 1024, stdin);ret = write(fd2, buffer, strlen(buffer));if(ret == -1) {perror("write");exit(0);}}close(fd1);close(fd2);return 0; }

在兩個(gè)終端中分別運(yùn)行程序:
chatterA:

chatterB:

在運(yùn)行chatterA的終端中輸入:hello,im chatterA ,回車

chatterB:

輸出了chatterA中傳來(lái)的信息,同樣,在運(yùn)行chatterB的終端中輸入:hello,im chatterB ,回車

chatterA:

總結(jié)

以上是生活随笔為你收集整理的【Linux系统编程学习】匿名管道pipe与有名管道fifo的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。