摘自:https://blog.csdn.net/guoping16/article/details/6584058
共享內存函數由shmget、shmat、shmdt、shmctl四個函數組成。下面的表格列出了這四個函數的函數原型及其具體說明。
1. shmget函數原型
| shmget(得到一個共享內存標識符或創建一個共享內存對象) |
||
| 所需頭文件 |
#include <sys/ipc.h> #include <sys/shm.h> |
|
| 函數說明 |
得到一個共享內存標識符或創建一個共享內存對象並返回共享內存標識符 |
|
| 函數原型 |
int shmget(key_t key, size_t size, int shmflg) |
|
| 函數傳入值 |
key |
0(IPC_PRIVATE):會建立新共享內存對象 |
| 大於0的32位整數:視參數shmflg來確定操作。通常要求此值來源於ftok返回的IPC鍵值 |
||
| size |
大於0的整數:新建的共享內存大小,以字節為單位 |
|
| 0:只獲取共享內存時指定為0 |
||
| shmflg |
0:取共享內存標識符,若不存在則函數會報錯 |
|
| IPC_CREAT:當shmflg&IPC_CREAT為真時,如果內核中不存在鍵值與key相等的共享內存,則新建一個共享內存;如果存在這樣的共享內存,返回此共享內存的標識符 |
||
| IPC_CREAT|IPC_EXCL:如果內核中不存在鍵值與key相等的共享內存,則新建一個消息隊列;如果存在這樣的共享內存則報錯 |
||
| 函數返回值 |
成功:返回共享內存的標識符 |
|
| 出錯:-1,錯誤原因存於error中 |
||
| 附加說明 |
上述shmflg參數為模式標志參數,使用時需要與IPC對象存取權限(如0600)進行|運算來確定信號量集的存取權限 |
|
| 錯誤代碼 |
EINVAL:參數size小於SHMMIN或大於SHMMAX EEXIST:預建立key所指的共享內存,但已經存在 EIDRM:參數key所指的共享內存已經刪除 ENOSPC:超過了系統允許建立的共享內存的最大值(SHMALL) ENOENT:參數key所指的共享內存不存在,而參數shmflg未設IPC_CREAT位 EACCES:沒有權限 ENOMEM:核心內存不足 |
|
在Linux環境中,對開始申請的共享內存空間進行了初始化,初始值為0x00。
如果用shmget創建了一個新的消息隊列對象時,則shmid_ds結構成員變量的值設置如下:
shm_lpid、shm_nattach、shm_atime、shm_dtime設置為0。
msg_ctime設置為當前時間。
shm_segsz設成創建共享內存的大小。
shmflg的讀寫權限放在shm_perm.mode中。
shm_perm結構的uid和cuid成員被設置成當前進程的有效用戶ID,gid和cuid成員被設置成當前進程的有效組ID。
2. shmat函數原型
| shmat(把共享內存區對象映射到調用進程的地址空間) |
||
| 所需頭文件 |
#include <sys/types.h> #include <sys/shm.h> |
|
| 函數說明 |
連接共享內存標識符為shmid的共享內存,連接成功后把共享內存區對象映射到調用進程的地址空間,隨后可像本地空間一樣訪問 |
|
| 函數原型 |
void *shmat(int shmid, const void *shmaddr, int shmflg) |
|
| 函數傳入值 |
msqid |
共享內存標識符 |
| shmaddr |
指定共享內存出現在進程內存地址的什么位置,直接指定為NULL讓內核自己決定一個合適的地址位置 |
|
| shmflg |
SHM_RDONLY:為只讀模式,其他為讀寫模式 |
|
| 函數返回值 |
成功:附加好的共享內存地址 |
|
| 出錯:-1,錯誤原因存於error中 |
||
| 附加說明 |
fork后子進程繼承已連接的共享內存地址。exec后該子進程與已連接的共享內存地址自動脫離(detach)。進程結束后,已連接的共享內存地址會自動脫離(detach) |
|
| 錯誤代碼 |
EACCES:無權限以指定方式連接共享內存 EINVAL:無效的參數shmid或shmaddr ENOMEM:核心內存不足 |
|
3. shmdt函數原型
| shmat(斷開共享內存連接) |
|
| 所需頭文件 |
#include <sys/types.h> #include <sys/shm.h> |
| 函數說明 |
與shmat函數相反,是用來斷開與共享內存附加點的地址,禁止本進程訪問此片共享內存 |
| 函數原型 |
int shmdt(const void *shmaddr) |
| 函數傳入值 |
shmaddr:連接的共享內存的起始地址 |
| 函數返回值 |
成功:0 |
| 出錯:-1,錯誤原因存於error中 |
|
| 附加說明 |
本函數調用並不刪除所指定的共享內存區,而只是將先前用shmat函數連接(attach)好的共享內存脫離(detach)目前的進程 |
| 錯誤代碼 |
EINVAL:無效的參數shmaddr |
4. shmctl函數原型
| shmctl(共享內存管理) |
||
| 所需頭文件 |
#include <sys/types.h> #include <sys/shm.h> |
|
| 函數說明 |
完成對共享內存的控制 |
|
| 函數原型 |
int shmctl(int shmid, int cmd, struct shmid_ds *buf) |
|
| 函數傳入值 |
msqid |
共享內存標識符 |
| cmd |
IPC_STAT:得到共享內存的狀態,把共享內存的shmid_ds結構復制到buf中 |
|
| IPC_SET:改變共享內存的狀態,把buf所指的shmid_ds結構中的uid、gid、mode復制到共享內存的shmid_ds結構內 |
||
| IPC_RMID:刪除這片共享內存 |
||
| buf |
共享內存管理結構體。具體說明參見共享內存內核結構定義部分 |
|
| 函數返回值 |
成功:0 |
|
| 出錯:-1,錯誤原因存於error中 |
||
| 錯誤代碼 |
EACCESS:參數cmd為IPC_STAT,確無權限讀取該共享內存 EFAULT:參數buf指向無效的內存地址 EIDRM:標識符為msqid的共享內存已被刪除 EINVAL:無效的參數cmd或shmid EPERM:參數cmd為IPC_SET或IPC_RMID,卻無足夠的權限執行 |
|
共享內存應用范例
5. 父子進程通信范例
父子進程通信范例,shm.c源代碼如下:
1 #include <stdio.h> 2 3 #include <unistd.h> 4 5 #include <string.h> 6 7 #include <sys/ipc.h> 8 9 #include <sys/shm.h> 10 11 #include <error.h> 12 13 #define SIZE 1024 14 15 int main() 16 17 { 18 19 int shmid ; 20 21 char *shmaddr ; 22 23 struct shmid_ds buf ; 24 25 int flag = 0 ; 26 27 int pid ; 28 29 30 31 shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ; 32 33 if ( shmid < 0 ) 34 35 { 36 37 perror("get shm ipc_id error") ; 38 39 return -1 ; 40 41 } 42 43 pid = fork() ; 44 45 if ( pid == 0 ) 46 47 { 48 49 shmaddr = (char *)shmat( shmid, NULL, 0 ) ; 50 51 if ( (int)shmaddr == -1 ) 52 53 { 54 55 perror("shmat addr error") ; 56 57 return -1 ; 58 59 60 61 } 62 63 strcpy( shmaddr, "Hi, I am child process!\n") ; 64 65 shmdt( shmaddr ) ; 66 67 return 0; 68 69 } else if ( pid > 0) { 70 71 sleep(3 ) ; 72 73 flag = shmctl( shmid, IPC_STAT, &buf) ; 74 75 if ( flag == -1 ) 76 77 { 78 79 perror("shmctl shm error") ; 80 81 return -1 ; 82 83 } 84 85 86 87 printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ; 88 89 printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ; 90 91 printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ; 92 93 shmaddr = (char *) shmat(shmid, NULL, 0 ) ; 94 95 if ( (int)shmaddr == -1 ) 96 97 { 98 99 perror("shmat addr error") ; 100 101 return -1 ; 102 103 104 105 } 106 107 printf("%s", shmaddr) ; 108 109 shmdt( shmaddr ) ; 110 111 shmctl(shmid, IPC_RMID, NULL) ; 112 113 }else{ 114 115 perror("fork error") ; 116 117 shmctl(shmid, IPC_RMID, NULL) ; 118 119 } 120 121 122 123 return 0 ; 124 125 }
編譯 gcc shm.c –o shm。
執行 ./shm,執行結果如下:
shm_segsz =1024 bytes shm_cpid = 9503 shm_lpid = 9504 Hi, I am child process!
6. 多進程讀寫范例
多進程讀寫即一個進程寫共享內存,一個或多個進程讀共享內存。下面的例子實現的是一個進程寫共享內存,一個進程讀共享內存。
(1)下面程序實現了創建共享內存,並寫入消息。
shmwrite.c源代碼如下:
1 #include <stdio.h> 2 3 #include <sys/ipc.h> 4 5 #include <sys/shm.h> 6 7 #include <sys/types.h> 8 9 #include <unistd.h> 10 11 #include <string.h> 12 13 typedef struct{ 14 15 char name[8]; 16 17 int age; 18 19 } people; 20 21 int main(int argc, char** argv) 22 23 { 24 25 int shm_id,i; 26 27 key_t key; 28 29 char temp[8]; 30 31 people *p_map; 32 33 char pathname[30] ; 34 35 36 37 strcpy(pathname,"/tmp") ; 38 39 key = ftok(pathname,0x03); 40 41 if(key==-1) 42 43 { 44 45 perror("ftok error"); 46 47 return -1; 48 49 } 50 51 printf("key=%d\n",key) ; 52 53 shm_id=shmget(key,4096,IPC_CREAT|IPC_EXCL|0600); 54 55 if(shm_id==-1) 56 57 { 58 59 perror("shmget error"); 60 61 return -1; 62 63 } 64 65 printf("shm_id=%d\n", shm_id) ; 66 67 p_map=(people*)shmat(shm_id,NULL,0); 68 69 memset(temp, 0x00, sizeof(temp)) ; 70 71 strcpy(temp,"test") ; 72 73 temp[4]='0'; 74 75 for(i = 0;i<3;i++) 76 77 { 78 79 temp[4]+=1; 80 81 strncpy((p_map+i)->name,temp,5); 82 83 (p_map+i)->age=0+i; 84 85 } 86 87 shmdt(p_map) ; 88 89 return 0 ; 90 91 }
(2)下面程序實現從共享內存讀消息。
1 shmread.c源代碼如下: 2 3 #include <stdio.h> 4 5 #include <string.h> 6 7 #include <sys/ipc.h> 8 9 #include <sys/shm.h> 10 11 #include <sys/types.h> 12 13 #include <unistd.h> 14 15 typedef struct{ 16 17 char name[8]; 18 19 int age; 20 21 } people; 22 23 int main(int argc, char** argv) 24 25 { 26 27 int shm_id,i; 28 29 key_t key; 30 31 people *p_map; 32 33 char pathname[30] ; 34 35 36 37 strcpy(pathname,"/tmp") ; 38 39 key = ftok(pathname,0x03); 40 41 if(key == -1) 42 43 { 44 45 perror("ftok error"); 46 47 return -1; 48 49 } 50 51 printf("key=%d\n", key) ; 52 53 shm_id = shmget(key,0, 0); 54 55 if(shm_id == -1) 56 57 { 58 59 perror("shmget error"); 60 61 return -1; 62 63 } 64 65 printf("shm_id=%d\n", shm_id) ; 66 67 p_map = (people*)shmat(shm_id,NULL,0); 68 69 for(i = 0;i<3;i++) 70 71 { 72 73 printf( "name:%s\n",(*(p_map+i)).name ); 74 75 printf( "age %d\n",(*(p_map+i)).age ); 76 77 } 78 79 if(shmdt(p_map) == -1) 80 81 { 82 83 perror("detach error"); 84 85 return -1; 86 87 } 88 89 return 0 ; 90 91 }
(3)編譯與執行
① 編譯gcc shmwrite.c -o shmwrite。
② 執行./shmwrite,執行結果如下:
key=50453281 shm_id=688137
③ 編譯gcc shmread.c -o shmread。
④ 執行./shmread,執行結果如下:
key=50453281 shm_id=688137 name:test1 age 0 name:test2 age 1 name:test3 age 2
⑤ 再執行./shmwrite,執行結果如下:
key=50453281 shmget error: File exists
⑥ 使用ipcrm -m 688137刪除此共享內存。
摘錄自《深入淺出Linux工具與編程》
7、我的練習
write.c
1 #include <string.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/ipc.h> 5 #include <sys/types.h> 6 #include <sys/shm.h> 7 8 int shmget(key_t key, size_t size, int shmflg); 9 int shmctl(int shmid, int cmd, struct shmid_ds *buf); 10 void *shmat(int shmid, const void *shmaddr, int shmflg); 11 int shmdt(const void *shmaddr); 12 13 14 void print_buf(struct shmid_ds *buf) 15 { 16 printf("shm_segsz=%d==================\r\n", buf->shm_segsz); 17 printf("shm_cpid=%d\r\n", buf->shm_cpid); 18 printf("shm_lpid=%d\r\n", buf->shm_lpid); 19 printf("shm_atime=%d\r\n", buf->shm_atime); 20 printf("shm_dtime=%d\r\n", buf->shm_dtime); 21 printf("shm_ctime=%d\r\n", buf->shm_ctime); 22 printf("shm_nattch=%d\r\n", buf->shm_nattch); 23 printf("shm_perm.uid=%d\r\n", buf->shm_perm.uid); 24 printf("shm_perm.gid=%d\r\n", buf->shm_perm.gid); 25 printf("shm_perm.cuid=%d\r\n", buf->shm_perm.cuid); 26 printf("shm_perm.cgid=%d\r\n", buf->shm_perm.cgid); 27 printf("shm_perm.mode=%d\r\n", buf->shm_perm.mode); 28 printf("shm_perm.__seq=%d\r\n", buf->shm_perm.__seq); 29 printf("shm_perm.__pad1=%d\r\n", buf->shm_perm.__pad1); 30 printf("shm_perm.__pad2=%d\r\n", buf->shm_perm.__pad2); 31 printf("print======================================\r\n\r\n"); 32 } 33 34 35 int main(int argc ,char *argv[]) 36 { 37 int rt = 0; 38 int shmid = 0; 39 void *shmaddr = NULL; 40 struct shmid_ds buf; 41 42 memset((void *)&buf, 0, sizeof(struct shmid_ds)); 43 44 int k = ftok("/tmp", 0x03); 45 46 shmid = shmget(k, 1024, IPC_CREAT); 47 printf("shmget k=%p(%d),shmid=%p(%d)\r\n", k, k, shmid, shmid); 48 49 rt = shmctl(shmid, IPC_STAT, &buf); // get 50 printf("shmctl rt=%d\r\n", rt); 51 print_buf(&buf); 52 53 54 55 shmaddr = shmat(shmid, NULL, 0); 56 printf("shmat addr=%p\r\n", shmaddr); 57 58 rt = shmctl(shmid, IPC_STAT, &buf); // get 59 printf("shmctl rt=%d\r\n", rt); 60 print_buf(&buf); 61 62 63 strncpy(shmaddr, "GGGGGGGGGGGGGood!", 1024); 64 65 66 67 rt = shmdt(shmaddr); 68 printf("shmdt rt=%d\r\n", rt); 69 rt = shmctl(shmid, IPC_STAT, &buf); // get 70 printf("shmctl rt=%d\r\n", rt); 71 print_buf(&buf); 72 73 74 75 76 //rt = shmctl(shmid, IPC_RMID, NULL); // rm 77 //printf("shmctl [RMID] rt=%d\r\n", rt); 78 //print_buf(&buf); 79 return 0; 80 }
read.c
1 #include <string.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/ipc.h> 5 #include <sys/types.h> 6 #include <sys/shm.h> 7 8 int shmget(key_t key, size_t size, int shmflg); 9 int shmctl(int shmid, int cmd, struct shmid_ds *buf); 10 void *shmat(int shmid, const void *shmaddr, int shmflg); 11 int shmdt(const void *shmaddr); 12 13 14 void print_buf(struct shmid_ds *buf) 15 { 16 printf("shm_segsz=%d==================\r\n", buf->shm_segsz); 17 printf("shm_cpid=%d\r\n", buf->shm_cpid); 18 printf("shm_lpid=%d\r\n", buf->shm_lpid); 19 printf("shm_atime=%d\r\n", buf->shm_atime); 20 printf("shm_dtime=%d\r\n", buf->shm_dtime); 21 printf("shm_ctime=%d\r\n", buf->shm_ctime); 22 printf("shm_nattch=%d\r\n", buf->shm_nattch); 23 printf("shm_perm.uid=%d\r\n", buf->shm_perm.uid); 24 printf("shm_perm.gid=%d\r\n", buf->shm_perm.gid); 25 printf("shm_perm.cuid=%d\r\n", buf->shm_perm.cuid); 26 printf("shm_perm.cgid=%d\r\n", buf->shm_perm.cgid); 27 printf("shm_perm.mode=%d\r\n", buf->shm_perm.mode); 28 printf("shm_perm.__seq=%d\r\n", buf->shm_perm.__seq); 29 printf("shm_perm.__pad1=%d\r\n", buf->shm_perm.__pad1); 30 printf("shm_perm.__pad2=%d\r\n", buf->shm_perm.__pad2); 31 printf("print======================================\r\n\r\n"); 32 } 33 34 35 int main(int argc ,char *argv[]) 36 { 37 int rt = 0; 38 int shmid = 0; 39 void *shmaddr = NULL; 40 struct shmid_ds buf; 41 42 memset((void *)&buf, 0, sizeof(struct shmid_ds)); 43 44 int k = ftok("/tmp", 0x03); 45 46 shmid = 196634; 47 //shmid = shmget(k, 0, 0); 48 printf("shmget k=%p,shmid=%p\r\n", k,shmid); 49 50 rt = shmctl(shmid, IPC_STAT, &buf); // get 51 printf("shmctl rt=%d\r\n", rt); 52 print_buf(&buf); 53 54 55 56 shmaddr = shmat(shmid, NULL, 0); 57 printf("shmat addr=%p\r\n", shmaddr); 58 59 rt = shmctl(shmid, IPC_STAT, &buf); // get 60 printf("shmctl rt=%d\r\n", rt); 61 print_buf(&buf); 62 63 64 printf("[src]=%s\r\n", shmaddr); 65 66 67 rt = shmdt(shmaddr); 68 printf("shmdt rt=%d\r\n", rt); 69 rt = shmctl(shmid, IPC_STAT, &buf); // get 70 printf("shmctl rt=%d\r\n", rt); 71 print_buf(&buf); 72 73 74 75 76 //rt = shmctl(shmid, IPC_RMID, NULL); // rm 77 //printf("shmctl [RMID] rt=%d\r\n", rt); 78 //print_buf(&buf); 79 return 0; 80 }
