Linux的通信方式主要有分類有下面幾種:
-匿名管道和FIFO有名管道
-消息隊列,信號量和共享存儲
-套接字
.對於套接字的進程通信,我就留在套接字的文章中再寫了.
一.管道
管道是最古老的進程通信機制了.提供進程間的單向通信.
1.創建管道
int pipe(int fdes[2]);
實際上管道通過參數返回讀和寫的兩個文件描述符.相當於是打開了兩個文件吧.但是這個文件是特殊的pipe文件.fdes[0]表示的是輸入,fdes[2]表示的是輸出.注意,這個函數只創建一個文件,而不是創建兩個文件一個用來讀,一個用來寫.
管道創建調用成功,那麼就返回0,否則返回-1,並設置errno錯誤條件.
如果向寫文件描述符進行讀,那麼會生成一個SIGPIPE信號,並且當信號被阻塞是將以EPIPE錯誤失敗.管道文件不允許文件定位,讀和寫操作都是順序的,讀從文件的開始處讀,寫則寫至文件尾.
這麼看似乎好像沒有和別的進程關聯起來.實際上,管道單獨使用的話只是創建了兩個文件而已,想要使用管道還得使用fork,exec,dup等函數來創建一個進程才能使用.
簡答來說,對於創建的進程來說,其讀寫的文件是相反的.並且對於管道來說,其方向只能是單向的,所以如果你創建了新進程,兩個進程至少要關閉一個文件描述符.
2.popen和pclose函數
這兩個函數是C標准庫中的兩個函數.並且創建的方式也比系統調用方便.
FILE *popen(const char *command,const char *mode);
int pclose(FILE * stream);
第一個函數通過第一個參數,解析其命令建立一個新進程.過程就是先建立一個管道,然後派生一個子進程,調用exec啟動一個shell程序執行command給出的命令.並立即返回一個管道對應的流指針.
mode的方式只能是”r”和”w”兩種方式之一,但是不能兩個一起,所以這和一般的文件模式是不同的.
這麼來看,這個函數就比用上面的pipe系統調用方便得多.封裝了包括fork,exec,dup等一系列調用~當然,這個函數相對自己調用pipe+fork+exec+close來說效率就要差一些了.
管道IO的原子性
實際上管道就是一片內存緩沖區.(我記得原來學的時候創建一個管道,然後在圖形界面下把管道文件給刪了,但是管道仍然正常使用).所以如果寫入管道的數據塊大小超過這片緩沖區的大小時,內核就要分幾次寫入了.如果是分批寫的話,那麼就很可能造成混亂了.試想如果A進程需要分3次寫,在寫完第二次的時候時間片到了,切換到另外一個進程也要往這個管道寫內容,那麼就可能導致內容產生混亂.(可以fork出多個子進程向同一個管道進行寫操作給父進程數據).如果是一對一的關系就無所謂了,但是對於一對多就需要注意了.
管道緩沖區的大小由定義在頭文件<limits.h>中的宏PIPE_BUF給出.在Linux系統中其值為4096.並且一旦寫入PIPE_BUF個字節至管道後,進一步寫管道的操作將阻塞直到有些字節已讀出.
Linux內核態與用戶態進程通信方法-用戶上下文 http://www.linuxidc.com/Linux/2014-02/96883.htm
簡單解析Linux下進程通信方法 http://www.linuxidc.com/Linux/2013-06/86341.htm
Linux的進程通信(IPC) http://www.linuxidc.com/Linux/2012-10/71887.htm
Linux 進程通信(System V) http://www.linuxidc.com/Linux/2012-03/57248.htm
二.FIFO特別文件
由於管道只能用於存在父子關系的進程之間的通信,為了滿足非父子進程關系下的通信.可以用FIFO特別文件.
FIFO特別文件類似於管道,也是半雙工方式,並且數據也是按先進先出順序傳送,因此的名FIFO.但是創建的方法與管道不同,管道式匿名通信通道,而FIFO特別文件通過調用mknod()被等級到文件系統,因此FIFO有時也成為有名管道.
與管道的主要不同有:
--FIFO作為特別文件存在於文件系統中
--不同祖先的進程可以通過FIFO特別文件共享數據
--當共享進程完成了所有IO操作後,除非unlink刪除它,否則FIFO特別文件將存在文件系統中,並且可以留待下一次使用.
創建了FIFO文件之後,對於任何進程,只要知道了其名字並且有適當的訪問權限,就能按照正常文件相同的方法打開它讀或者寫.
在某個進程讀之前,必須有進程表示以寫的方式打開這個文件,否則讀進程則會一直阻塞.
同理,在寫之前也一定要有進程打開了讀,否則一樣阻塞.
1.創建FIFO文件
int mkfifo(const char *path,mode_t mode);
int mknod(const char *path,mode_t mode,dev_t dev);
mkfifo創建一個FIFO特別文件,用參數path來指定其路徑和文件名.mode參數則用來設置文件的權限.取值和open函數一樣.
mknod創建一個給定文件類型的新文件,path同mkfifo.文件類型由mode參數給出,可選的值如下:
S_IFIFO 這個就是特別文件
S_IFCHR 字符特別文件(不可移植)
S_IFDIR 目錄文件(不可移植)
S_IFBLK 塊特別文件(不可移植)
S_IFREG 普通文件(不可移植)
除開這些個參數,mode還需要另外附加上文件的訪問方式,用獲得方式組合宏定義.mknod似乎是比mkdir之類函數更底層的函數.
並且這個mode還受umask的修正,對於mkfifo則不受其影響.
由於剛打開時可能造成阻塞(讀方式打開沒有寫進程,寫操作打開沒有讀進程),並且打開文件的時候可以用O_NONBLOCK標志.第一次操作這個文件,並且打開方式使用了O_NONBLOCK的時候,open函數會返回-1,並且設置errno為錯誤值.
2.讀寫FIFO文件
和管道不同,由於打開FIFO文件可以使用O_NONBLOCK標志.所以要復雜一些.
沒有O_NONBLOCK的時候讀,如果沒有可以讀的,那麼就會阻塞.有O_NONBLOCK的話,那麼就直接返回-1,並且設置errno為EAGAIN了.
正常情況下如果文件寫滿了再調用write,那麼會一直阻塞,知道信息被讀走.完成操作時,返回寫入的字節數.當用了O_NONBLOCK打開的時候,那麼不能寫則會返回-1,表示沒有寫進任何數據.errno也設置為EAGAIN.
原子性和管道一致.對於多個寫一個讀的時候,最好保證write的進程是不沒有使用O_NONBLOCK的.並且每次寫的時候,寫入的數據小於PIPE_BUF的大小.保證寫入數據的完整性.
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-06/103322p2.htm