一、共享內存的數據結構數據結構
共享內存就是分配一塊能被其餘進程訪問的內存。每一個共享內存段在內核中維護着一個內部結構:函數
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};spa
struct ipc_perm shm_perm; 操做許可,裏面包含共享內存的用戶ID、組ID
size_t shm_segsz;共享內存段的大小,以單位爲字節
time_t shm_atime; 最後一個進程訪問共享內存的時間
time_t shm_dtime;最後一個進程離開共享內存的時間
time_t shm_ctime; 最後一次修改共享內存的時間
pid_t shm_cpid; 建立共享內存的進程ID
pid_t shm_lpid; 最後操做共享內存的進程ID
shmatt_t shm_nattch; 當前使用該共享內存段的進程數量指針
二、共享內存的建立code
獲得一個共享內存標識符或建立一個共享內存對象並返回共享內存標識符對象
int shmget(key_t key, size_t size, int shmflg);blog
key:此值來源於ftok返回的IPC鍵值繼承
size:大於0的整數:新建的共享內存大小,以字節爲單位;只獲取共享內存時指定爲0隊列
shmflg:進程
0:取共享內存標識符,若不存在則函數會報錯
IPC_CREAT:當shmflg&IPC_CREAT爲真時,若是內核中不存在鍵值與key相等的共享內存,則新建一個共享內存;若是存在這樣的共享內存,返回此共享內存的標識符
IPC_CREAT|IPC_EXCL:若是內核中不存在鍵值與key相等的共享內存,則新建一個消息隊列;若是存在這樣的共享內存則報錯
三、共享內存的操做
在使用共享內存前,必須經過shmat函數將其附加到進程的地址空間。進程與共享內存就創建了鏈接。
shmat調用成功就會返回一個指向共享內存區的指針,使用該指針就能夠訪問共享內存區了。
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:共享內存標識符,爲shmget的返回值
shmflg:SHM_RDONLY:爲只讀模式,其餘爲讀寫模式
shmaddr:指定共享內存出如今進程內存地址的什麼位置。
若是爲空,則由內核選擇一個空閒的內存區,若是非空,返回地址取決於調用者是否給shmflg參數指定了SHM_RND值,若是沒有指定,
則共享內存區附加到shmaddr指定的地址;不然附加地址爲shmaddr向下舍入一個共享內存低端邊界地址後的地址(SHMLBA,一個常址)
一般將參數shmaddr設置爲NULL
-----------------------------------------------------------------------
當進程結束使用共享內存區時,要經過函數shmdt斷開與共享內存區的鏈接。
int shmdt(const void *shmaddr);
參數shmaddr爲shmat函數的返回值。該函數調用成功後,返回0,不然返回-1
進程脫離共享內存區後,數據結構shmid_ds 中的shm_nattch就會減1。可是共享內存依然存在,只有
shm_nattch爲0後,即沒有任何進程再使用該共享內存,共享內存纔會在內核中被刪除。
通常來講,當一個進程終止時,它所附加的共享內存都會自動脫離。
fork後子進程繼承已鏈接的共享內存地址。exec後該子進程與已鏈接的共享內存地址自動脫離(detach)。進程結束後,已鏈接的共享內存地址會自動脫離(detach)
四、共享內存的控制
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
shmid:共享內去標識符
buf:爲指向shmid_ds結構體的指針。
cmd:操做標誌位。
IPC_STAT:讀取共享內存的shmid_ds結構,並將其存儲到buf指定的地址中
IPC_SET:設置共享內存的shmid_ds結構
IPC_RMID:從系統中刪除由shmid標識的共享內存
五、共享內存的應用實例
經過讀寫者問題(不考慮優先級)來演示共享內存和信號量如何配合使用。這裏的讀者寫者問題要求一個進程讀共享內存的時候,其餘進程不能寫內存;當一個進程寫共享內存的時候,其餘進程不能讀
內存。
程序首先定義了一個包含共用函數的頭文件 sharemem.h
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; // int createsem(const char *pathname, int proj_id, int members, int init_val) { key_t key; int semid; union semun sem_opts; int index; if ((key=ftok(pathname, proj_id)) == -1) { perror("ftok error:"); return -1; } if ((semid=semget(key, members, IPC_CREAT|0666)) == -1) { perror("semget error:"); return -1; }
//初始化信號量 sem_opts.val = init_val; for(index=0;index<members;index++) { semctl(semid, index, SETVAL, sem_opts); } return semid; } // int opensem(const char *pathname, int proj_id) { key_t key; int semid; if ((key=ftok(pathname, proj_id)) == -1) { perror("ftok error:"); return -1; } if ((semid=semget(key, 0, IPC_CREAT|0660)) == -1) { perror("semget error:"); return -1; } return semid; } // int sem_p(int semid, int index) { struct sembuf buf = {0, -1, IPC_NOWAIT}; buf.sem_num = index; if (semop(semid, &buf, 1) == -1) { perror("semop error:"); return -1; } return 0; } // int sem_v(int semid, int index) { struct sembuf buf = {0, 1, IPC_NOWAIT}; buf.sem_num = index; if (semop(semid, &buf, 1) == -1) { perror("semop error:"); return -1; } return 0; } // int sem_delete(int semid) { semctl(semid, 0, IPC_RMID); } int sem_wait(int semid, int index) { while(semctl(semid, index, GETVAL) <= 0) { sleep(1); } return 1; } int createshm(const char *pathname, int proj_id, size_t size) { key_t key; int semid; if ((key=ftok(pathname, proj_id)) == -1) { perror("ftok error:"); return -1; } if ((semid=shmget(key, size, IPC_CREAT|0660)) == -1) { perror("shmget error:"); return -1; } return semid; }
writer和reader程序,兩個程序在進入共享內存以前,首先都檢查信號量的值是否爲1(至關因而否能進入共享內存區),若是不爲1,調用sleep進入休眠狀態,直到信號量的值變爲1.
write程序:
#include <sharemem.h> #include <string.h>
#define SHM_SIZE 256
int main() { int shmid; int semid; char *shmaddr; char write_str[SHM_SIZE]; shmid = createshm(".", 'm', SHM_SIZE); shmaddr = (char*)shmat(shmid, (char*)0, 0); semid = createsem(".", 's', 1, 1); while(1) { sem_wait(semid, 0); sem_p(semid, 0); printf("writer: "); fgets(write_str, SHM_SIZE, stdin); int len = strlen(write_str) - 1; write_str[len] = '\0'; strcpy(shmaddr, write_str); sleep(10); sem_v(semid, 0); sleep(10); } }
reader程序:
#include <sharemem.h>
#define SHM_SIZE 256
int main() { int shmid; int semid; char *shmaddr; char write_str[SHM_SIZE]; shmid = createshm(".", 'm', SHM_SIZE); shmaddr = (char*)shmat(shmid, (char*)0, 0); semid = opensem(".", 's'); while(1) { printf("reader: "); sem_wait(semid, 0); sem_p(semid, 0); printf("%s\n", shmaddr); sleep(10); sem_v(semid, 0); sleep(10); } }