linux進程通訊-共用記憶體

來源:互聯網
上載者:User

統調用mmap()通過映射一個普通檔案實現共用記憶體。系統V則是通過映射特殊檔案系統shm中的檔案實現進程間的共用記憶體通訊。也就是說,每個共用記憶體地區對應特殊檔案系統shm中的一個檔案(這是通過shmid_kernel結構聯絡起來的),後面還將闡述。

1、系統V共用記憶體原理

進程間需要共用的資料被放在一個叫做IPC共用記憶體地區的地方,所有需要訪問該共用地區的進程都要把該共用區域對應到本進程的地址空間中去。系統V共用記憶體通過shmget獲得或建立一個IPC共用記憶體地區,並返回相應的標識符。核心在保證shmget獲得或建立一個共用記憶體區,初始化該共用記憶體區相應的shmid_kernel結構注同時,還將在特殊檔案系統shm中,建立並開啟一個同名檔案,並在記憶體中建立起該檔案的相應dentry及inode結構,新開啟的檔案不屬於任何一個進程(任何進程都可以訪問該共用記憶體區)。所有這一切都是系統調用shmget完成的。

註:每一個共用記憶體區都有一個控制結構struct shmid_kernel,shmid_kernel是共用記憶體地區中非常重要的一個資料結構,它是儲存管理和檔案系統結合起來的橋樑,定義如下:

struct shmid_kernel /* private to the kernel */{struct kern_ipc_permshm_perm;struct file *shm_file;intid;unsigned longshm_nattch;unsigned longshm_segsz;time_tshm_atim;time_tshm_dtim;time_tshm_ctim;pid_tshm_cprid;pid_tshm_lprid;};

該結構中最重要的一個域應該是shm_file,它儲存了將被對應檔的地址。每個共用記憶體區對象都對應特殊檔案系統shm中的一個檔案,一般情況下,特殊檔案系統shm中的檔案是不能用read()、write()等方法訪問的,當採取共用記憶體的方式把其中的檔案對應到進程地址空間後,可直接採用訪問記憶體的方式對其訪問。

這裡我們採用[1]中的圖表給出與系統V共用記憶體相關資料結構:

正如訊息佇列和號誌一樣,核心通過資料結構struct ipc_ids shm_ids維護系統中的所有共用記憶體地區。中的shm_ids.entries變數指向一個ipc_id結構數組,而每個ipc_id結構數組中有個指向kern_ipc_perm結構的指標。到這裡讀者應該很熟悉了,對於系統V共用記憶體區來說,kern_ipc_perm的宿主是shmid_kernel結構,shmid_kernel是用來描述一個共用記憶體地區的,這樣核心就能夠控制系統中所有的共用地區。同時,在shmid_kernel結構的file類型指標shm_file指向檔案系統shm中相應的檔案,這樣,共用記憶體地區就與shm檔案系統中的檔案對應起來。

在建立了一個共用記憶體地區後,還要將它映射到進程地址空間,系統調用shmat()完成此項功能。由於在調用shmget()時,已經建立了檔案系統shm中的一個同名檔案與共用記憶體地區相對應,因此,調用shmat()的過程相當於對應檔系統shm中的同名檔案過程,原理與mmap()大同小異。

回頁首

2、系統V共用記憶體API

對於系統V共用記憶體,主要有以下幾個API:shmget()、shmat()、shmdt()及shmctl()。

#include <sys/ipc.h>#include <sys/shm.h>

shmget()用來獲得共用記憶體地區的ID,如果不存在指定的共用地區就建立相應的地區。shmat()把共用記憶體區域對應到調用進程的地址空間中去,這樣,進程就可以方便地對共用地區進行訪問操作。shmdt()調用用來解除進程對共用記憶體地區的映射。shmctl實現對共用記憶體地區的控制操作。這裡我們不對這些系統調用作具體的介紹,讀者可參考相應的手冊頁面,後面的範例中將給出它們的調用方法。

註:shmget的內部實現包含了許多重要的系統V共用記憶體機制;shmat在把共用記憶體區域對應到進程空間時,並不真正改變進程的頁表。當進程第一次訪問記憶體映射地區訪問時,會因為沒有物理頁表的分配而導致一個缺頁異常,然後核心再根據相應的儲存管理機製為共用記憶體映射地區分配相應的頁表。

回頁首

3、系統V共用記憶體限制

在/proc/sys/kernel/目錄下,記錄著系統V共用記憶體的一下限制,如一個共用記憶體區的最大位元組數shmmax,系統範圍內最大共用記憶體區標識符數shmmni等,可以手工對其調整,但不推薦這樣做。

在[2]中,給出了這些限制的測試方法,不再贅述。

回頁首

4、系統V共用記憶體範例

本部分將給出系統V共用記憶體API的使用方法,並對比分析系統V共用記憶體機制與mmap()映射普通檔案實現共用記憶體之間的差異,首先給出兩個進程通過系統V共用記憶體通訊的範例:

/***** testwrite.c *******/#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{char name[4];int age;} people;main(int argc, char** argv){int shm_id,i;key_t key;char temp;people *p_map;char* name = "/dev/shm/myshm2";key = ftok(name,0);if(key==-1)perror("ftok error");shm_id=shmget(key,4096,IPC_CREAT);if(shm_id==-1){perror("shmget error");return;}p_map=(people*)shmat(shm_id,NULL,0);temp='a';for(i = 0;i<10;i++){temp+=1;memcpy((*(p_map+i)).name,&temp,1);(*(p_map+i)).age=20+i;}if(shmdt(p_map)==-1)perror(" detach error ");}/********** testread.c ************/#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{char name[4];int age;} people;main(int argc, char** argv){int shm_id,i;key_t key;people *p_map;char* name = "/dev/shm/myshm2";key = ftok(name,0);if(key == -1)perror("ftok error");shm_id = shmget(key,4096,IPC_CREAT);if(shm_id == -1){perror("shmget error");return;}p_map = (people*)shmat(shm_id,NULL,0);for(i = 0;i<10;i++){printf( "name:%s\n",(*(p_map+i)).name );printf( "age %d\n",(*(p_map+i)).age );}if(shmdt(p_map) == -1)perror(" detach error ");}

testwrite.c建立一個系統V共用記憶體區,並在其中寫入格式化資料;testread.c訪問同一個系統V共用記憶體區,讀出其中的格式化資料。分別把兩個程式編譯為testwrite及testread,先後執行./testwrite及./testread 則./testread輸出結果如下:

name: bage 20;name: cage 21;name: dage 22;name: eage 23;name: fage 24;name: gage 25;name: hage 26;name: Iage 27;name: jage 28;name: kage 29;

通過對實驗結果分析,對比系統V與mmap()映射普通檔案實現共用記憶體通訊,可以得出如下結論:

1、 系統V共用記憶體中的資料,從來不寫入到實際磁碟檔案中去;而通過mmap()映射普通檔案實現的共用記憶體通訊可以指定何時將資料寫入磁碟檔案中。註:前面講到,系統V共用記憶體機制實際是通過映射特殊檔案系統shm中的檔案實現的,檔案系統shm的安裝點在交換分區上,系統重新引導後,所有的內容都丟失。

2、 系統V共用記憶體是隨核心持續的,即使所有訪問共用記憶體的進程都已經正常終止,共用記憶體區仍然存在(除非顯式刪除共用記憶體),在核心重新引導之前,對該共用記憶體地區的任何改寫操作都將一直保留。

3、 通過調用mmap()映射普通檔案進行處理序間通訊時,一定要注意考慮進程何時終止對通訊的影響。而通過系統V共用記憶體實現通訊的進程則不然。註:這裡沒有給出shmctl的使用範例,原理與訊息佇列大同小異。

回頁首

結論:

共用記憶體允許兩個或多個進程共用一給定的儲存區,因為資料不需要來回複製,所以是最快的一種處理序間通訊機制。共用記憶體可以通過mmap()映射普通檔案(特殊情況下還可以採用匿名映射)機制實現,也可以通過系統V共用記憶體機制實現。應用介面和原理很簡單,內部機制複雜。為了實現更安全通訊,往往還與號誌等同步機制共同使用。

共用記憶體涉及到了儲存管理以及檔案系統等方面的知識,深入理解其內部機制有一定的難度,關鍵還要緊緊抓住核心使用的重要資料結構。系統V共用記憶體是以檔案的形式組織在特殊檔案系統shm中的。通過shmget可以建立或獲得共用記憶體的標識符。取得共用記憶體標識符後,要通過shmat將這個記憶體區映射到本進程的虛擬位址空間。

posix共用記憶體

1、posix共用記憶體區涉及兩個步驟:

1)指定一個名字參數調用shm_open,以建立一個新的共用記憶體區對象或開啟一個以存在的共用記憶體區對象。

2)調用mmap把這個共用記憶體區映射到調用進程的地址空間。傳遞給shm_open的名字參數隨後由希望共用該記憶體區的任何其他進程使用。

2、函數

(1)建立

#include <sys/mman.h>

int shm_open(const char *name,int oflag,mode_t mode);

1)name    共用記憶體區的名字

2)oflag    標誌位

3)mode    許可權位,它指定O_CREAT標誌的前提下使用。

(2)刪除

int shm_unlink(const char *name);

(3)調整大小

    普通檔案或共用記憶體區對象的大小都可以通過調用ftruncate修改。

int ftruncate(int fd,off_t length);

(4)查看狀態

當開啟一個已存在的共用記憶體區對象時,我們可調用fstat來擷取有關該對象的資訊。

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

int stat(const char *file_name,struct stat *buf);

對於普通檔案stat結構可以獲得12個以上的成員資訊,然而當fd指代一個共用記憶體區對象時,只有四個成員含有資訊。

struct stat

{

mode_t st_mode;

uid_t st_uid;

gid_t st_gid;

off_t st_size;

};

g++ -g -o statt fstatt.cpp  -lrt

3、樣本

樣本1

View Code

#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>#include <unistd.h>int main(int argc,char **argv){    int shm_id;    struct stat buf;    char *ptr;    if(argc!=2)    {        printf("usage:shm_open <pathname>\n");        exit(1);    }    shm_id=shm_open(argv[1],O_RDWR|O_CREAT,0644);/*建立共用記憶體區*/    ftruncate(shm_id,100);/*修改共用區大小*/    fstat(shm_id,&buf);    ptr=mmap(NULL,buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_id,0);/*串連共用記憶體區*/    strcpy(ptr,"hello linux");/*寫入共用記憶體區*/    printf("%s\n",ptr);/*讀出共用記憶體區*/    shm_unlink(argv[1]);/*刪除共用記憶體區*/}
複製代碼

樣本2

pshm_w

#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>#include <unistd.h>#include <semaphore.h>#include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc,char **argv){    int shm_id;    char *ptr;    sem_t *sem;        if(argc != 2)    {        printf("usage:shm_open <pathname>\n");        exit(1);    }    shm_id = shm_open(argv[1], O_RDWR, 0644);    sem = sem_open(argv[1], O_RDWR);    ptr = (char*)mmap(NULL,100,PROT_READ | PROT_WRITE, MAP_SHARED, shm_id, 0);    while(1)    {         sem_wait(sem);         fgets(ptr, 15, stdin);         printf("user:%s", ptr);         if((strcmp(ptr, "q\n")) == 0)             exit(0);         sem_post(sem);         sleep(1);     }     return (0);}
複製代碼pshm_r

#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>#include <unistd.h>#include <semaphore.h>#include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc,char **argv){    int shm_id;    char *ptr;    sem_t *sem;        if(argc != 2)    {        printf("usage:shm_open <pathname>\n");        exit(1);    }    shm_id = shm_open(argv[1], O_RDWR | O_CREAT, 0644);    ftruncate(shm_id, 100);    sem = sem_open(argv[1], O_CREAT, 0644, 1);    ptr = (char*)mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, shm_id, 0);    strcpy(ptr, "\0"); //清空共用記憶體區    while(1)    {        if((strcmp(ptr, "\0")) ==0)        {            continue;        }            else        {             if((strcmp(ptr, "q\n"))==0)                break;             sem_wait(sem);             printf("server:%s", ptr);             strcpy(ptr, "\0");             sem_post(sem);         }         sem_unlink(argv[1]);         shm_unlink(argv[1]);     }
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.