標籤:des style blog http io ar color 使用 sp
今天Mayuyu要學習Linux中一個重要的東西,即共用記憶體。我們知道在Linux中提供了多種IPC通訊機制,比如訊號,訊號量,管道,訊息佇列,共用記憶體和通訊端等。其中共用記憶體的效率最高。Mayuyu將用以下幾個方面來介紹
Contens
1. 共用記憶體的認識
2. 共用記憶體的原理
3. Linux中共用記憶體的API
4. 共用記憶體的讀寫問題
5. 關於共用記憶體的命令
1. 共用記憶體的認識
所謂共用記憶體,就是多個進程間共同地使用同一段實體記憶體空間,它是通過將同一段實體記憶體映射到不同進程
的虛擬空間來實現的。由於映射到不同進程的虛擬空間中,不同進程可以直接使用,不需要像訊息佇列那樣進
行複製,所以共用記憶體的效率很高。
共用記憶體可以通過mmap()映射普通檔案機制來實現,也可以System V共用記憶體機制來實現,System V是通
過映射特殊檔案系統shm中的檔案實現進程間的共用記憶體通訊,也就是說每個共用記憶體地區對應特殊檔案系統
shm中的一個檔案。
2. 共用記憶體的原理
System V共用記憶體把所有共用資料放在共用記憶體區,任何想要訪問該資料的進程都必須在本進程的地址空間
新增一塊記憶體地區,用來映射存放共用資料的實體記憶體頁面。System V共用記憶體通過shmget函數獲得或建立
一個IPC共用記憶體地區,並返回相應的標識符,核心在保證shmget獲得或建立一個共用記憶體區,初始化該共用
記憶體區相應的shmid_kernel結構,同時還將在特殊檔案系統shm中建立並開啟一個同名檔案,並在記憶體中建立
起該檔案的相應的dentry及inode結構,新開啟的檔案不屬於任何一個進程,所有這一切都是系統調用shmget
函數完成的。
上面說到,在每一個共用記憶體區都有一個控制結構shmid_kernel,它是共用記憶體地區中一個非常重要的結構,
它是儲存管理和檔案系統結合起來的橋樑,定義如下
該結構體中一個最重要的域是shm_file,它儲存了被對應檔的地址,每個共用記憶體區對象都對應特殊檔案
系統shm中的一個檔案,一般情況下,特殊檔案系統shm中的檔案是不能用read(),write()等方法訪問的,
當採取共用記憶體的方式把其中的檔案對應到進程地址空間後,可以採用訪問記憶體方式對其訪問。
對於System V共用記憶體區來說,kern_ipc_perm的宿主是shmid_kernel結構,shmid_kernel是用來描
述一個共用記憶體地區的,這樣核心就能夠控制系統中所有的共用地區。同時在shmid_kernel結構的file類型
指標類型shm_file指向檔案系統shm中相應的檔案。這樣,共用記憶體地區就與shm檔案系統中的檔案對應起來。
3. Linux中共用記憶體的API
System V IPC機制下建立和使用共用記憶體是通過如下四個API來完成的。
(1)shmget()
建立共用記憶體
函數原型為:
此函數調用用來建立一個新的共用記憶體段或者存取一個已經存在的共用記憶體段。函數調用成功返回共用內
存段的標識符,否則返回-1。
參數說明如下
key : 用來表示建立或者已存在的共用記憶體區的關鍵字。
size: 共用記憶體的大小。
shmflg : 可以指定的特殊標識,IPC_CREAT,IPC_EXCL以及低九位的許可權。
shmget的內部包含了許多重要的System V共用記憶體機制,shmget在把共用記憶體區域對應到進程空間時,
並不真正改變進程的頁表,當進程第一次訪問記憶體映射地區時,會因為沒有物理頁表的分配而導致一個缺
頁異常,然後核心再根據相應的儲存管理機製為共用記憶體映射地區分配相應的頁表。
(2)shmat()
映射共用記憶體
函數原型為:
在獲得新建立的一個共用記憶體段或者已存在的共用記憶體段的標識符後,通過shmat調用可以將共用記憶體段
映射到進程的記憶體空間。函數調用成功返回共用記憶體段的地址,否則返回-1。當一個共用記憶體段被附加到
一個新進程後,共用記憶體段的shm_array結構中的共用計數器會加1。
參數說明
shmid : 共用記憶體的關鍵字。
shmaddr :指定共用記憶體出現在進程記憶體位址的位置,通常設定為零,表示讓核心指定一個合適的位置。
shmflg :附加段的讀寫屬性。值為SHM_RDONLY為唯讀模式,其它值為讀寫入模式。
(3)shmdt()
共用記憶體解除映射
函數原型為:
此函數調用可以將映射到進程的共用記憶體段撤銷。函數調用成功返回0值,否則返回-1。一個進程與一個
共用記憶體分離時,共用記憶體段的shm_array結構中的共用計數器值減1,直到計數器值為0,系統才真正
刪除這個共用記憶體頁面。
(4)shmctl()
完成對共用記憶體的控制
函數原型為:
用來查詢共用記憶體的使用方式和進行一些控制操作,函數調用成功返回0,否則返回-1。
而表示將刪除shm_id指示的共用記憶體。
參數說明如下
shmid : 共用記憶體的標識符。
cmd : IPC_STAT:得到共用記憶體的狀態,把共用記憶體的shmid_ds結構複製到buf中。
IPC_SET: 改變共用記憶體的狀態,把buf所指的shmid_ds結構中的uid、gid、mode複製
到共用記憶體的shmid_ds結構內。
IPC_RMID:刪除這片共用記憶體。
buf : 共用記憶體管理結構體,具體說明參見共用記憶體核心結構定義部分。
4. 共用記憶體的讀寫問題
下面的讀寫問題是共用記憶體的一個經典執行個體。通過Writer程式將內容寫入共用記憶體,而通過Reader程式將寫
入的共用記憶體的資料讀取出來,通過調用共用記憶體的API實現,也算是對共用記憶體的一個簡單實踐。
代碼:Writer.cpp
#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>#include <string.h>#include <stdio.h>struct Person{int age;char name[25];};void _work(){const char *file = "share";key_t key = ftok(file, 0);if(key == -1){perror("ftok error!");return;}int shm_id = shmget(key, 4096, IPC_CREAT);if(shm_id == -1){perror("shmget error!");return;}Person *_map = (Person*)shmat(shm_id, NULL, 0);char c = 'a';for(int i = 0; i < 10; i++){ c++;memcpy((*(_map + i)).name, &c, 1); (*(_map + i)).age = 20 + i;}if(shmdt(_map) == -1){perror("detach error!");}}int main(int argc, char **argv){_work(); return 0;}
Reader.cpp
#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <string.h>#include <stdio.h>struct Person{int age;char name[25];};void _work(){const char *file = "share";key_t key = ftok(file, 0);if(key == -1){perror("ftok error!");return;}int shm_id = shmget(key, 4096, IPC_CREAT);if(shm_id == -1){perror("shmget error!");return;}Person *_map = (Person*)shmat(shm_id, NULL, 0); for(int i = 0; i < 10; i++){ printf("name = %s\n", (*(_map + i)).name);printf("age = %d\n\n", (*(_map + i)).age);} if(shmdt(_map) == -1){perror("detach error!");}}int main(int argc, char **argv){_work();return 0;}
5. 關於共用記憶體的命令
要說到共用記憶體的命令,首先來介紹一個命令:ipcs,它是用來查看當前處理序間通訊設施的狀態的。後面跟
的參數種類比較多,比如ipcs -all表示查看所有設施類型,主要包括三種:共用記憶體,訊息佇列,訊號量。
當然也可以單獨查詢某一IPC類型的裝置情況
查詢共用記憶體的資訊
第一列就是共用記憶體的key;
第二列是共用記憶體的編號shmid;
第三列就是建立的使用者owner;
第四列就是許可權perms;
第五列為建立的大小bytes;
第六列為串連到共用記憶體的進程數nattach;
第七列是共用記憶體的狀態status。其中顯示“dest”表示共用記憶體段已經被刪除,但是還有使用者在使用它,當
該段記憶體的mode欄位設定為SHM_DEST時就會顯示“dest”。當使用者調用shmctl的IPC_RMID時,記憶體先查看
多少個進程與這個記憶體關聯著,如果關聯數為0,就會銷毀這段共用記憶體,否者設定這段記憶體的mod的mode位
為SHM_DEST,如果所有進程都不用則刪除這段共用記憶體。
查詢訊息佇列的資訊
查詢訊號量的資訊
在Linux中可以使用ipcrm命令來刪除共用記憶體,訊號量或訊息佇列的標誌。
文檔推薦:http://wenku.baidu.com/link?url=LUi4JVQNPw71f_n59u40wy0gH0Yq0l0YqRhnWxpBbxFS2dI72wpg4CLkfec94Ygh_gugRokhcm1mF3rORitvEFwLfzNtm0tE8SSECDoA0Je
Linux共用記憶體