共享內存區是最快的IPC(進程內通信)形式,不再通過執行進入內核的系統調用來傳遞彼此的數據
1.共享內存的原理
IPC通信的本質是讓不同的進程先看到同一份資源,然后再進行通信,所以想要通過共享內存進行通信,那么第一步一定是讓兩個進程能夠看到看到物理內存中的同一份資源。
2.共享內存函數
通過共享內存函數來創建共享內存
shmget:
功能:
? ? ? ? 創建共享內存原型:
????????int shmget(key_t key, size_t size, int shmflg);
參數:
? ? ? ? key:該共享內存段名字
? ? ? ? size:共享內存的大小
? ? ? ? shmflg:由九個權限標志位構成
? ? ? ? ? ? ? ? ? ? ? 若取值為IPC_CREAT:共享內存不存在,創建并返回;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 共享內存存在,獲取并返回;
? ? ? ? ? ? ? ? ? ? ? 若取值為IPC_CREAT | IPC_EXCL:共享內存不存在,創建并返回
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 共享內存已存在,出錯返回
返回值:
????????成功返回一個非負整數,即該共享內存的標識碼(shmid)
? ? ? ? 失敗返回-1? ? ? ?
注:所以當想要創建一個新的共享內存時,shmflg的取值應為:IPC_CREAT | IPC_EXCL
問:這個key值從何而來?以及這個key值有和作用?
答1:key值是通過ftok函數得來的,ftok會根據文件名/路徑相應的inode以及proj_id生成一個唯一的key值,且該key值和路徑(pathname)是一一對應的關系
ftok:
功能:????????
? ? ? ? 用來生成一個唯一的key值,該key值用于IPC 通信,共享內存的位置由系統決定
原型:
????????key_t ftok(const char *pathname, int proj_id);
參數:
? ? ? ? pathname:文件路徑,必須存在
? ? ? ? proj_id:項目標識符,通常是一個字符(0~255之間的整數)
注:為什么是路徑?除了歷史原因之外,路徑具有唯一性。
答2:顯然這個key值是用來創建共享內存的。具體怎么實現的呢?比如當前進程需要創建一個新的共享內存,那么在shmget中,只要key值相同,他們就能夠打開同一個共享內存,此時就滿足了不同進程看到同一份資源的要求。
進程A和進程B的虛擬地址不同,但是它們指向了同一塊物理內存
除了讓不同進程能夠看到同一份資源外,我們還需要將物理內存中的共享內存和進程的虛擬地址建立映射關系
shmat:
功能:
????????將共享內存段連接到進程地址空間(建立物理內存和虛擬內存間的映射關系)
原型:
????????void *shmat(int shmid, const void *shmaddr, int shmflg);
參數:
? ? ? ? shmid:創建/打開共享內存成功時返回給當前進程的共享內存標識碼
? ? ? ? shmaddr:指定連接的地址
? ? ? ? shmflg:一般為nulltpr
返回值:
? ? ? ? 成功返回一個指針,指向共享內存的第一個節
? ? ? ? 失敗返回-1
當前進程不在使用共享內存時,需要與共享內存脫離
shmdt:
功能:
????????將共享內存段與當前進程脫離
原型:
????????int shmdt(const void *shmaddr);
參數:
????????shmaddr: 由shmat所返回的指針??
返回值:
? ? ? ? 成功返回0
? ? ? ? 失敗返回-1
注:脫離不等于刪除當前內存段
shmctl:
功能:
????????用于控制共享內存
原型:
????????int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數:
????????shmid:由shmget返回的共享內存標識碼
????????cmd:將要采取的動作(有三個可取值)
????????buf:指向?個保存著共享內存的模式狀態和訪問權限的數據結構
返回值:
? ? ? ? 成功返回0
? ? ? ? 失敗返回-1
注:三個可取值
IPC_STAT:把shmid_ds結構中的數據設置為共享內存的當前關聯值
IPC_SET:在進程有足夠權限的前提下,把共享內存的當前關聯值設置為shmid_ds數據結構中給出的值
IPC_RMID:刪除共享內存段
3.初步認識信號量
對于共享內存而言,它沒有保護機制(當一個用戶的信息還沒有寫完時,可能就被另一個用戶讀走了),沒有保護機制就會造成讀取數據的不一致。因此我們需要引入信號量來解決這個問題
3.1概念補充
在初步認識信號量前,需要對某些概念進行補充。
①:代碼分為臨界區和非臨界區
對于非臨界區而言,即每個程序自己的代碼,他們之間不會相互影響
對于臨界區而言,涉及資源互斥的部分,也就上面提到的共享資源訪問時出現問題的部分
②:互斥
只允許一個程序訪問進程
③:臨界資源
被保護起來的資源
④:同步
多個進程訪問臨界資源時,具有一定的順序性
⑤:原子性
簡單說就是做和不做的區別
注:對共享資源的保護,總之就是對訪問共享資源的代碼進行保護
3.2信號量
舉一個電影院的例子:
一個影廳只有100個位置,如果票重復和提供的座位不足以發售的票都會出現問題。那么解決問題的措施就是 1.避免出現座位重復的票 2.避免發售的票太多。因此進入影院前,需要訂票來確定自己座位對于共享內存而言,將共享內存分塊訪問,那么所做的就是 1.避免多個進程訪問共享內存中的同一塊資源? 2.避免過多進程對該共享內存進行訪問
因此信號量的本質是一個計數器,用于描述臨界資源中,資源數量的多少,如果進程申請資源成功就做減減操作,直至為零。
注:任何進程想要訪問臨界資源,必須先申請信號量,本質是對資源(座位)的預訂機制
當進程申請信號量時,就做減減操作,保證該操作具有原子性,該操作稱為p操作
當進程不用時,就做加加操作,同時保證該操作具有原子性,該操作稱為v操作
3.3不同信號量
二元信號量:信號量只有0或者1
多元信號量:1以上就是多元信號量,內部資源可以供多個進程使用