進程通訊(五)--共享內存

 共享內存就是倆進程共同擁有的內存,一般執行一個程序,會給內存上分配該進程的空間吧。
  如果兩個進程有一部分是使用共享內存,那麼這倆進程其他部分會各自分配自己的內存空間,而使用共享內存的地方只會分配一個共有的內存空間,也就是說這部分共享內存空間都屬於這兩個進程,這部分空間對於這倆進程來說都是自己的。

共享內存本身不難理解,使用起來也無非是介紹幾種函數,熟悉下用法。和其他IPC比較一下,還是很容易的。
但是關鍵的是共享內存一個主要的問題是解決同步問題,即這片內存是共享的,就會存在很頻繁的數據競爭問題,而共享內存函數本身也沒有提供同步的功能。因此往往需要我們把信號量引入進來,和共享內存一起使用才能解決問題。   當然雖然共享內存同步異步這裏稍微麻煩,但是換來的確實很大很大的通訊量,比起消息隊列管道那些小水管的數據傳送,共享內存相當於在內存上直接共享數據 ,也是這幾種IPC中最大的。

這之前還是先介紹下函數吧。
其實比起之前消息隊列信號量這幾種IPC來講,都是大同小異。
有着類似的shmget shmctl。但是有倆函數稍微不同,就是shmat 和shmdt,這倆函數前者負責將創建的共享內存空間鏈接到使用函數的進程中,一個負責斷開共享內存空間和使用該函數的進程空間。

四個函數具體聲明如下

1.int shmget(key_t key,size_t size,int shmflg);
第一個key值自己隨意定義
第二個參數size是自己要開闢的共享內存大小
第三個參數就是權限標誌 類似 0664|IPC_CREAT 這樣的
失敗返回-1 成功返回標識符shmid

2.void *shmat(int shmid,const void * shm_addr,int shmflg);
第一個參數就是shmget返回的標識符
第二個參數爲共享內存連接到當前進程中的地址,可以自己給定,一般都是NULL讓系統自己選擇最適合的。
第三個參數就是宏,可以SHM_RDONLY表示只讀。也可以寫0表示默認的可讀可寫。
失敗返回-1  成功返回指向共享內存第一個字節的指針

3.int shmdt(void *shmatrt)
參數爲 shmat中返回的指針。
失敗返回-1,成功爲0。


4.int shmctl(int shm_id,int command,struct shmid_ds*buf);
和消息隊列與信號量的控制函數幾乎一樣。
一般我們用它刪除共享內存的比較多,第二個參數取IPC_RMID 第三個參數爲0就好。其他用法可以參考前兩篇博客裏的控制函數。

照樣拿一道題舉例,一個進程負責讀取用戶輸入的單詞寫入共享內存,另一個進程負責統計個數並打印出來
這裏就要引用信號量了。
我們知道當一個進程正在讀取用戶輸入的單詞並寫入內存時,另一個統計單詞個數的進程是不能工作的,因爲第一個進程可能還沒寫入完畢,此時讀取統計個數可能會發生錯誤。
當第一個進程寫入用戶輸入的單詞完畢後,第二個進程開始統計個數。此時第一個進程就不能繼續讀取用戶輸入的單詞並寫入了,如果寫入的話,第二個進程統計個數時就可能發生錯誤,正統計的單詞時被寫入的新單詞覆蓋了。
因此我們要求這倆進程要同步運行。即第一個進程輸入寫入時,統計單詞個數的進程要等待,寫入完畢後,統計單詞的進程開始工作,輸入寫入的進程要等待,並且這倆必須一個來一次,交換着等待。
所以我們要引入兩個信號量。  比如甲 乙信號量,甲信號量初值爲1,乙信號量初值爲0。
我們把負責讀取用戶輸入的單詞這個進程簡稱爲A.
把統計個數並打印的進程簡稱爲B.
我們的目的是讓A先運行,B等着,然後A完成功能後,B運行,A等着。然後B完成功能後,A運行,B等着.....這樣一直你來我往的等待。
所以我們可以讓A先對甲信號量p操作,B對乙信號量p操作,由於乙初值爲0,所以B不能運行。只能A運行。
A運行達到條件後對乙信號量V操作,此時B就可以運行了,然而A由於一開始已經p操作一次了,所以再次循環到輸入時,會卡在對甲信號量的p操作。  此時B運行完了,然後B再對甲信號量v操作,這時A又可以運行了,B再循環時又卡在對乙的p操作了。這樣就完成了同步的控制。

代碼:
讀取用戶輸入的進程




統計單詞個數的進程:




運行結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章