三、管道
管道是一種半雙工通信,即允許信号在兩個方向上傳輸,但某一時刻隻允許信号在一個信道上單向傳輸。管道是把一個程式的輸出直接連接配接到另一個程式的輸入,常說的管道多是指無名管道,無名管道隻能用于父子的程序之間,這是它與有名管道的最大差別。
(1)、管道有固定大小:實際上,管道是一個固定大小的緩沖區
(2)、管道讀取程序比檔案讀取程序快:當所有目前程序資料已被讀取時,管道變空。當這種情況發生時,管道一個随後的read()調用将預設地被阻塞,等待某些資料被寫入,而檔案read()調用傳回檔案結束。
注:從管道讀資料是一次性操作,資料一旦被讀,它就從管道中被抛棄,釋放空間以便寫更多的資料。
(3)、管道檔案存儲在磁盤,普通檔案存儲在記憶體裡面。
1、有名管道:
在檔案系統中存在一個檔案辨別(檔案名),但是管道檔案不占據磁盤的空間,需要傳遞的資料緩存在記憶體區域。
具體操作有:
建立:mkfifo 檔案名
打開:int open(char &path,int flag,);
在僅僅一端打開時,open會阻塞。直到有讀端、寫端,open才會傳回
讀:int read(int fd,void *buf,size_t size);
read阻塞有兩種情況:一種是管道中沒有資料,另一種是管道中有資料,但所有的寫端關閉
寫:int write(int fd,void *buf,size_t size);
wirte阻塞有兩種情況:一種是管道緩存區滿,另一種是管道中有空間,但所有的讀端關閉
關閉:int close(int fd);
注:讀和寫的次數沒有必然關系
2、無名管道
沒有名稱的管道(沒有管道檔案存在)。fork 之後,父子程序對于檔案描述符共享
限制:隻能應用于父子程序之間完成程序間通訊,并且管道的建立與打開必須在fork 之前完成。
具體操作:
建立和打開:int pipe(int fds[2]); pipe 函數建立一個無名管道,fds[0]指向讀端,fds[1]指向寫端。
因為管道是半雙工通訊,是以在fork之後,父子程序分别關閉一對讀寫;
讀資料:read(int fds,void *buff,size_t size); read傳回值為0,則讀端通訊完成
寫資料:write(int fds,void* buff,size_t size); 若子程序不關閉寫端,當父程序結束,read傳回 ??
關閉檔案:close(fds);
四、共享記憶體:是最快的一種 IPC
共享記憶體是最快IPC原因:
(1)、在通訊之前通路核心對象,需要使用者态切換核心态,但真正發送資料時,直接通過指針操作空間,不需要使用者态切換核心态
(2)、共享記憶體可以直接通過指針将資料寫到共享記憶體區域,接收方直接通過指針操作共享記憶體區域的資料,相比于管道、消息隊列,共享記憶體少了兩次資料的拷貝
注:兩個程序的虛拟位址空間相等或者不相等,對于共享記憶體而言,無影響。
虛拟位址需要通過程序頁表,進行頁面映射才能找到實體内記憶體空間
注:本篇部落格内容部分取自https://blog.csdn.net/century_sunshine/article/details/79882445
共享記憶體共享的是實體記憶體空間
具體操作:
1.shmget函數:建立共享記憶體
int shmget(key_t key,size_t size,int shmflg);
失敗傳回-1 成功傳回辨別符shmid
2.shmat函數:第一次建立共享記憶體時,它不被任何通路,要想啟動對該共享記憶體的通路,必須将其連接配接到一個程序的位址空間中,這項工作由shmat函數完成。
void *shmat(int shmid,const void * shm_addr,int shmflg);
失敗傳回-1 成功傳回指向共享記憶體第一個位元組的指針
3.将共享記憶體從目前程序中分離
int shmdt(void *shmatrt)
失敗傳回-1,成功為0。
4.删除核心對象、且立即删除
int shmctl(int shm_id,int command,struct shmid_ds*buf);
5、優點:我們可以看到使用共享記憶體進行程序間的通信真的是非常友善,而且函數的接口也簡單,資料的共享還使程序間的資料不用傳送,而是直接通路記憶體,也加快了程式的效率。同時,它也不像匿名管道那樣要求通信的程序有一定的父子關系。
6、缺點:共享記憶體沒有提供同步的機制,這使得我們在使用共享記憶體進行程序間通信時,往往要借助其他的手段來進行程序間的同步工作。