1. 程式人生 > >linux (五)程序間通訊(匿名管道,命名管道,訊息佇列)

linux (五)程序間通訊(匿名管道,命名管道,訊息佇列)

程序間通訊

程序間通訊的目的
資料傳輸:一個程序需要將他的資料傳送給另一個程序
資源共享:多個程序之間共享同樣的資源
通知事件:一個程序需要向另一個或一組程序傳送訊息,通知它發生了某種事件(如程序終止時要通知父程序)
程序控制:有寫程序希望完全控制另一個程序的執行,此時控制程序希望能夠攔截另一個程序的所有陷入和異常,並能夠即使知道他們的狀態改變。

程序間通訊分類
管道:
匿名管道
命名管道
System V IPC:
System V 訊息佇列
System V 共享記憶體
System V 訊號量

首先我們來介紹下管道
我們把一個程序連線到另一個程序的一個數據流稱為管道

匿名管道

#include<unistd.h>
//功能:建立一無名管道
//原型
int pipe(int fd[2]);
//引數 fd:檔案描述符陣列,其中fd[0]表示讀端,fd[1]表示寫端
//返回值:成功返回0;失敗返回錯誤程式碼

站在檔案描述符角度-深度理解管道
這裡寫圖片描述
接下來父程序fork出子程序然後讓父程序關閉fd[0].子程序關閉fd[1]
這裡寫圖片描述
這裡寫圖片描述
我們可以看到父程序寫入的東西被子程序讀到了

管道讀寫規則
當沒有資料可讀的時候
O_NONBLOCK disable:read 呼叫阻塞,即程序暫停執行,一直等到有資料來到為止。
O_NONBLOCK enable:read呼叫返回-1,errno值為EAGAIN
當管道滿的時候


O_NONBLOCK disable: write呼叫阻塞,直到有程序讀走資料
O_NONBLOCK enable:呼叫返回-1,errno值為EAGAIN
如果所有管道寫端對應的檔案描述符被關閉,則read返回0
如果所有管道讀端對應的檔案描述符被關閉,則write操作會產生訊號SIGPIPE,進而可能導致write程序退出
當要寫入的資料量不大於PIPE_BUF時,Linux將保證寫入的原子性。
當要寫入的資料量大於PIPE_BUF是,Linux將不再保證寫入的原子性。
管道特點
只能用於具有共同祖先的程序(具有親緣關係的程序)之間進行通訊;通常,一個管道由於程序建立,然後該程序呼叫fork,此後父子程序之間就可應用該管道。
一般而言,程序退出,管道釋放,所以管道的生命週期隨程序
一般而言,核心會對管道操作進行同步與互斥
管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道
**

命名管道

**
管道應用的一個限制就是隻能在具有共同祖先(具有親緣關係)的程序間通訊。
如果我們想在不想管的程序之間交換資料,就可以使用FIFO檔案來做這項工作,它經常被稱為命名管道
命名管道是一種特殊型別的檔案

建立一個命名管道
命名管道可以從命令列上建立,命令列方法是使用下面這個命令

mkfifo filename

命名管道也可以從程式裡建立,相關函式有:

int mkfifo(const char *filename,mode_t mode);

建立命名管道:

int main(int argc,char *argv[])
{
    mkfifo("p2",0644);
    return 0;
}

匿名管道和命名管道的區別
匿名管道由pipe函式建立並開啟。
命名管道由mkfifo函式建立,開啟用open
FIFO與pipe之間唯一的區別就是他們的建立與開啟的港式不同,一旦這些工作完成之後,他們具有相同的語義。
命名管道的開啟規則
如果當前開啟操作是為讀而開啟FIFO時
O_NONBLOCK disable:阻塞直到有相應程式為寫而開啟該FIFO
O_NONBLOCK enable:立刻返回成功
如果當前開啟操作是為寫而開啟FIFO時
O_NONBLOCK disable:阻塞直到有相應的程序為讀而開啟該FIFO
O_NONBLOCK enable:立刻返回失敗,錯誤碼是ENXIO

用命名管道實現server和client通訊

clientpipe.c
clientpipe.c

serverpipe.c
這裡寫圖片描述

這裡寫圖片描述
這裡寫圖片描述

訊息佇列

訊息佇列提供了一個從一個程序向另一個程序傳送一塊資料的方法
每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值
訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有限的MSGMAX,每個訊息佇列的總的位元組數是有上限的MSGMNB,系統上訊息佇列的總數也是有上限的MSGMNI

訊息佇列的函式
msgget函式
功能:用來建立和訪問一個訊息佇列
原型:
int msgget(key_t key,int msgglg);
引數:
key: 某個訊息佇列的名字
msgflg:由九個許可權標誌構成,他們的用法和建立檔案時使用的mode模式標誌是一樣的
返回值:成功返回一個非負整數,即該訊息佇列的標識碼;失敗返回-1;
msgctl函式
功能:訊息佇列的控制函式
原型
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
引數
msqid:由msgget函式返回的訊息佇列標識碼
cmd:是將要採取的動作,
返回值:成功返回0;失敗返回-1

msgsnd函式
功能:把一條訊息新增到訊息佇列中
原型:
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
引數
msqid:由msgget函式返回的訊息佇列標識碼
msgp:是一個指標,指標指向準備傳送的訊息
msgsz:是msgp指向的訊息長度,這個長度不含儲存資訊型別的那個long int長整型
msgflg:控制著當前訊息佇列滿或者達到系統上限時將要發生的事情
msgflg = IPC_NOWAIT標識佇列滿不等待,返回EAGAIN錯誤
返回值:成功返回0;失敗返回-1;
說明:
1:訊息結構在兩方面受到制約
首先,它必須小於系統規定的上限值;
其次,它必須以一個long int長整數開始,接收者函式將利用這個長整數確定訊息佇列的型別
2:訊息結構參考形式如下:
struct msgbuf{
long mtype;
char mtext[1];
}
msgrcv函式
功能:是從一個訊息佇列接收訊息
原型:
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
引數
msqid:由msgget函式返回的訊息佇列標識碼
msgp:是一個指標,指標指向準備接受的訊息
msgsz:是msgp指向的訊息長度,這個長度不含儲存訊息型別的那個long int長整型
msgtype:它可以實現接收優先順序的簡單形式
msgflg:控制著佇列中沒有相應型別的訊息可供接收時將要發生的事

下面我們通過訊息佇列來實現通訊
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述這裡寫圖片描述
這裡寫圖片描述

好,我們來看他們的效果
這裡寫圖片描述
這裡寫圖片描述

我們來看看建立的訊息佇列
這裡寫圖片描述
我們當時建立訊息佇列的時候用的是IPC_CREAT | IPC_EXCL 這個ipc 操作的時候是如果不存在這個訊息佇列,我們就建立一個,如果存在的話,就會返回-1,因為已經有一個訊息佇列存在,如果我們再使用./server的時候,就會報錯
這裡寫圖片描述
這時,我們用 ipcs -q 加上msqid 刪除之後 就可以繼續運行了