一 Linux進程間通信( 三 )

注意:O_NONBLOCK是非阻塞的標志位,指定管道對我們的操作要么成功,要么立刻返回錯誤,不被阻塞 。
管道特點(了解)

  • 只能用于具有共同祖先的進程(具有親緣關系的進程)之間進行通信;通常,一個管道由一個進程創建,然后該進程調用fork , 此后父、子進程之間就可應用該管道 。
  • 管道提供流式服務 。也就是你想往管道里讀寫多少數據是根據自身來定的
  • 一般而言 , 進程退出 , 管道釋放,所以管道的生命周期隨進程
  • 一般而言,內核會對管道操作進行同步與互斥
  • 管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道
  • 半雙工是指傳輸過程中同時只能向一個方向傳輸,一方的數據傳輸結束之后 , 另外一方再回應 。雙方傳輸數據是不可以同時進行的
  • 全雙工是指兩方能同時發送和接受數據 。在這種情況下就沒有擁堵的危險 , 數據的傳輸也就更快
命名管道概念:無名管道,由于沒有名字,所以只能用于親緣關系的進程通信 。為了克服這個缺點,提出了命名管道(FIFO) 。
命名管道不同于無名管道之處在于它提供了一個路徑名與之關聯 , 以FIFO的文件形式存在于文件系統中,這樣,即使與FIFO的創建進程不存在親緣關系的進程 , 只要可以訪問該路徑 , 就能夠彼此通過FIFO相互通信,因此,通過FIFO不相關的進程也能交換數據 。
  • FIFO在文件系統(磁盤上)中作為一個特殊文件而存在 , 但是FIFO中的內容卻存放在內存中 。
  • 當使用FIFO的進程退出后,FIFO文件將繼續保存在文件系統中以便以后使用 。
  • FIFO有名字,不相關的進程可以通過打開命名通道進行通信 。
創建命名管道1.通過命令創建命名管道
mkfifo filename
2.通過函數創建命名管道
int mkfifo(const char *pathname, mode_t mode);
功能:創建命名管道
參數:pathname:普通的路徑名,也就是創建后FIFO的名字 。
?mode:文件的權限,與打開普通文件的open函數中的mode參數類似 。
返回值:成功:0 (狀態碼)失?。喝绻募呀洿嬖?,則會出錯返回-1
代碼示例:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#define FIFO "./fifo"int main(){umask(0);// 創建管道int ret = mkfifo(FIFO, 0666);if (ret == -1){perror("make fifo");exit(-1);}}運行結果如下:
一 Linux進程間通信

文章插圖
上面說過 , 管道其實就是一種特殊的文件,管道文件大小是0,因為上面介紹過 , 管道文件的內容都存放在內存當中 。
命名管道讀寫操作以及注意事項一旦創建了一個FIFO,就可以用open打開它,常見的文件I/O都可以作用于FIFO文件 。
FIFO嚴格的遵循先進先出的原則 , 對管道以及FIFO的讀總是從開始處返回數據,對它們的寫則是把數據添加到末尾 。
  • 一個為只讀而打開一個管道的進程會阻塞直到另外一個進程為只寫打開該管道
  • 一個為只寫而打開一個管道的進程會阻塞直到另外一個進程為只讀打開該管道
讀寫規則:
讀管道
  • 管道中有數據,read返回返回實際讀到的字節數
  • 管道中無數據:(1)若管道寫端被全部關閉 , read返回0
?(2)若寫端沒有全部關閉,read阻塞等待
寫管道
  • 管道讀端全部被關閉,進程異常終止
  • 管道讀端沒有全部關閉:(1)若管道已經滿了 。write阻塞
?(2)若管道沒滿 , write將數據寫入,并返回實際寫入的字節數
使用命名管道進行通信接下來我會使用命名管道實現簡單的版本聊天 。
一 Linux進程間通信

文章插圖
talkA.c
#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<stdlib.h>#include<fcntl.h>//先讀后寫//以只讀的方式打開管道1//以只寫的方式打開管道2define SIZE 1024int main() {int fdr = -1;int fdw = -1;int ret = -1;char buf[SIZE];//以只讀的方式打開管道1fdr = open("fifo1",O_RDONLY);if(-1==fdr){perror("open");return 1;}printf("以只讀的方式打開管道1....\n");//以只寫的方式打開管道2fdw = open("fifo2",O_WRONLY);if(-1==fdw){perror("open");return 1;}printf("以只寫的方式打開管道2....\n");//循環讀寫while(1){//讀管道1memset(buf,0,SIZE);ret = read(fdr,buf,SIZE);if(ret<=0){perror("read");break;}printf("read:%s\n",buf);//寫管道2memset(buf,0,SIZE);fgets(buf,SIZE,stdin);//去掉最后一個換行符if('\n'==buf[strlen(buf)-1])buf[strlen(buf)-1]=0;//寫管道ret = write(fdw,buf,strlen(buf));if(ret<=0){perror("write");break;}printf("write ret:%d\n",ret);}//關閉文件描述符close(fdr);close(fdw);}

推薦閱讀