01?概述
1.1 發(fā)展
??? Linux進程間通信(IPC)大致發(fā)展如下:
????早期UNIX進程間通信偿枕、基于System V進程間通信年局、基于Socket進程間通信和Posix進程間通信。
??? UNIX進程間通信方式包括:管道席楚、FIFO凭需、信號染乌。
??? System V進程間通信方式包括:System V消息隊列瘫辩、System V信號量伏嗜、System V共享內(nèi)存。
??? Posix進程間通信包括:Posix消息隊列伐厌、Posix信號量、Posix共享內(nèi)存弧械。
1.2 目的
????進程間通信(IPC)的目的:
??? 1空民、數(shù)據(jù)傳輸:一個進程向另一個進程傳輸數(shù)據(jù)羞迷。
? ? 2、共享數(shù)據(jù):多個進程想要操作共享數(shù)據(jù)画饥,一個進程對共享數(shù)據(jù)的修改,其他進程應(yīng)實時看到抖甘。
??? 3、通知事件:一個進程需要向另一個進程發(fā)送消息衔彻,通知它發(fā)生了某種事件(如進程終止時需要通知父進程)薇宠。
??? 4、資源共享:多個進程之間共享同樣的資源艰额。為了做到這一點,需要內(nèi)核提供鎖和同步機制回梧。
??? 5祖搓、進程控制:有些進程希望完全控制另一個進程的執(zhí)行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常详囤,并能夠及時知道它的狀態(tài)改變。
1.3?單機和跨主機通信
????同一主機進程間的通信方式:
??? 1纬纪、Unix進程間通信方式:PIPE滑肉、FIFO和信號
??? 2、SystemV進程間通信方式:信號量(Semaphore)问畅、消息隊列(Message Queue)和共享內(nèi)存(Shared Memory)
????3六荒、讀寫鎖、互斥鎖卵皂、條件變量
????4砚亭、文件殴玛、文件鎖
????網(wǎng)絡(luò)主機間的通信方式:
??? 1添祸、RPC:Remote Protocol Call遠程過程調(diào)用(Thrift刃泌、gRPC)
??? 2、Socket:基于TCP/IP協(xié)議
02?管道
2.1 概述
????管道是Linux系統(tǒng)最古老的IPC方法耙替,可以簡單地將管道理解為一個通道林艘,它允許數(shù)據(jù)從一個進程流向另外一個進程。
2.2 特點
????1狐援、管道傳輸?shù)氖菬o格式字節(jié)流
????讀者從管道中讀取任意大小的數(shù)據(jù)塊,而不管寫者寫入管道的數(shù)據(jù)塊大小爹凹,即使用管道時不存在消息邊界的概念镶殷。此外,通過管道傳遞的數(shù)據(jù)是順序的绘趋,從管道中讀取出來字節(jié)的順序與它們被寫入管道的順序是完全一樣的陷遮。
????2、從管道中讀取數(shù)據(jù)
????試圖從一個當(dāng)前為空的管道中讀取數(shù)據(jù)將會被阻塞直到至少有一個字節(jié)被寫入到管道為止帽馋。如果管道的寫入端被關(guān)閉了绽族,那么從管道中讀取數(shù)據(jù)的進程在讀完管道中剩余的所有數(shù)據(jù)之后將會看到文件結(jié)束。
??? 3涛漂、管道是半雙工(單工)的(有的系統(tǒng)可能支持全雙工)检诗,數(shù)據(jù)只能向一個方向流動底哗;需要雙方通信時锚沸,需要建立起兩個管道哗蜈。
??? 4坠韩、管道的容量是有限的。管道其實是一個在內(nèi)核內(nèi)存中紅維護的緩沖區(qū)音比,這個緩沖區(qū)的存儲大小是有限的氢惋,一旦管道被填滿之后,繼續(xù)向該管道的寫入操作就會被阻塞直到讀者從管道中移除一些數(shù)據(jù)為止骚亿。
2.3 函數(shù)
????1熊赖、管道創(chuàng)建/關(guān)閉
????#include<unistd.h>intpipe(intfd[2]);
????成功的pipe()調(diào)用會在數(shù)組fields中返回兩個打開的文件描述符:一個表示管道的讀取端(fields[0]),另一個表示管道的寫入端(fields[1])俱笛。
??? 2传趾、管道的一個常見用途是執(zhí)行shell命令并讀取其輸出或者向其發(fā)送一些輸入墨缘,popen()和pclose()函數(shù)簡化了這個任務(wù)。
????FILE?*popen(constchar*?command?,constchar*?type?);intpclose( FILE * stream );
??? popen函數(shù)創(chuàng)建了一個管道宽涌,然后創(chuàng)建了一個子進程來執(zhí)行shell蝶棋,而shell又建立了一個子進程來執(zhí)行command字符串。mode參數(shù)是一個字符串兼贸,它確定調(diào)用進程是從管道中讀取數(shù)據(jù)(mode是r)還是將數(shù)據(jù)寫入到管道中(mode是w)。
03?信號
3.1?概述
????由于管道是一種無形鸯檬、無名的文件螺垢,它就只能通過fork的過程創(chuàng)建在“近親”的進程之間,而不可能成為可以在任意兩個進程之間建立通信的機制功茴,更不可能稱為一般的孽亲、通用的進程間通信模型。因此引入有名管道FIFO玲昧,它與管道最大的區(qū)別在于在文件系統(tǒng)中有名稱旭等,并且打開方式與打開普通文件一樣搔耕,可以實現(xiàn)非相關(guān)進程間的通信。
3.2 特點
????有名管道也是半雙工的通信方式菩收,但是它允許無親緣關(guān)系進程間的通信鲸睛。
3.3 函數(shù)
????#include<sys/types.h>#include<sys/stat.h>intmkfifo(constchar*pathname,mode_tmode);
????返回:若成功返回0,出錯返回-1箱舞。
????FIFO相關(guān)出錯信息:
????EACCES? 無存取權(quán)限
????EEXIST? 指定文件不存在
????ENAMETOOLONG? 路徑名太長
????ENOENT? 包含的目錄不存在
????ENOSPC? 文件系統(tǒng)剩余空間不足
????ENOTDIR? 文件路徑無效
????EROFS? 指定的文件存在于只讀文件系統(tǒng)中
04?信號
4.1 概述
????信號(signal)是Linux進程間通信的一種機制拳亿,全稱為軟中斷信號肺魁,也被稱為軟中斷。信號本質(zhì)上是在軟件層次上對硬件中斷機制的一種模擬。
????信號表示一種事件怎诫,也可能異步發(fā)生贷痪。如果程序并未安排怎么處理一個特定的信號劫拢,那么該信號出現(xiàn)時程序就作出一個缺省的反應(yīng)。標準未定義這個缺省反應(yīng)是什么,但絕大多數(shù)編譯器選擇終止程序哪廓。另外涡真,程序可以調(diào)用signal函數(shù),或者忽略這個信號哆料,或者設(shè)置一個信號處理函數(shù)东亦,當(dāng)信號發(fā)生時程序就調(diào)用這個函數(shù)。
????與其他進程間通信方式相比奋渔,信號所能傳遞的信息比較粗糙壮啊,只是一個整數(shù)。但正是由于傳遞的信息量少玄渗,信號也便于管理和使用狸眼,可以用于系統(tǒng)管理相關(guān)的任務(wù)份企,例如通知進程終結(jié)、中止或恢復(fù)等。
4.2 信號名
????SIGABRT程序請求異常終止
????SIGFPE發(fā)生一個算術(shù)錯誤
????SIGILL檢測到非法指令
????SIGSEGV檢測到對內(nèi)存的非法訪問
????SIGINT收到一個交互性注意信號
????SIGTERM收到一個終止程序的請求
????注:前幾個都是同步的降宅,SIGINT囚霸、SIGTERM是異步的拓型,它們在程序外部發(fā)生,同城是由程序的用戶觸發(fā)册养。
4.3 函數(shù)
????void (*signal(intsig , void (*func)(int)))(int);
????參數(shù):
????sig:信號編號
????void(*func)(int):信號處理函數(shù)
????返回值:類型是一個信號處理函數(shù)的函數(shù)指針
????SIG_ERR錯誤返回結(jié)果(void(*)(int))(-1)
????SIG_IGN忽略某個信號(void(*)(int))(0)
????SIG_DFL默認處理某個信號(void(*)(int))(1)
????信號集操作
????對信號集進行信號的清空压固、加入帐我、刪除等操作。
????intsigemptyset(sigset_t *set);將set集合置空
????intsigfillset(sigset_t *set)谣光;將所有信號加入set集合
????intsigaddset(sigset_t *set,intsigno)將signo信號加入到set集合
????intsigdelset(sigset_t *set芬为,intsigno);從set集合中移除signo信號
????intsigismember(constsigset_t *set,intsigno);signo判斷信號是否存在于set集合中
????設(shè)置或獲取信號集的信號屏蔽字:
????intsigprocmask(inthow,constsigset_t*restrictset,sigset_t*restritoset);
????返回值:成功返回0,出錯返回-1
????SIG_BLOCK? ? 追加屏蔽某個信號
????SIG_UNBLOCK? 取消屏蔽某個信號
????SIG_SETMASK???設(shè)置新的屏蔽字信號集
????如果屏蔽所有信號捡絮,可以進行如下設(shè)置:
????sig_set_tset;
????sigfillset(&set);
????sigprocmask(SIG_BLOCK, &set,NULL)福稳;
????等價于如下設(shè)置:
????sig_set_tset;
????sigfillset(&set);
????sigprocmask(SIG_SETMASK,?&set,NULL);
????如果清空所有信號屏蔽字集合瑞侮,可以進行如下設(shè)置:
????sig_set_tset;
????sigfillset(&set);
????sigprocmask(UN_BLOCK, &set,NULL);
????等價于如下設(shè)置:
????sig_set_tset;
????sigemptyset(&set);
????sigprocmask(SIG_SETMASK,?&set,NULL);
????獲取當(dāng)前信號屏蔽字信號集:
????sig_set_tset;
????sigemptyset(&set);
????sigprocmask(SIG_BLOCK,NULL, &set);
05?消息隊列
5.1 概述
????消息隊列是由消息的鏈表半火,存儲在內(nèi)核中并由消息隊列標識符標識。消息隊列克服了信號傳遞信號量少梅掠、管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺點。
????與管道和FIFO不同酪我,進程可以在沒有另外一個進程等待讀的情況下進行寫且叁。另外一方面,管道和FIFO一旦相關(guān)進程都關(guān)閉并退出后欺矫,里面的數(shù)據(jù)也就沒有了展氓,但是對于消息隊列遇汞,一個進程往消息隊列中寫入數(shù)據(jù)后退出,另外一個進程仍然可以打開并讀取消息勺疼。消息隊列與UNIX域套接字相比执庐,在速度上沒有多少優(yōu)勢导梆。
5.2 函數(shù)
????System V定義:
????#include<sys/msg.h>
????intmsgget(key_tkey,intflag);
????intmsgctl(intmsgid,intcmd,struct?msqid_ds?*buf);
????intmsgsnd(intmsgqid,constvoid*ptr,size_tnbytes,intflag);
????size_tmsgrcv(intmsgqid,void*ptr,size_tnbytes,longtype,intflag);
????Posix定義:
????#include<mqueue.h>
????mqd_tmq_open(constchar*name,intoflag,/*?mode_t?mode,?struct?mq_attr?*attr?*/);
????mqd_tmq_close(mqd_tmqdes);
????mqd_tmq_unlink(constchar*name);
????mqd_tmq_send(mqd_tmqdes,constchar*msg_ptr,size_tmsg_len,unsignedmsg_prio);
????mqd_tmq_receive(mqd_tmqdes,char*msg_ptr,size_tmsg_len,unsigned*msg_prio);
06?共享內(nèi)存
6.1 概述
????共享內(nèi)存區(qū)域是被多個進程共享的一部分物理內(nèi)存看尼。如果多個進程都把該內(nèi)存區(qū)域映射到自己的虛擬地址空間,則這些進程就都可以直接訪問該共享內(nèi)存區(qū)域躏结,從而可以通過該區(qū)域進行通信狰域。
????共享內(nèi)存是進程間共享數(shù)據(jù)的一種最快的方法兆览,一個進程向共享內(nèi)存區(qū)域?qū)懭霐?shù)據(jù),共享這個內(nèi)存區(qū)域的所有進程就可以立刻看到其中的內(nèi)容子巾。
6.2 函數(shù)
????SYSTEM V定義:
????intshmget(key_tkey,intsize,intshmflg);
????void*shmat(intshmid,constvoid*shmaddr,intshmflg);
????intshmdt(constvoid*shmaddr);
????intshmctl(intshmid,intcmd,?struct?shmid_ds?*buf);
????POSIX定義:
????intshm_open(constchar*name,intoflag,mode_tmode);
????intshm_unlink(constchar*name);
????intftruncate(intfd,off_tlength);
????由于POSIX標準比較通用,一般建議使用該標準定義的方法集椰于。
07?信號量
7.1 概述
????信號量(semaphore)缠导,有時稱為信號燈僻造,是多線程環(huán)境下使用的一種設(shè)施,可以用來保證兩個或多個關(guān)鍵代碼段不被并發(fā)調(diào)用(就是具有原子性的計數(shù)器)竹挡。
????信號量是一個計數(shù)器立膛,可用來控制多個進程對共享資源的訪問。它常常作為一種鎖機制好啰,防止某進程正在訪問共享資源時儿奶,其他進程也訪問該資源闯捎。因為,作為進程間以及同一進程內(nèi)不同線程之間的同步手段瓤鼻。
7.2 函數(shù)
????System V定義:
????#incldue<sys/sem.h>
????intsemget(key_tkey,intnsems,intflag);
????intsemctl(intsemid,intsemnum,intcmd,.../*union?semun?arg*/)
????intsemop(intsemid,struct sembuf *semop,size_tnops);
????Posix定義:
????intsem_init(sem_t*sem,intpshared,unsignedintvalue);
????intsem_wait(sem_t*sem);
????intsem_trywait(sem_t* sem);
????intsem_timedwait(sem_t*sem,conststruct timespec *abs_timeout);
????intsem_post(sem_t*sem);
????intsem_close(sem_t*sem);
????intsem_unlink(constchar*name);
????intsem_destroy(sem_t*sem);
08?內(nèi)存映射
8.1 概述
??? mmap()系統(tǒng)調(diào)用在調(diào)用進程的虛擬地址空間中創(chuàng)建一個新內(nèi)存映射,映射分為兩種:
? ??1清焕、文件映射
????文件映射將一個文件的一部分直接映射到調(diào)用進程的虛擬內(nèi)存中耐朴,一旦一個文件被映射之后就可以通過在相應(yīng)的內(nèi)存區(qū)域中操作字節(jié)來訪問文件內(nèi)容了访诱。映射的分頁會在需要的時候從文件中(自動)加載影晓。這種映射也被稱為基于文件的映射或內(nèi)存映射文件。
? ??2疤祭、匿名映射
????一個匿名映射沒有對應(yīng)的文件饵婆,相反,這種映射的分頁會被初始化為0草穆。
8.2 函數(shù)
????void*mmap(void*start,size_tlength,intprot,intflags,intfd,off_toffset);
????intmunmap(void*start,size_tlength);
????參數(shù):start:映射區(qū)的開始地址悲柱。
????length:映射區(qū)的長度些己。
????prot:期望的內(nèi)存保護標志,不能與文件的打開模式?jīng)_突涯冠。
????是以下的某個值逼庞,可以通過or運算合理地組合在一起
????PROT_EXEC//頁內(nèi)容可以被執(zhí)行
????PROT_READ//頁內(nèi)容可以被讀取
????PROT_WRITE//頁可以被寫入
????PROT_NONE//頁不可訪問flags:指定映射對象的類型往堡,映射選項和映射頁是否可以共享共耍。
09?RPC
????遠程過程調(diào)用(Remote Protocol Call),跨主機通信穆咐。
10?套接字
????網(wǎng)絡(luò)通信字旭,包括UDP和TCP兩種遗淳,單機或跨主機通信。