共享內存允許系統內兩個或多個進程共享同一塊內存空間,並且數據不用在客戶進程和服務器進程間復制,因此共享內存是通信速度最快的一種IPC。
實現的機制簡單描述如下:一個進程在系統中申請開辟了一塊共享內存空間,然后使用這個共享內存空間的各個進程分別打開這個共享內存空間,並將這個內存空間映射到自己的進程空間上,這樣各個進程就可以共同使用這個共享內存空間,就如同使用自己進程地址空間的內存一樣。
要實現共享內存空間,內核做了許多工作:比如給每個共享內存塊分發一個“身份證”、允許用戶進程將共享內存映射到各自的地址空間上、在進程提出申請以后將共享內存和進程地址空間脫離,並在適當的時候講共享內存刪除,讓其回到可以被創建的狀態。
用戶利用共享內存實現進程間的通信,實際上就是使用內核提供的服務完成對共享內存的建立、映射、脫離、刪除等。當建立並映射成功以后,進程間就能通過共享內存實現數據的交互。下面就分別介紹內核提供的服務:
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid,const void* shmaddr,int shmflg);
int shmdt(const void* shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmget 函數
shmget 函數實現共享內存的建立或者打開。當共享內存的鍵值key 尚未存在時,調用這個函數並且指定shmflg 參數為IPC_CREAT 可以創建一個大小為 size 的共享內存空間。假設key指定的共享內存已經存在,調用這個函數可以打開這個共享內存,但不會創建。鍵值的獲取可以利用 ftok(),該函數的使用在博主的另一篇文章里http://www.cnblogs.com/linzizhang/p/4544794.html中有介紹。
shmat 函數
該函數將一個共享內存空間映射到調用進程的地址空間上,並且返回在進程地址空間中的地址。用戶拿到改地址后就可以通過這個地址間接的訪問共享內存。shmid 參數就是shmget 函數的返回值,shmaddr 參數實際上是指出了共享內存映射到進程地址空間上的位置,但是我們一般不會指定這個地址,而是令其為NULL ,讓內核選擇一個合適的地址。shmflg 參數是配合着shmaddr 參數使用的,在shmaddr 為NULL時就變得沒有實際意義,因此通常指定為0。
shmdt 函數
這個函數將一個進程已經映射了的共享內存脫離進程地址空間。shmaddr 參數就是在進程地址空間的地址,實際就是shmat 函數的返回值。
shmctl 函數
此函數實際上有很多的功能,但是我們通長用它將一個已經創建了的共享內存刪除,所謂刪除實際就是將它放回可以被創建的共享內存隊列中。指定cmd 參數為IPC_RMID,就可以將shmid鍵值指定的共享內存刪除,而buf實際上是可以獲取這共享內存在內核中的狀態,如果不想了解可以指定為0。
共享內存通信使用到的系統服務就這么多,下面給出一個測試范例。該范例由兩個進程組成,進程write將鍵盤上輸入的字符串存儲到共享內存,read進程將共享內存中的數據讀出來,再西融入顯示器顯示。
write進程代碼:
#include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <sys/types.h> #include <string.h> #define TEXT_SZ 2048 struct shared_use_st { int written_by_you; char some_text[TEXT_SZ]; }; int main(int argc,char **argv) { key_t key; int shmid; struct shared_use_st *space; int running = 1; char buffer[TEXT_SZ]; //creat shared memory key = ftok("/home/application/shared_memory",2); shmid = shmget(key,sizeof(struct shared_use_st),IPC_CREAT); //attach share memory to space space = (struct shared_use_st *)shmat(shmid,NULL,0); //write datas to space while(running) { while(space->written_by_you==1) { sleep(1); } printf("Input strings:"); fgets(buffer,TEXT_SZ,stdin); strncpy(space->some_text,buffer,TEXT_SZ); space->written_by_you = 1; if(strncmp(buffer,"end",3)==0) { running = 0; } } //dis_attach to space shmdt((const void *)space); //distroy shared memory // shmctl(shmid,IPC_RMID); return 0; }
read進程代碼:
#include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <sys/types.h> #include <string.h> #define TEXT_SZ 2048 struct shared_use_st { int written_by_you; //標志用來標明write進程是否寫入了數據 written_by_you==1,寫入數據,且read進程還沒讀走 char some_text[TEXT_SZ]; }; int main() { key_t key; int shmid; struct shared_use_st *space; int running = 1; char buffer[TEXT_SZ]; //打開共享內存 key = ftok("/home/application/shared_memory",2); shmid = shmget(key,sizeof(struct shared_use_st),IPC_CREAT); //映射共享內存 space = (struct shared_use_st *)shmat(shmid,NULL,0); //讀取數據 while(running) { if(space->written_by_you==1) { strncpy(buffer,space->some_text,TEXT_SZ); space->written_by_you = 0; printf("Recieved string :%s",buffer); } if(strncmp(buffer,"end",3)==0) { running = 0; } } //斷開映射 shmdt((const void *)space); //刪除共享內存 shmctl(shmid,IPC_RMID,0); return 0; }
