|     本文關鍵字: linux 共享內存,linux 共享內存編程     可(ke)以(yi)(yi)說,共(gong)享內(nei)存是一(yi)種為(wei)(wei)高效(xiao)的(de)(de)進(jin)(jin)程(cheng)(cheng)間通信方(fang)式,因為(wei)(wei)進(jin)(jin)程(cheng)(cheng)可(ke)以(yi)(yi)直(zhi)接(jie)(jie)讀寫(xie)內(nei)存,不需要任何(he)數據的(de)(de)復制(zhi)。為(wei)(wei)了在多個進(jin)(jin)程(cheng)(cheng)間交換(huan)信息,內(nei)核(he)專(zhuan)門留出了一(yi)塊(kuai)內(nei)存區(qu)(qu),這段(duan)內(nei)存區(qu)(qu)可(ke)以(yi)(yi)由(you)需要訪(fang)問的(de)(de)進(jin)(jin)程(cheng)(cheng)將其(qi)映(ying)射(she)到自己的(de)(de)私有(you)地址空(kong)間。因此,進(jin)(jin)程(cheng)(cheng)就可(ke)以(yi)(yi)直(zhi)接(jie)(jie)讀寫(xie)這一(yi)內(nei)存區(qu)(qu)而(er)不需要進(jin)(jin)行數據的(de)(de)復制(zhi),從而(er)大大提高了效(xiao)率。當然,由(you)于多個進(jin)(jin)程(cheng)(cheng)共(gong)享一(yi)段(duan)內(nei)存,因此也需要依靠某種同步機制(zhi),如互斥鎖和(he)信號量等。其(qi)原理示(shi)意(yi)圖如圖1所示(shi)。  圖1  共享(xiang)內存原理示意圖
     共(gong)(gong)享內(nei)(nei)存(cun)(cun)的(de)(de)實現(xian)分(fen)為(wei)兩個步(bu)驟:第(di)(di)一步(bu)是創建共(gong)(gong)享內(nei)(nei)存(cun)(cun),這(zhe)(zhe)里(li)用(yong)到的(de)(de)函(han)數(shu)是shmget(),也就(jiu)是從(cong)內(nei)(nei)存(cun)(cun)中(zhong)獲(huo)得(de)一段(duan)共(gong)(gong)享內(nei)(nei)存(cun)(cun)區域;第(di)(di)二步(bu)是映(ying)射(she)共(gong)(gong)享內(nei)(nei)存(cun)(cun),也就(jiu)是把(ba)這(zhe)(zhe)段(duan)創建的(de)(de)共(gong)(gong)享內(nei)(nei)存(cun)(cun)映(ying)射(she)到具體的(de)(de)進程空間中(zhong),這(zhe)(zhe)里(li)使用(yong)的(de)(de)函(han)數(shu)是shmat()。到這(zhe)(zhe)里(li),就(jiu)可以使用(yong)這(zhe)(zhe)段(duan)共(gong)(gong)享內(nei)(nei)存(cun)(cun)了(le),也就(jiu)是可以使用(yong)不帶(dai)緩沖的(de)(de)I/O讀(du)寫命令對其進行操作(zuo)。除此之外,還有(you)撤銷映(ying)射(she)的(de)(de)操作(zuo),其函(han)數(shu)為(wei)shmdt()。這(zhe)(zhe)里(li)主要(yao)介紹這(zhe)(zhe)3個函(han)數(shu)。     表1 列(lie)舉(ju)了(le)shmget()函(han)數的(de)語法(fa)要點。 表1  shmget()函(han)數語法要點 
    
    | 所需頭文件 | #include <sys/types.h> #include <sys/ipc.h>
 #include <sys/shm.h>
 |  
    | 函數原型 | int shmget(key_t key, int size, int shmflg) |  
    | 函數傳入值 | key:共享內存的鍵值,多個進程可以通過它訪問同一個共享內存,其中有個特殊值IPC_PRIVATE,用于創建當前進程的私有共享內存 |  
    | size:共享內存區大小 |  
    | shmflg:同open()函數的權限位,也可以用八進制表示法 |  
    | 函數返回值 | 成功:共享內存段標識符 |  
    | 出錯:-1 |      表2列舉了shmat()函數的語法要點。 表2  shmat()函數語(yu)法(fa)要點 
    
    | 所需頭文件 | #include <sys/types.h> #include <sys/ipc.h>
 #include <sys/shm.h>
 |  
    | 函數原型 | char *shmat(int shmid, const void *shmaddr, int shmflg) |  
    | 函數傳入值 | shmid:要映射的共享內存區標識符 |  
    | shmaddr:將共享內存映射到指定地址(若為0則表示系統自動分配地址并把該段共享內存映射到調用進程的地址空間) |  
    | shmflg | SHM_RDONLY:共享內存只讀 |  
    | 默認0:共享內存可讀寫 |  
    | 函數返回值 | 成功:被映射的段地址 |  
    | 出錯:-1 |      表3列舉了shmdt()函數的語法要點。 表3  shmdt()函數(shu)語法要點 
    
    | 所需頭文件 | #include <sys/types.h> #include <sys/ipc.h>
 #include <sys/shm.h>
 |  
    | 函數原型 | int shmdt(const void *shmaddr) |  
    | 函數傳入值 | shmaddr:被映射的共享內存段地址 |  
    | 函數返回值 | 成功:0 |  
    | 出錯:-1 |      以(yi)下實例說明了如何使用(yong)(yong)基本的(de)(de)共(gong)享(xiang)內(nei)存(cun)函(han)數。首先創(chuang)(chuang)建一個(ge)共(gong)享(xiang)內(nei)存(cun)區(采用(yong)(yong)的(de)(de)共(gong)享(xiang)內(nei)存(cun)的(de)(de)鍵值為IPC_PRIVATE,是因(yin)為本實例中創(chuang)(chuang)建的(de)(de)共(gong)享(xiang)內(nei)存(cun)是父子(zi)進(jin)程間的(de)(de)共(gong)用(yong)(yong)部分),然后(hou)創(chuang)(chuang)建子(zi)進(jin)程,在父子(zi)兩個(ge)進(jin)程中將共(gong)享(xiang)內(nei)存(cun)分別(bie)映射到各自的(de)(de)進(jin)程地址空間中。     父(fu)(fu)進程(cheng)先等待用戶(hu)輸入(ru)(ru),然(ran)后(hou)將(jiang)用戶(hu)輸入(ru)(ru)的(de)(de)字(zi)(zi)符(fu)串寫(xie)入(ru)(ru)到(dao)共(gong)享內(nei)存,之(zhi)后(hou)向共(gong)享內(nei)存的(de)(de)頭部寫(xie)入(ru)(ru)“WROTE”字(zi)(zi)符(fu)串表示父(fu)(fu)進程(cheng)已(yi)成(cheng)功寫(xie)入(ru)(ru)數據。子(zi)進程(cheng)一直(zhi)等到(dao)共(gong)享內(nei)存的(de)(de)頭部字(zi)(zi)符(fu)串為“WROTE”,然(ran)后(hou)將(jiang)共(gong)享內(nei)存的(de)(de)有效數據(在(zai)父(fu)(fu)進程(cheng)中(zhong)用戶(hu)輸入(ru)(ru)的(de)(de)字(zi)(zi)符(fu)串)在(zai)屏幕上打印。父(fu)(fu)子(zi)兩個進程(cheng)在(zai)完成(cheng)以(yi)上工(gong)作(zuo)后(hou),分別(bie)解(jie)除與共(gong)享內(nei)存的(de)(de)映射關系。     后(hou)在子進程中刪除(chu)共享內存。因為共享內存自身并不提供同(tong)步(bu)機制,所以應額外實現不同(tong)進程間的(de)同(tong)步(bu)(如(ru)信號(hao)量)。為了(le)簡單起見(jian),在本實例中用標志(zhi)字(zi)符(fu)串來實現非常簡單的(de)父子進程間的(de)同(tong)步(bu)。     這(zhe)里(li)要介(jie)紹(shao)的(de)一(yi)個(ge)命(ming)令(ling)是ipcs,用于報告進(jin)程間通信機制(zhi)(zhi)狀態,它可以查(cha)看共享內存、消(xiao)息隊列等各種進(jin)程間通信機制(zhi)(zhi)的(de)情況,這(zhe)里(li)使用了system()函(han)數調用shell命(ming)令(ling)“ipcs”。程序源代碼如下:     /* shmem.c */#include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #define BUFFER_SIZE 2048
 
 int main()
 {
 pid_t pid;
 int shmid;
 char *shm_addr;
 char flag[] = "WROTE";
 char *buff;
 
 /* 創建共享內存 */
 if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
 {
 perror("shmget");
 exit(1);
 }
 else
 {
 printf("Create shared-memory: %d\n",shmid);
 }
 
 /* 顯示共享內存情況 */
 system("ipcs -m");
 
 pid = fork();
 if (pid == -1)
 {
 perror("fork");
 exit(1);
 }
 else if (pid == 0) /* 子進程處理 */
 {
 /* 映射共享內存 */
 if ((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
 {
 perror("Child: shmat");
 exit(1);
 }
 else
 {
 printf("Child: Attach shared-memory: %p\n", shm_addr);
 }
 system("ipcs -m");
 
 /* 通過檢查在共享內存的頭部是否有標志字符串“WROTE”來確認父進程已經向共享內存寫入有效數據 */
 while (strncmp(shm_addr, flag, strlen(flag)))
 {
 printf("Child: Wait for enable data...\n");
 sleep(5);
 }
 
 /* 獲取共享內存的有效數據并顯示 */
 strcpy(buff, shm_addr + strlen(flag));
 printf("Child: Shared-memory :%s\n", buff);
 
 /* 解除共享內存映射 */
 if ((shmdt(shm_addr)) < 0)
 {
 perror("shmdt");
 exit(1);
 }
 else
 {
 printf("Child: Deattach shared-memory\n");
 }
 system("ipcs -m");
 
 /* 刪除共享內存 */
 if (shmctl(shmid, IPC_RMID, NULL) == -1)
 {
 perror("Child: shmctl(IPC_RMID)\n");
 exit(1);
 }
 else
 {
 printf("Delete shared-memory\n");
 }
 
 system("ipcs -m");
 }
 else /* 父進程處理 */
 {
 /* 映射共享內存 */
 if ((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
 {
 perror("Parent: shmat");
 exit(1);
 }
 else
 {
 printf("Parent: Attach shared-memory: %p\n", shm_addr);
 }
 
 sleep(1);
 printf("\nInput some string:\n");
 fgets(buff, BUFFER_SIZE, stdin);
 strncpy(shm_addr + strlen(flag), buff, strlen(buff));
 strncpy(shm_addr, flag, strlen(flag));
 
 /* 解除共享內存映射 */
 if ((shmdt(shm_addr)) < 0)
 {
 perror("Parent: shmdt");
 exit(1);
 }
 else
 {
 printf("Parent: Deattach shared-memory\n");
 }
 system("ipcs -m");
 
 waitpid(pid, NULL, 0);
 printf("Finished\n");
 }
 
 exit(0);
 }
     下面是運行結(jie)果(guo),從該(gai)結(jie)果(guo)中可以(yi)看出,nattch的(de)(de)值(zhi)隨著共享內存狀(zhuang)態的(de)(de)變化而變化,共享內存的(de)(de)值(zhi)根據不同(tong)的(de)(de)系統會有所不同(tong)。     $ ./shmemCreate shared-memory: 753665
 /* 在剛創建共享內存時(尚未有任何地址映射)共享內存的情況 */
 ------ Shared Memory Segments --------
 key        shmid    owner    perms    bytes    nattch    status
 0x00000000 753665   david    666      2048      0
 
 Child: Attach shared-memory: 0xb7f59000 /* 共享內存的映射地址 */
 Parent: Attach shared-memory: 0xb7f59000
 /* 在父子進程中進行共享內存的地址映射后共享內存的情況 */
 ------ Shared Memory Segments --------
 key        shmid    owner    perms    bytes    nattch    status
 0x00000000 753665   david    666      2048      2
 
 Child: Wait for enable data...
 
 Input some string:
 Hello /* 用戶輸入字符串“Hello” */
 Parent: Deattach shared-memory
 /* 在父進程中解除共享內存的映射關系后共享內存的情況 */
 ------ Shared Memory Segments --------
 key        shmid    owner    perms    bytes    nattch    status
 0x00000000 753665   david    666      2048      1
 /* 在子進程中讀取共享內存的有效數據并打印 */
 Child: Shared-memory :hello
 
 Child: Deattach shared-memory
 /* 在子進程中解除共享內存的映射關系后共享內存的情況 */
 ------ Shared Memory Segments --------
 key        shmid    owner    perms    bytes    nattch    status
 0x00000000 753665   david    666      2048      0
 
 Delete shared-memory
 /* 在刪除共享內存后共享內存的情況 */
 ------ Shared Memory Segments --------
 key        shmid    owner    perms    bytes    nattch    status
 
 Finished
     本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》    熱點鏈接: 
         1、linux下的信號量2、linux下的信號處理實例
 3、信號處理函數signal()和信號集函數組
 4、信號捕捉函數alarm()和pause()
 5、信號發送函數kill()和raise()
 
 更多新聞>>  |