关于进程间通信的学习心得
進程:進程是指獨立地址空間的指令序列
進程的五種狀態:新建,就緒,運行,睡眠,僵死
進程間通信:是不同進程之間進行一些"接觸",這種接觸有簡單,有復雜。機制不同,復雜度也不同。通信是一個廣義上的意義,不僅指大批量數據傳送,還包括控制信息的傳送,但使用方法是基本相同的。
基本的進程通信機制
1.傳統UNIX-IPC機制:信號和管道
2.SystemV的IPC機制:共享內存、信號量和消息隊列
3.起源于Unix BSD版本的套結字(Socket)
4.遠程過程調用(RPC)
信號:Unix系統中使用的最古老的進程間通訊的方法之一,用于向一個或多個進程發送異步事件的信號。信號可以類比于DOS下的INT或者是Windows下的事件。在有一個信號發生的時候,相應的信號就會發送給相應的進程。
信號機制的實現
1.信號包括待處理信號和被阻塞信號
2.如果產生了一個被阻塞的信號,它一直保留待處理,直到被解除阻塞。
3.系統保存每一個進程如何處理每一種可能的信號的信息。
4.系統判斷進程是希望忽略這個信號還是讓內核處理。進程通過執行系統調用改變缺省的信號處理。
對信號的處理
1.初始化信號集,只有在信號集里面的信號才會被考慮
2.安裝信號處理器。所謂信號處理器,就是指定了一些對信號的處理方法。在安裝的時候,一定要對特定的信號賦予正確的信號處理函數。
信號相關函數
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);為進程安裝信號處理器,struct sigaction數據結構是用來保存信號處理器的相關信息。
int sigemptyset(sigset_t *set);將信號集合清空。
int sigfillset(sigset_t *set);將信號集合設置成包含所有的信號,在對信號進行操作以前一定要對信號集進行初始化。
int sigaddset(sigset_t *set, int signo);向信號集中加入signo對應的新信號。
int sigdelset(sigset_t *set, int signo);從信號集中刪除signo對應的一個信號。
int sigismember(const sigset_t *set, int signo);判斷某個信號是否在信號集中。
int sigprocmask(int how,const sigset_t *set, sigset_t *oset);設置進程的信號屏蔽碼。信號屏蔽碼可以用來在某段時間內阻塞一些信號集中的信號。
管道通信:是最古老的Unix IPC工具,一個進程從管道一頭寫數據,另一個進程從管道另一頭讀數據,以實現它們之間通信的共享方式,又稱pipe文件。由于發送和接收都是利用管道進行通信的,故稱為管道通信。通信方式是單向的。管道類型分為:無名管道、命名管道
管道通信的思想
1.發送進程可以源源不斷的從pipe一端寫入數據流,在規定的pipe文件的最大長度(如4096字節)范圍內,每次寫入的信息長度是可變的。
2.接收進程在需要時可以從pipe的另一端讀出數據,讀出單位長度也是可變的。
基本管道調用函數
int do_pipe(int *fd);創建管道
static int pipe_release(struct inode *inode, int decr, int decw);管道釋放
無名管道II
顯示了每一個file數據結構包含了不同的文件操作例程的向量表的指針:一個用于寫,另一個從管道中讀。這掩蓋了和通用的讀寫普通文件的系統調用的不同。當寫進程向管道中寫的時候,字節拷貝到了共享的數據頁,當從管道中讀的時候,字節從共享頁中拷貝出來。
命名管道:又名FIFO,它不是臨時的對象,而是文件系統中的實體,可以用mkfifo命令創建。系統必須處理在寫進程打開FIFO之前打開FIFO讀的進程,以及在寫進程寫數據之前讀的進程。它使用和無名管道一樣的數據結構和操作。
???????(寫入端)[Fd1]→pipe(fd)→[Fd0](讀出端)
信號量:信號量(Semaphore)和信號是不同的東西,信號是實現約定的固定的值,而信號量是一個變量記錄著某些特定信息,它的使用主要是用來保護共享資源,使得該資源在一個時刻只讓一個進程擁有。
信號量的數據結構:使用semid_ds數據結構表達信號量。系統中所有的semid_ds數據結構都由semary指針向量表指向。每一個信號燈數組中都有sem_nsems,通過sem_base指向的一個sem數據結構來描述
信號量機制的實現
1.對信號量的操作只有兩個:P、V。
2.為了在邏輯上便于組織信號量,信號量機制中有一個概念是信號量組。我們在一個信號量組中創建相關的信號量,這樣邏輯上清晰也便于管理。
3.在使用之前同樣需要對他們進行初始化:生成或打開信號量組,向其中生成或刪除指定的信號量。
4.一個信號量必須屬于一個信號量組,否則不能被系統所使用。
5.信號量和信號量組是不會被系統所自動清理的,所以在進程退出前,需要及時清理生成的那些信號量。
信號量的相關函數
int semget(key_t key, int nsems, int semflg);創建一個新的信號量組或獲取一個已經存在的信號量組。
int semop(int semid, struct sembuf *sop, int nsops);一次對一個或多個信號量進行操作,用于P、V操作。
Int semctl(int sem_id, int semnum, int cmd, union semun arg);用來獲取一些信號量的使用信息或者是來對信號量進行控制。
共享內存
????共享內存是進程通信的一個重要方法,為進程提供了直接通信的手段。操作系統中一個或多個進程通過同時出現在它們的虛擬地址空間的內存通訊,該虛擬內存被每個共享進程的頁表所引用,它們的地址無需相同。進程對共享內存的訪問是受控的,信號量等機制實現了共享內存訪問的同步
共享內存的簡單原理
每一個新創建的內存區域都用一個shmid_ds數據結構來表達。這些數據結構保存在shm_segs向量表中。Shmid_ds數據結構描述了這個共享內存取有多大、多少個進程在使用它以及共享內存如何映射到它們的地址空間。由共享內存的創建者來控制對于這塊內存的訪問權限和它的key是公開或私有。如果有足夠的權限它也可以把共享內存鎖定在物理內存中。每一個希望共享這塊內存的進程必須通過系統調用粘附(attach)到虛擬內存。這為該進程創建了一個新的描述這塊共享內存的vm_area_struct數據結構。進程可以選擇共享內存在它的虛擬地址空間的位置或者由Linux選擇一塊足夠的的空閑區域。這個新的vm_area_struct結構放在由shmid_ds指向的vm_area_struct列表中。通過vm_next_shared和vm_prev_shared把它們連在一起。
共享內存的基本函數
int shmget(key_t key,int size,int shmflg);建立新的共享內存或一個已存在的共享內存描述字
void *shmat(int shmid,const void *shmaddr,int shmflg);將物理共享內存粘附到進程虛擬地址空間
int shmdt(const void *shmaddr);進程從其虛擬地址空間分離共享內存
int shmctl(int shmid,int cmd,struct shmid_ds *buf);查詢及設置一個共享內存
共享內存機制的實現
在使用一個共享內存之前我們調用shmat得到共享內存的開始地址,使用結束以后我們使用
shmdt斷開這個內存。
進程通過系統調用粘附到虛擬內存,即創建了一個新的描述這塊共享內存的vm_area_struct數據結構,這個新的vm_area_struct結構放在由shmid_ds指向的vm_area_struct列表中。通過vm_next_shared和vm_prev_shared把它們連在一起。
虛擬內存在粘附的時候其實并沒有創建,而發生在第一個進程試圖訪問它的時候。
第一個訪問共享內存頁的進程使得這一頁被創建,而隨后訪問的其他進程使得此頁被加到它們的虛擬地址空間。
當進程不再需要共享虛擬內存的時候,它們從中分離出來。只要仍舊有其他進程在使用這塊內存,這種分離只是影響當前的進程。
當共享這塊內存的最后一個進程從中分離出的時候,共享內存當前在物理內存中的頁被釋放
消息隊列:消息隊列是比較高級的一種進程間通信方法,實現一個或多個進程間message傳送,一個消息隊列可以被多個進程所共享(IPC就是在這個基礎上進行的);如果一個進程的消息太多一個消息隊列放不下,也可以用多于一個的消息隊列(不過可能管理會比較復雜)。
消息隊列的基本函數
int msgget(key_t key, int msgflg);獲取一個存在的消息隊列的ID,或者是根據跟定的權限創建一個消息隊列。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);用來從msqid_ds中獲取很多消息隊列本身的信息。
int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg);用于向隊列發送消息。
int msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, intmsgflg);從隊列中接收消息。
消息隊列的實現機制
消息隊列,是一個隊列的結構,隊列里面的內容由用戶進程自己定義。實際上,隊列里面記錄的是指向用戶自定義結構的指針和結構的大小。要使用message queue,首先要通過系統調用(msgget)產生一個隊列,然后,進程可以用msgsnd發送消息到這個隊列,消息就是如上所說的結構。別的進程用msgrcv讀取。消息隊列一旦產生,除非明確的刪除(某個有權限的進程或者用ipcrm命令)或者系統重啟。否則,產生的隊列會一直保留在系統中。而且,只要有權限,就可以對隊列進行操作。消息隊列和管道很相似,實際上,管道就是用戶消息為1個字節的隊列。
消息隊列的寫進程
每一次一個進程試圖向寫隊列寫消息,它的有效用戶和組的標識符就要和隊列的ipc_perm數據結構的模式比較。如果進程可以想這個隊列寫,則消息會從進程的地址空間寫到msg數據結構,放到消息隊列的最后。每一個消息都帶有進程間約定的,應用程序指定類型的標記。
消息隊列的讀進程
從隊列中讀是一個相似的過程。進程的訪問權限一樣被檢查。一個讀進程可以選擇是不管消息的類型從隊列中讀取第一條消息還是選擇特殊類型的消息。如果沒有符合條件的消息,讀進程會被加到消息隊列的讀等待進程,然后運行調度程序。當一個新的消息寫到隊列的時候,這個進程會被喚醒,繼續運行。
消息隊列的簡單流程
發送進程?????????????????????????????接收流程
Send(m)??????????????????????????????Receive(m )
?? Begin?????????????????????????????????begin
???向系統申請一個消息緩沖區??????????????P(SM) 等待接的消息的個數
???P(mutex) 使用公用緩沖區???????????????P(mutex) 使用公用緩沖區
???將發送區消息m送入新申請的消息緩沖區??摘下消息隊列中的消息m
???把消息緩沖區掛入接收進程的消息隊列?????將消息隊列m從緩沖區復制到接收區
???V(mutex)釋放緩沖區????????????????????釋放緩沖區
???V(SM)向接收進程發送消息??????????????V(mutex) 釋放公用緩沖區
??End?????????????????????????????????????end
Socket
獨立于具體協議的網絡編程接口;
在ISO模型中,主要位于會話層和傳輸層。
網絡編程接口:UNIX BSD的套接字(socket)、UNIX System V的TLI
BSD Socket(伯克立套接字)是通過標準的UNIX文件描述符和其它程序通訊的一個方法,目前已經被廣泛移植到各個平臺。
Socket的類型
流式套接字:提供了一個面向連接,可靠的數據傳輸服務,數據無差錯,無重復地發送且按發送順序接收.內設流量控制,避免數據流超限;數據被看作是字節流,無長度限制,FTP即用此
數據報套接字:提供了一個無連接服務.數據包以獨立包形式被發送,不提供無差錯保證,數據可能丟失或重復,并且接受順序無序,網絡文件系統NFS
原始套接字(SOCK_RAW):該接口允許對較低層次協議,如IP,ICMP直接訪問
基本套接字調用
創建套接字???????????????socket();
綁定本機端口???????????bind();
建立連接???????????????????connect();
接受連接???????????????????accept();
監聽端口???????????????????listen();
數據傳輸???????????????????send(), recv()等
關閉套接字???????????????close();
Socket相關數據結構
struct sockaddr_in
??????????{
??????????????????short int??? sin_family;????/*通信類型*/
??????????????????unsigned short int sin_port;???????/*端口號,網絡直接順序*/
??????????????????struct in_addr??
???????? }
總結
以上是生活随笔為你收集整理的关于进程间通信的学习心得的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《感兴二首》是哪个时期的作品?
- 下一篇: Centos5.6 VNC安装配置【无错