文章目錄
- 1.行程間通信
- 1.1 行程間通訊概述
- 1.2 行程間通信目的
- 1.3 行程間通信分類
- 2. 管道
- 2.1 概述
- 2.2 匿名管道
- 2.2.1 概述
- 2.2.2 創建匿名管道
- 2.2.3 基本程序
- 2.2.4 內核角度了解實質
- 2.2.5代碼實作原理
- 2.2.6代碼實作
- 2.2.7 常見讀寫行為
- 2.2.8 管道特點
- 2.3 命名管道(FIFO)
- 2.3.1 概述
- 2.3.2 創建命名管道
- 2.3.3代碼實作
- 2.3.4 運行總結
- 2.3.5 命令列創建命名管道
- 2.3.6 命令列命名管道通信
- 2.3.7 運行總結
- 2.4 匿名/命名管道的區別
- 行程間通信還有共享記憶體方式等,將在后序博客詳解,
1.行程間通信
1.1 行程間通訊概述
- 行程是一個獨立的資源分配單元,不同行程之間的資源是獨立的,沒有關聯,不能在一個行程中直接訪問另一個行程的資源,行程不是孤立的,不同的行程需要進行資訊的互動和狀態的傳遞等,因此需要行程間通信,
- 每個行程各自有不同的
行程地址空間,所有行程之間要交換資料必須通過內核,在內核中開辟一塊緩沖區,行程1把資料從用戶空間拷到內核緩沖區,行程2再從內核緩沖區讀走資料,內核提供的這種機制就是行程間通信(IPC),管道是最基本的IPC機制,
1.2 行程間通信目的
資料傳輸:一個行程需要將它的資料發送給另一個行程資源共享:多個行程之間共享同樣的資源,通知事件:一個行程需要向另一個或一組行程發送訊息,通知它(它們)發生了某種事件(如行程終止時要通知父行程),行程控制:有些行程希望完全控制另一個行程的執行(如Debug行程),此時控制行程希望能夠攔截另一個行程的所有陷入和例外,并能夠及時知道它的狀態改變
1.3 行程間通信分類
管道
- 匿名管道
- 命名管道
System V IPC
- System V 訊息佇列
- System V 共享記憶體
- System V 信號量
POSIX IPC
- 訊息佇列
- 共享記憶體
- 信號量
- 互斥量
- 條件變數
- 讀寫鎖
2. 管道
2.1 概述
- 所謂“管道”,是指用于連接一個讀行程和一個寫行程以實作它們之間通信的一個共享檔案,就像現實中的水管,水就像資料,(連接行程,相當于在行程間連接一個通路,用來傳遞資訊)
- Linux下?切皆?件”,所以我們可以把管道看做是檔案,是服務于管道通信的特殊檔案,而管道通信是一種通信方式,
管道通信原理圖:

2.2 匿名管道
2.2.1 概述
- 僅僅適用于
具有親緣關系(如父子行程)的行程間通信,因為匿名管道無法被其他行程找到,也就無法通信,所以只能通過子行程復制父行程的方法,讓子行程能夠訪問到相同的管道來實作通信,
2.2.2 創建匿名管道
#include <unistd.h>
int pipe(int fd[2])
引數
fd:檔案描述符
- fd[0]:表示讀端
- fd[1]:表示寫端
回傳值
成功回傳0,失敗回傳錯誤代碼,
2.2.3 基本程序

2.2.4 內核角度了解實質
- 在linux中,管道的實作并沒有專門的資料結構,而是借助了檔案系統的file結構和VFS的索引節點inode,通過將兩個file結構指向同一個臨時的VFS索引節點,而這個索引節點又指向一個物理頁面而實作,如下圖所示:

2.2.5代碼實作原理
- 父行程呼叫
pipe函式創建管道,得到兩個檔案描述符fd[0]、fd[1]指向管道的讀端和寫端, - 父行程呼叫fork創建子行程,那么子行程也有兩個檔案描述符指向同一管道,
父行程關閉管道寫端,子行程關閉管道讀端,父行程可以從管道中讀資料,子行程將從管道中寫入資料,由于管道是利用環形佇列實作的,資料從寫端流入管道,從讀端流出,這樣就實作了行程間通信,
2.2.6代碼實作
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define SIZE 128
int main()
{
int pipefd[2]={0};
pipe(pipefd);
pid_t id=fork();
if(id==0)
{
close(pipefd[0]);
const char* msg="Hello World!\n";
while(1)
{
write(pipefd[1],msg,strlen(msg));
sleep(1);
}
}
else
{
close(pipefd[1]);
char buff[SIZE];
while(1)
{
ssize_t s=read(pipefd[0],buff,sizeof(buff)-1);
if(s>0)
{
buff[s]=0;
printf("fathrt get message:%s\n",buff);
sleep(1);
}
}
}
return 0;
}
運行結果:

2.2.7 常見讀寫行為
- 使用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設定O_NONBLOCK標志):
-
如果所有指向管道寫端的檔案描述符都關閉了(管道寫端參考計數為0),而仍然有行程從管道的讀端讀資料,那么
管道中剩余的資料都被讀取后,再次read會回傳0,就像讀到檔案末尾一樣, -
如果有指向管道寫端的檔案描述符沒關閉(管道寫端參考計數大于0),而持有管道寫端的行程也沒有向管道中寫資料,這時有行程從管道讀端讀資料,那么
管道中剩余的資料都被讀取后,再次read會阻塞,直到管道中有資料可讀了才讀取資料并回傳, -
如果
所有指向管道讀端的檔案描述符都關閉了(管道讀端參考計數為0),這時有行程向管道的寫端write,那么該行程會收到信號SIGPIPE,通常會導致行程例外終止,當然也可以對SIGPIPE信號實施捕捉,不終止行程,具體方法信號章節詳細介紹, -
如果有指向管道讀端的檔案描述符沒關閉(管道讀端參考計數大于0),而持有管道讀端的行程也沒有從管道中讀資料,這時有行程向管道寫端寫資料,那么在
管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入資料并回傳,
2.2.8 管道特點
- 只能用于
具有共同祖先的行程(具有親緣關系的行程)之間進行通信;通常,一個管道由一個行程創建,然后該行程呼叫fork,此后父、子行程之間就可應用該管道, - 管道提供
流式服務 - 一般而言,
行程退出,管道釋放,所以管道的生命周期隨行程 - 一般而言,內核會對管道操作進行
同步與互斥 - 管道是
半雙工的,資料只能向一個方向流動;需要雙方通信時,需要建立起兩個管道
2.3 命名管道(FIFO)
2.3.1 概述
- 命名管道在某種程度上可以看做是匿名管道 ,但他
打破了匿名管道只能在有血緣關系的行程間的通信,命名管道之所以可以實作行程間通信在于通過同一個路徑名而看到同一份資源,這份資源以FIFO的檔案形式存在于檔案系統中, - 它作為特殊的設備檔案存在于檔案系統中,因此,在行程中可以使用
open()和close()函式打開和關閉命名管道,
2.3.2 創建命名管道
程式里創建,相關函式:
#include <sys/tyoes.h>
#include <sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);
引數:
- pathname是一個檔案的路徑名,是創建的一個命名管道的檔案名;
- 引數mode是指檔案的權限,檔案權限取決于(mode&~umask)的值,
回傳值:
- 回傳值:若成功則回傳0,否則回傳-1
2.3.3代碼實作
//server.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define FIFO_FILE "./fifo"
int main()
{
umask(0);
if(mkfifo(FIFO_FILE ,0666)== -1)
{
perror("mkfifo error!\n");
return 1;
}
int fd=open(FIFO_FILE,O_RDONLY);
if(fd >=0)
{
char buf[64];
while(1)
{
ssize_t s = read(fd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]=0;
printf("client message is:%s",buf);
}
else if(s==0)
{
perror("client quit!\n");
break;
}
else
{
perror("read error!");
break;
}
}
}
return 0;
}
// client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#define FIFO_FILE "./fifo"
int main()
{
int fd=open(FIFO_FILE,O_WRONLY);
if(fd >=0)
{
char buf[64];
while(1)
{
printf("Please enter message:");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]=0;
write(fd,buf,s);
}
}
}
return 0;
}
運行結果:

2.3.4 運行總結
- 有運行結果可知實作了
client 和 server 之間的通信,在client上輸入的內容即在server上顯示, - 且由圖可得
client和server之間是兩個沒有任何關系的行程,他們的pid 和 ppid都不相同, - 正是因為是兩個不同的行程,所以才
打破了匿名管道只有在具有親緣關系間通信的束縛,
2.3.5 命令列創建命名管道
命名管道可以從命令列上創建:
$ mkfifo filename
2.3.6 命令列命名管道通信

2.3.7 運行總結
mkfifo 不僅是一個函式,還是一條命令,不僅可以在函式內創建管道,還可以在命令列上創建命名管道,實作兩個命令列程之間的通信,
2.4 匿名/命名管道的區別
- 匿名管道由
pipe函式創建并打開, - 命名管道由
mkfifo函式創建,打開用open, - FIFO(命名管道)與pipe(匿名管道)之間唯一的區別在它們創建與打開的方式不同,一但這些作業完成之后,它們具有相同的語意,
行程間通信還有共享記憶體方式等,將在后序博客詳解,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/273313.html
標籤:其他
上一篇:Web服務器群集——HAProxy+Keepalived 負載均衡高可用配置步驟
下一篇:海螢物聯網介紹
