共用記憶體是運行在同一台機器上的處理序間通訊最快的方式,因為資料不需要在不同的進程間複製。通常由一個進程建立一塊共用記憶體區,其餘進程對這塊記憶體區進行讀寫。共用記憶體往往與其它通訊機制,如訊號量結合使用,來達到進程間的同步及互斥。
首先要用的函數是shmget,它獲得一個共用儲存標識符。
#i nclude <sys/types.h>
#i nclude <sys/ipc.h>
#i nclude <sys/shm.h>
int shmget(key_t key, int size, int flag);
這個函數有點類似大家熟悉的malloc函數,系統按照請求分配size大小的記憶體用作共用記憶體。Linux系統核心中每個IPC結構都有的一個非負整數的標識符,這樣對一個訊息佇列發送訊息時只要引用標識符就可以了。這個標識符是核心由IPC結構的關鍵字得到的,這個關鍵字,就是上面第一個函數的key。資料類型key_t是在標頭檔sys/types.h中定義的,它是一個長整形的資料。在我們後面的章節中,還會碰到這個關鍵字。
當共用記憶體建立後,其餘進程可以調用shmat()將其串連到自身的地址空間中。
void *shmat(int shmid, void *addr, int flag);
shmid為shmget函數返回的共用儲存標識符,addr和flag參數決定了以什麼方式來確定串連的地址,函數的傳回值即是該進程資料區段所串連的實際地址,進程可以對此進程進行讀寫操作。
使用共用儲存來實現處理序間通訊的注意點是對資料存取的同步,必須確保當一個進程去讀取資料時,它所想要的資料已經寫好了。通常,訊號量被要來實現對共用儲存資料存取的同步,另外,可以通過使用shmctl函數設定共用儲存記憶體的某些標誌位如SHM_LOCK、SHM_UNLOCK等來實現。
共用記憶體機制使得兩個不相關進程可以讀取和修改同一段記憶體從而實現資料共用。主要函數定義:
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflag);
C代碼
- /*server.c:向共用記憶體中寫入People*/
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
-
- int main()
- {
- struct People{
- char name[10];
- int age;
- };
-
- int semid;
- int shmid;
- key_t semkey;
- key_t shmkey;
- semkey=ftok("server.c",0);
- shmkey=ftok("client.c",0);
-
- /*建立共用記憶體和訊號量的IPC*/
- semid=semget(semkey,1,0666|IPC_CREAT);
- if(semid==-1)
- printf("creat sem is fail\n");
- shmid=shmget(shmkey,1024,0666|IPC_CREAT);
- if(shmid==-1)
- printf("creat shm is fail\n");
-
- /*設定訊號量的初始值,就是資源個數*/
- union semun{
- int val;
- struct semid_ds *buf;
- ushort *array;
- }sem_u;
-
- sem_u.val=1;
- semctl(semid,0,SETVAL,sem_u);
-
- /*將共用記憶體映射到當前進程的地址中,之後直接對進程中的地址addr操作就是對共用記憶體操作*/
-
- struct People * addr;
- addr=(struct People*)shmat(shmid,0,0);
- if(addr==(struct People*)-1)
- printf("shm shmat is fail\n");
-
- /*訊號量的P操作*/
- void p()
- {
- struct sembuf sem_p;
- sem_p.sem_num=0;
- sem_p.sem_op=-1;
- if(semop(semid,&sem_p,1)==-1)
- printf("p operation is fail\n");
- }
-
- /*訊號量的V操作*/
- void v()
- {
- struct sembuf sem_v;
- sem_v.sem_num=0;
- sem_v.sem_op=1;
- if(semop(semid,&sem_v,1)==-1)
- printf("v operation is fail\n");
- }
-
- /*向共用記憶體寫入資料*/
- p();
- strcpy((*addr).name,"xiaoming");
- /*注意:①此處只能給指標指向的地址直接賦值,不能在定義一個 struct People people_1;addr=&people_1;因為addr在addr=(struct People*)shmat(shmid,0,0);時,已經由系統自動分配了一個地址,這個地址與共用記憶體相關聯,所以不能改變這個指標的指向,否則他將不指向共用記憶體,無法完成通訊了。
- 注意:②給字元數組賦值的方法。剛才太虎了。。*/
- (*addr).age=10;
- v();
-
- /*將共用記憶體與當前進程斷開*/
- if(shmdt(addr)==-1)
- printf("shmdt is fail\n");
-
- }
C代碼
- /*client.c:從共用記憶體中讀出People*/
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
-
- int main()
- {
- int semid;
- int shmid;
- key_t semkey;
- key_t shmkey;
- semkey=ftok("server.c",0);
- shmkey=ftok("client.c",0);
-
- struct People{
- char name[10];
- int age;
- };
-
- /*讀取共用記憶體和訊號量的IPC*/
- semid=semget(semkey,0,0666);
- if(semid==-1)
- printf("creat sem is fail\n");
- shmid=shmget(shmkey,0,0666);
- if(shmid==-1)
- printf("creat shm is fail\n");
-
- /*將共用記憶體映射到當前進程的地址中,之後直接對進程中的地址addr操作就是對共用記憶體操作*/
- struct People * addr;
- addr=(struct People*)shmat(shmid,0,0);
- if(addr==(struct People*)-1)
- printf("shm shmat is fail\n");
-
- /*訊號量的P操作*/
- void p()
- {
- struct sembuf sem_p;
- sem_p.sem_num=0;
- sem_p.sem_op=-1;
- if(semop(semid,&sem_p,1)==-1)
- printf("p operation is fail\n");
- }
-
- /*訊號量的V操作*/
- void v()
- {
- struct sembuf sem_v;
- sem_v.sem_num=0;
- sem_v.sem_op=1;
- if(semop(semid,&sem_v,1)==-1)
- printf("v operation is fail\n");
- }
-
- /*從共用記憶體讀出資料*/
- p();
- printf("name:%s\n",addr->name);
- printf("age:%d\n",addr->age);
- v();
-
- /*將共用記憶體與當前進程斷開*/
- if(shmdt(addr)==-1)
- printf("shmdt is fail\n");
-
- /*IPC必須顯示刪除。否則會一直留存在系統中*/
- if(semctl(semid,0,IPC_RMID,0)==-1)
- printf("semctl delete error\n");
- if(shmctl(shmid,IPC_RMID,NULL)==-1)
- printf("shmctl delete error\n");
- }