shmget
int shmget(key_t key, size_t size, int flag);
key: 標識符的規則
size:共享存儲段的字節數
flag:讀寫的權限
返回值:成功返回共享存儲的id,失敗返回-1
key_t key
-----------------------------------------------
??? key標識共享內存的鍵值: 0/IPC_PRIVATE。?當key的取值為IPC_PRIVATE,則函數shmget()將創建一塊新的共享內存;如果key的取值為0,而參數shmflg中設置了IPC_PRIVATE這個標志,則同樣將創建一塊新的共享內存。
??? 在IPC(InterProcess Communication)的通信模式下,不管是使用消息隊列還是共享內存,甚至是信號量,每個IPC的對象(object)都有唯一的名字,稱為“鍵”(key)。通過“鍵”,進程能夠識別所用的對象。“鍵”與IPC對象的關系就如同文件名稱之于文件,通過文件名,進程能夠讀寫文件內的數據,甚至多個進程能夠共用一個文件。而在IPC的通訊模式下,通過“鍵”的使用也使得一個IPC對象能為多個進程所共用。
??? Linux系統中的所有表示System V中IPC對象的數據結構都包括一個ipc_perm結構,其中包含有IPC對象的鍵值,該鍵用于查找System V中IPC對象的引用標識符。如果不使用“鍵”,進程將無法存取IPC對象,因為IPC對象并不存在于進程本身使用的內存中。
??? 通常,都希望自己的程序能和其他的程序預先約定一個唯一的鍵值,但實際上并不是總可能的成行的,因為自己的程序無法為一塊共享內存選擇一個鍵值。因此,在此把key設為IPC_PRIVATE,這樣,操作系統將忽略鍵,建立一個新的共享內存,指定一個鍵值,然后返回這塊共享內存IPC標識符ID。而將這個新的共享內存的標識符ID告訴其他進程可以在建立共享內存后通過派生子進程,或寫入文件或管道來實現。
int size(單位字節Byte)
-----------------------------------------------
??? size是要建立共享內存的長度。所有的內存分配操作都是以頁為單位的。所以如果一段進程只申請一塊只有一個字節的內存,內存也會分配整整一頁(在i386機器中一頁的缺省大小PACE_SIZE=4096字節)這樣,新創建的共享內存的大小實際上是從size這個參數調整而來的頁面大小。即如果size為1至4096,則實際申請到的共享內存大小為4K(一頁);4097到8192,則實際申請到的共享內存大小為8K(兩頁),依此類推。
int shmflg
-----------------------------------------------
??? shmflg主要和一些標志有關。其中有效的包括IPC_CREAT和IPC_EXCL,它們的功能與open()的O_CREAT和O_EXCL相當。
????IPC_CREAT?? 如果共享內存不存在,則創建一個共享內存,否則打開操作。
????IPC_EXCL??? 只有在共享內存不存在的時候,新的共享內存才建立,否則就產生錯誤。
??? 如果單獨使用IPC_CREAT,shmget()函數要么返回一個已經存在的共享內存的操作符,要么返回一個新建的共享內存的標識符。如果將IPC_CREAT和IPC_EXCL標志一起使用,shmget()將返回一個新建的共享內存的標識符;如果該共享內存已存在,或者返回-1。IPC_EXEL標志本身并沒有太大的意義,但是和IPC_CREAT標志一起使用可以用來保證所得的對象是新建的,而不是打開已有的對象。對于用戶的讀取和寫入許可指定SHM_R和SHM_W,(SHM_R>3)和(SHM_W>3)是一組讀取和寫入許可,而(SHM_R>6)和(SHM_W>6)是全局讀取和寫入許可。
需要注意的是,使用參數要加上 | 0666 作為校驗,在有些Linux系統中,如果不加此校驗,則不能順利獲取共享空間的值(如Ubuntu)。此外,有兩個常用參數,一般要同時出現,他們是:S_IRUSH | S_IWUSR 。由于這兩個參數非常常用,程序員一般做這樣的操作
#define PERM S_IRUSR | S_IWUSR | IPC_CREAT
這樣一來,第三個參數就可以直接用PERM來表示了!
返回值
-----------------------------------------------
成功返回共享內存的標識符;不成功返回-1,errno儲存錯誤原因。
??? EINVAL??????? 參數size小于SHMMIN或大于SHMMAX。
??? EEXIST??????? 預建立key所致的共享內存,但已經存在。
??? EIDRM???????? 參數key所致的共享內存已經刪除。
??? ENOSPC??????? 超過了系統允許建立的共享內存的最大值(SHMALL )。
??? ENOENT??????? 參數key所指的共享內存不存在,參數shmflg也未設IPC_CREAT位。
??? EACCES??????? 沒有權限。
??? ENOMEM??????? 核心內存不足。
struct shmid_ds
-----------------------------------------------
??? shmid_ds數據結構表示每個新建的共享內存。當shmget()創建了一塊新的共享內存后,返回一個可以用于引用該共享內存的shmid_ds數據結構的標識符。
include/linux/shm.h
??? struct shmid_ds {?
??????? struct ipc_perm??? shm_perm;????? /* operation perms */?
??????? int??????????????? shm_segsz;???? /* size of segment (bytes) */?
??????? __kernel_time_t??? shm_atime;???? /* last attach time */?
??????? __kernel_time_t??? shm_dtime;???? /* last detach time */?
??????? __kernel_time_t??? shm_ctime;???? /* last change time */?
??????? __kernel_ipc_pid_t shm_cpid;????? /* pid of creator */?
??????? __kernel_ipc_pid_t shm_lpid;????? /* pid of last operator */?
??????? unsigned short???? shm_nattch;??? /* no. of current attaches */?
??????? unsigned short???? shm_unused;??? /* compatibility */?
??????? void?????????????? *shm_unused2; /* ditto - used by DIPC */?
??????? void?????????????? *shm_unused3; /* unused */?
??? };
struct ipc_perm
-----------------------------------------------
??? 對于每個IPC對象,系統共用一個struct ipc_perm的數據結構來存放權限信息,以確定一個ipc操作是否可以訪問該IPC對象。
??? struct ipc_perm {?
??????? __kernel_key_t?? key;?
??????? __kernel_uid_t?? uid;?
??????? __kernel_gid_t?? gid;?
??????? __kernel_uid_t?? cuid;?
??????? __kernel_gid_t?? cgid;?
??????? __kernel_mode_t mode;?
??????? unsigned short?? seq;?
};
//----------------------------------------
shmat
void *shmat(int shmid, const void *addr, int flag);
shmid:共享存儲的id
addr:一般為0,表示連接到由內核選擇的第一個可用地址上,否則,如果flag沒有指定SHM_RND,則連接到addr所指定的地址上,如果flag為SHM_RND,則地址取整
flag:如前所述,一般為0
返回值:如果成功,返回共享存儲段地址,出錯返回-1
共享存儲器的執行方式是將一個儲存器區段標記為共用,這時各進程可以把這個區段映射到該進程本身的虛擬地址里。建立共享存儲器可通過shmget系統調用,shmget執行后,核心程序就保留一塊指定大小的空間,同時關于此共享存儲器的一切數據,如區段的長度,區段的存取權,區段建立者的進程識別碼等存入一個叫shmid_ds的結構。現在共享存儲器雖然已經建立了,可是仍無法連上它,這時就須通過shmat系統調用得到一個指向共享存儲器基址的指針,通過此指針,就可以如同于操作一般存儲器似的取用共享存儲器。shmdt進行相反的工作,用來脫離已連上的共享存儲器。
shmdt
int shmdt(void *addr);
addr:共享存儲段的地址,以前調用shmat時的返回值
shmdt將使相關shmid_ds結構中的shm_nattch計數器值減1
當一個進程不再需要共享內存段時,它將調用shmdt()系統調用取消這個段,但是,這并不是從內核真正地刪除這個段,而是把相關shmid_ds結構的?shm_nattch域的值減1,當這個值為0時,內核才從物理上刪除這個共享段
shmctl
int shmctl(int shmid,int cmd,struct shmid_ds *buf)
shmid:共享存儲段的id
cmd:一些命令
IPC_STAT 得到共享內存的狀態
????????IPC_SET 改變共享內存的狀態
????????IPC_RMID 刪除共享內存?
IPC_RMID?命令實際上不從內核刪除一個段,而是僅僅把這個段標記為刪除,實際的刪除發生在最后一個進程離開這個共享段時。?
請注意,共享內存不會隨著程序結束而自動消除,要么調用shmctl刪除,要么自己用手敲命令去刪除,否則永遠留在系統中。
?
實例:
?
- #include?<stdio.h>??
- #include?<string.h>??
- #include?<stdlib.h>??
- #include?<errno.h>??
- #include?<unistd.h>??
- #include?<sys/stat.h>??
- #include?<sys/types.h>??
- #include?<sys/ipc.h>??
- #include?<sys/shm.h>??
- #define?PERM?S_IRUSR|S_IWUSR??
- ??
- int?main(int?argc,char?**argv){??
- ????int?shmid;??
- ????char?*p_addr,*c_addr;??
- ????if(argc!=2){??
- ????????fprintf(stderr,"Usage:%s\n\a",argv[0]);??
- ????????exit(1);??
- ????}??
- ??
- ????if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1){??
- ????????fprintf(stderr,"Create?Share?Memory?Error:%s\n\a",strerror(errno));??
- ????????exit(1);??
- ????}??
- ??
- ????if(fork()){??
- ????????p_addr=shmat(shmid,0,0);??
- ????????memset(p_addr,'\0',1024);??
- ????????strncpy(p_addr,argv[1],1024);??
- ????????exit(0);??
- ????}else{??
- ????????c_addr=shmat(shmid,0,0);??
- ????????printf("Client?get?%s",c_addr);??
- ????????exit(0);??
- ????}??
- }??
?這個程序是父進程將參數寫入到共享內存,然后子進程把內容讀出來.最后我們要使用ip
crm 釋放資源的.先用ipcs 找出ID 然后用ipcrm shm ID 刪除.