(一)簡單概念數據結構
共享內存做爲一種進程間通訊的方式,其相較於其餘進程間通訊方式而言最大的優勢就是數據傳輸速率快。其內部實現的方式採用了Linux進程地址空間中的mmap文件映射區,將文件內容直接映射到各自進程的進程地址空間中,進程對各自進程地址空間的訪問便可多線程
完成數據通訊,因爲直接讀取內存的方式,故其效率遠遠快於其餘IPC方法,這是共享內存的一大優點。但對於共享內存來講,保證數據同步問題是一個難點,在通常狀況下能夠採用管道的方式,本節內容的實例代碼採用了互斥鎖機制。函數
(二)System V共享內存API函數spa
(1)int shmget(key_t key, ssize_t size, int oflag)操作系統
功能:用於建立共享內存區域。線程
參數:code
key:共享內存的名字,用於惟一肯定一個共享內存;blog
size:建立的共享內存的大小;進程
oflag:共享內存的讀寫權限值集合,與文件建立中的mode標誌是同樣的。一樣還能夠與IPC_CREAT,IPC_EXCL等標誌聯合使用。內存
返回:函數返回共享內存區的標識符。
(2)void *shmat(int shmid, const void *shmaddr, int flag)
功能:將一個建立好的共享內存區附接到進程對應的進程地址空間中。
參數:
shmid:shmget函數的返回值;
shmaddr:將共享內存附接道shmaddr指定的進程地址空間的對應地址上。若是設置爲NULL,將由系統指定合法的區域將共享內存映射,這是推薦的作法;
flag:兩個可能取值爲SHM_RND和SHM_RDONLY。
返回:函數返回映射區的起始地址。
(3)int shmdt(const void *shmaddr)
功能:將共享內存從該進程地址空間中脫離。
參數:
shmaddr:shmat函數的返回值。
返回:若是成功,函數返回0,出錯返回-1。
注意:該函數只是將共享內存從進程的地址空間中脫離,不會刪除該共享內存區域。而且當一個進程終止時,它當前附接的全部共享內存區都將會自動脫離。
(4)int shmctl(int shmid, int cmd, struct shmid_ds *buf)
功能:提供對共享內存區的多種操做
參數:
shmid:shmget函數的返回值;
cmd:具體操做。取值有1)IPC_RMID:從系統中刪除shmid標識的共享內存區並拆除;2)IPC_SET:在進程有足夠權限的前提下,把共享內存的當前關聯值設置爲shmid_ds數據結構中給出的值;3)IPC_START:向調用者返回指定的共享內存區當前的shmid_ds結構。
返回:成功返回0,出錯返回-1。
(三)採用互斥鎖與共享內存的進程間通訊方式
因爲共享內存須要解決的一個問題就是數據同步,進程間的同步方式採用信號量能夠完成,但我考慮可否採用互斥鎖的方式。這裏採用互斥鎖最初的疑惑爲:兩個沒有近親關係的進程,如何可以獲取到同一個互斥鎖(通常互斥鎖用於線程間同步的時候比較多,這裏也說回來,線程之間原本就是共享數據,因此只須要解決數據同步問題便可,故尚未據說過用共享內存來解決線程通訊的 - -。)。
這裏來看下,若是設置互斥鎖pthread_mutex_t,來使得不一樣進程能夠獲取到同一個互斥鎖。
對於一個互斥鎖pthread_mutex_t來講,有兩種方式進行初始化,一種是靜態分配,一種是動態初始化。
1)利用宏PTHREAD_MUTEX_INITALIZER來初始化靜態分配的互斥鎖
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER
2)利用pthread_mutex_init來動態初始化互斥鎖
函數原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
這裏第一個參數mutex很好理解,就是定義的須要初始化的互斥鎖。這裏的第二個參數mutexattr表明什麼?Linux下互斥鎖具備一系列的屬性,其中pthread_mutexattr_t結構體定義了一套完整的互斥鎖屬性。兩種經常使用的屬性是pshared和type。其中pshared屬性指定了是否容許跨進程共享互斥鎖,可選值爲:1)PTHREAD_PROCESS_SHARED,互斥鎖能夠被跨進程共享;2)PTHREAD_PROCESS_PRIVATE,互斥鎖只能隸屬於一個進程,默認屬性。
咱們能夠經過設置互斥鎖自身屬性,來達到跨進程共享互斥鎖的方法。
結合實例代碼來進行分析:
//shmat.h #ifndef __SHMAT_H__ #define __SHMAT_H__ #include <pthread.h> #include <string.h> #include <sys/shm.h> #include <stdlib.h> #define SM_BUF_SIZE 1024 #define SM_ID 0x1234 struct shmat_msg { int flag; pthread_mutex_t shmat_mutex; char buf[SM_BUF_SIZE]; }; #endif
頭文件聲明瞭共享內存結構體,其中shmat_mutex用於保證對共享內存的互斥訪問。
//shmat-read.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> #include "shmat.h" int main(void) { int running = 1; int shm_id, ret; void *shared_memory = NULL; struct shmat_msg *sm_msg = NULL; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); //設置跨進程屬性 shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666 | IPC_CREAT); //建立共享內存,返回共享內存標識符 if (shm_id < 0) { perror("fail to shmget\n"); exit(1); } shared_memory = shmat(shm_id, NULL, 0); //映射到進程虛擬地址空間 if (shared_memory == NULL) { perror("fail to shmat\n"); exit(1); } sm_msg = (struct shmat_msg*)shared_memory; //轉型爲shmat_msg數據結構 sm_msg->flag = 0; pthread_mutex_init(&sm_msg->shmat_mutex, &attr); //初始化共享內存的互斥鎖 while (running) { pthread_mutex_lock(&sm_msg->shmat_mutex); //互斥訪問 if (sm_msg->flag) { printf("read message : %s\n", sm_msg->buf); sm_msg->flag = 0; if (strncmp(sm_msg->buf, "exit", 4) == 0) running = 0; pthread_mutex_unlock(&sm_msg->shmat_mutex); } else { printf("no data to read, waiting\n"); pthread_mutex_unlock(&sm_msg->shmat_mutex); sleep(2); } } ret = shmdt(shared_memory); //脫離共享內存的映射 if (ret < 0) { perror("failed to shmdt\n"); exit(1); } if (shmctl(shm_id, IPC_RMID, 0) < 0) //刪除共享內存 { perror("failed to shmctl\n"); exit(1); } return 0; }
讀文件。設置了共享內存互斥鎖爲跨進程屬性。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> #include "shmat.h" int main(void) { int running = 1; int shm_id, ret; void *shared_memory = NULL; struct shmat_msg *sm_msg = NULL; shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666); //獲取共享內存 if (shm_id < 0) { perror("failed to shmget\n"); exit(1); } shared_memory = shmat(shm_id, NULL, 0); //映射 if (shared_memory == NULL) { perror("failed to shmat\n"); exit(1); } sm_msg = (struct shmat_msg*)shared_memory; char buff[100]; while (running) { pthread_mutex_lock(&sm_msg->shmat_mutex); //獲取共享內存的互斥鎖 if (sm_msg->flag == 0) { fgets(buff, 100, stdin); printf("write sm_msg : %s\n", buff); strncpy(sm_msg->buf, buff, sizeof(buff)); sm_msg->flag = 1; if (strncmp(sm_msg->buf, "exit", 4) == 0) running = 0; pthread_mutex_unlock(&sm_msg->shmat_mutex); } else { printf("sm_msg waiting read\n"); pthread_mutex_unlock(&sm_msg->shmat_mutex); sleep(2); } } ret = shmdt(shared_memory); if (ret < 0) { perror("failed to shmdt\n"); exit(1); } ret = shmctl(shm_id, IPC_RMID, 0); if (ret < 0) { perror("failed to shmctl\n"); exit(1); } return 0; }
寫文件。
程序運行結果:
其中shmat-write程序運行後會阻塞在fgets等待用戶輸入。其中shmat-read與shmat-write兩個程序搶佔lock的時機是不肯定的,可能同一個程序會屢次搶佔互斥鎖,但因爲沒有內容可讀或可寫,則睡眠等待。
unlock解鎖後,操做系統下次調度哪一個進程加鎖是不必定的,若是須要在知足必定條件後才被調度能夠採用條件變量(但通常都是在多線程環境下了)。