linux進程間通訊----簡介(1)linux
一、進程間通訊概述shell
進程間通訊有以下一些目的:數組
數據傳輸:一個進程須要將它的數莘⑺透 硪桓黿 蹋 ⑺偷氖 萘吭諞桓鱟紙詰郊剛鬃紙諡 洹?br> 共享數據:多個進程想要操做共享數據,一個進程對共享數據的修改,別的進程應該馬上看到。socket
通知事件:一個進程須要向另外一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。函數
資源共享:多個進程之間共享一樣的資源。爲了做到這一點,須要內核提供鎖和同步機制。線程
進程控制:有些進程但願徹底控制另外一個進程的執行(如Debug進程),此時控制進程但願可以攔截另外一個進程的全部陷入和異常,並可以及時知道它的狀態改變。設計
linux進程間通訊(IPC)由如下幾部分發展而來:3d
早期UNIX進程間通訊、基於System V進程間通訊、基於Socket進程間通訊和POSIX進程間通訊。blog
UNIX進程間通訊方式包括:管道、FIFO、信號。繼承
System V進程間通訊方式包括:System V消息隊列、System V信號燈、System V共享內存。
POSIX進程間通訊包括:posix消息隊列、posix信號燈、posix共享內存。
如今linux使用的進程間通訊方式:
(1)管道(pipe)和有名管道(named pipe):管道可用於具備親緣關係進程間的通訊,有名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊;
(2)信號(signal):信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進
程還能夠發送信號給進程自己;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數
sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又可以統一對外接口,用sigaction函數從新實現了signal
函數);
(3┫ ⒍恿校∕essage):消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺點。
(4)共享內存:使得多個進程能夠訪問同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。每每與其它通訊機制,如信號量結合使用,來達到進程間的同步及互斥。
(5)信號量(semaphore):主要做爲進程間以及同一進程不一樣線程之間的同步手段。
(6)套接字(socket):更爲通常的進程間通訊機制,可用於不一樣機器之間的進程間通訊。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
二、管道通訊
普通的Linux shell都容許重定向,而重定向使用的就是管道。例如:
ps | grep vsftpd
管道是單向的、先進先出的、無結構的、固定大小的字節流,它把一個進程的標準輸出和另外一個進程的標準輸入鏈接在一塊兒。寫進程在管道的尾端寫入數據,讀進程
在管道的首端讀出數據。數據讀出後將從管道中移走,其它讀進程都不能再讀到這些數據。管道提供了簡單的流控制機制。進程試圖讀空管道時,在有數據寫入管道
前,進程將一直阻塞。一樣,管道已經滿時,進程再試圖寫管道,在其它進程從管道中移走數據以前,寫進程將一直阻塞。
管道主要用於不一樣進程間通訊。
建立一個簡單的管道,可使用系統調用pipe( )。它接受一個參數,也就是一個包括兩個整數的數組。若是系統調用成功,此數組將包括管道使用的兩個文件描述符。建立桓齬艿樂 螅 話闈榭魷陸 探 桓魴碌慕 獺?br> 系統調用:pipe( );
原型:int pipe( int fd[2] );
返回值:若是系統調用成功,返回0。若是系統調用失敗返回-1:
errno = EMFILE (沒有空閒的文件描述符)
EMFILE (系統文件表已滿)
EFAULT (fd數組無效)
#include
#include
#include
#include
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd)
2.2 管道讀寫
管道主要用於不一樣進程間通訊。實際上,一般先建立一個管道,再經過fork函數建立一個子進程。
子進程讀和父進程寫的命名管道:
2.3 管道讀寫注意事項
能夠經過打開兩個管道來建立一個雙向的管道。但須要在子進程中正確地設置文件描述符。
必須在系統調用fork( )中調用pipe( ),不然子進程將不會繼承文件描述符。
當使用半雙工管道時,任何關聯的進程都必須共享一個相關的祖先進程。由於管道存在於系統內核之中,因此任何不在建立管道的進程的祖先進程之中的進程都將沒法尋址它。而在命名管道中卻不是這樣。
管道實例見:pipe_rw.c
2.4 標準流管道
與linux中文件操做有文件流的標準I/O同樣,管道的操做也支持基於文件流的模式。接口函數以下
庫函數:popen();
原型: FILE *popen ( char *command, char *type);
返回值:若是成功,返回一個新的文件流。若是沒法建立進程或者管道,返回NULL。
管道中數據流的方向是由第二個參數type控制的。此參數能夠是r或者w,分別表明讀或寫。但不能同時爲讀和寫。在Linux系統下,管道將會以參數type中第一個字符表明的方式打開。因此,若是你在參數type中寫入rw,管道將會以讀的方式打開。
使用popen()建立的管道必須使用pclose( )關閉。其實,popen/pclose和標準文件輸入/輸出流中的fopen() / fclose()十分類似。
庫函數: pclose();
原型: int pclose( FILE *stream );
返回值: 返回系統調用wait4( )的狀態。
若是stream無效,或者系統調用wait4( )失敗,則返回 -1。
注意此庫函數等待管道進程運行結束,而後關閉文件流。
庫函數pclose( )在使用popen( )建立的進程上執行wait4( )函數。當它返回時,它將破壞管道和文件系統。
#include
#include
#include
#include
#define BUFSIZE 1024
int main()
{
FILE *fp;
char *cmd = "ps -ef";
char buf[BUFSIZE];
buf[BUFSIZE] = '\0';
if((fp=popen(cmd,"r"))==NULL)
perror("popen");
while((fgets(buf,BUFSIZE,fp))!=NULL)
printf("%s",buf);
pclose(fp);
exit(0);
}
2.5 命名管道(FIFO)
2.5.1 基本概念
命名管道和通常的管道基本相同,但也有一些顯著的不一樣:
命名管道是在文件系統中做爲一個特殊的設備文件而存在的。
不一樣祖先的進程之間能夠經過管道共享數據。
當共享管道的進程執行完全部的I / O操做之後,命名管道將繼續保存在文件系統中以便之後使用。
管道只能由相關進程使用,它們共同的祖先進程建立了管道。可是,經過FIFO,不相關的進程也能交換數據。
名管道建立
#include
#include
int mkfifo(const char * pathname,
mode_t mode) ;
返回:若成功則爲0,若出錯則爲- 1
一旦已經用mkfifo建立了一個FIFO,就可用open打開它。確實,通常的文件I / O函數(close、read、write、unlink等)均可用於FIFO。
當打開一個FIFO時,非阻塞標誌(O_NONBLOCK)產生下列影響:
(1) 在通常狀況中(沒有說明O_NONBLOCK),只讀打開要阻塞到某個其餘進程爲寫打開此FIFO。相似,爲寫而打開一個FIFO要阻塞到某個其餘進程爲讀而打開它。
(2) 若是指定了O_NONBLOCK,則只讀打開當即返回。可是,若是沒有進程已經爲讀而打開一個FIFO,那麼只寫打開將出錯返回,其errno是ENXIO。
相似於管道,若寫一個尚無進程爲讀而打開的FIFO,則產生信號SIGPIPE。若某個FIFO的最後一個寫進程關閉了該FIFO,則將爲該FIFO的讀進程產生一個文件結束標誌。
FIFO相關出錯信息:
EACCES (無存取權限)
EEXIST (指定文件不存在)
ENAMETOOLONG (路徑名太長)
ENOENT (包含的目錄不存在)
ENOSPC (文件系統剩餘空間不足)
ENOTDIR (文件路徑無效)
EROFS (指定的文件存在於只讀文件系統中)
實例見:fifo_write.c 、 fifo_read.c