天天看點

細說linux IPC(五):system V共享記憶體

system V共享記憶體和posix共享記憶體類似,system V共享記憶體是調用shmget函數和shamat函數。   

        shmget函數建立共享記憶體區,或者通路一個存在的記憶體區,類似系統調用共享記憶體的open和posix共享記憶體shm_open函數。shmget函數原型為:

  1. #include <sys/ipc.h>  
  2. #include <sys/shm.h>  
  3. int shmget(key_t key, size_t size, int shmflg);  

        key: 函數ftok傳回值,或者IPC_PRIVATE ,當使用IPC_PRIVATE時,最好兩個程序空間是共享的,比如父子程序,否則目前程序産生的共享記憶體辨別(傳回值),在另一個程序裡面不易得到;

        ftok函數原型為:key_t ftok(const char *pathname, int proj_id); 參數pathname為檔案絕對路徑名,proj_id為一個整型辨別符,該函數将一個已存在的的路徑名和一個整型辨別符轉化成一個key_t值(傳回 值),稱為IPC鍵。

        size:建立新的共享記憶體大小,當建立一片新的共享記憶體時,該值為不為0的參數。如果是讀取一片共享記憶體,該值可以為0。

        shmflg:讀寫權限值組合。IPC_CREAT(建立新的共享記憶體)或IPC_CREAT|IPC_EXCL(當将要建立的共享記憶體已經存在時,再試 圖建立将傳回EEXIST)。其實IPC_CREAT和IPC_EXCL的組合和open函數的O_CREAT和O_EXCL組合類似。

        函數傳回共享記憶體區的辨別。shmxxx函數操作共享記憶體将使用該函數傳回值。該函數類似posix共享記憶體shm_open函數功能。

        當shmget建立或打開一個共享記憶體區後,需要使用函數shmat來将該片共享記憶體連接配接到目前程序空間中來,當某一程序使用完共享記憶體後,使用函數shmdt斷開和共享記憶體的連結。

  1. #include <sys/types.h>  
  2. #include <sys/shm.h>  
  3. void *shmat(int shmid, const void *shmaddr, int shmflg);  
  4. int shmdt(const void *shmaddr);    

        shmid:是函數shmget函數傳回的共享記憶體辨別符。

        shmaddr: 連接配接到調用程序位址空間的位址,如果該參數為NULL,系統選擇一個合适位址;如果shmaddr非空并且shmflg指定了選項SHM_RND,那麼相 應的共享記憶體連結到由shmaddr參數指定的位址向下舍入一個SHMLAB常值。如果shmaddr非空并且shmflg未指定SHM_RND,共享内 存位址連結到shmaddr參數指定的位址。

        shmflg:可以指定SHM_RND和SHM_RDONLY(隻讀),如果指定SHM_RDONLY選項,那麼調用程序對該片共享記憶體隻有讀權限,否則,程序對該片記憶體将有讀寫權限。

        函數shmdt不會删除指定的共享記憶體,它隻是斷開和該片共享記憶體的連結而已。當一個程序終止後,該程序連結的共享記憶體将自動斷開。

        shmat函數成功傳回目前程序共享記憶體位址,失敗傳回(void *)-1;shmdt成功傳回0,失敗傳回-1;

        删除共享記憶體需要函數shmctl調用IPC_RMID指令來完成。

  1. #include <sys/ipc.h>  
  2. #include <sys/shm.h>  
  3. int shmctl(int shmid, int cmd, struct shmid_ds *buf);  

        shmid:共享記憶體區辨別;

        cmd:對共享記憶體的操作指令,指令IPC_RMID銷毀(destroy)一片共享記憶體,銷毀之後所有shmat,shmdt,shmctl對該片記憶體 操作都将失效,銷毀該共享記憶體要等到該共享記憶體引用計數變為0才進行;IPC_SET指令設定shmid_ds結構成員;IPC_STAT傳回目前共享内 存結構;其餘指令檢視man手冊。

        buf:為指向shmid_ds資料結構;

system V 共享記憶體示例:

server process:

  1. int sln_shm_get(char *shm_file, void **mem, int mem_len)  
  2. {  
  3.     int shmid;  
  4.     key_t key;  
  5.     if (NULL == fopen(shm_file, "w+")) {  
  6.         printf("fopen: %s\n", strerror(errno));  
  7.         return -1;  
  8.     }  
  9.     key = ftok(shm_file, 0);  
  10.     if (key < 0) {  
  11.         printf("ftok: %s\n", strerror(errno));  
  12.         return -1;  
  13.     }  
  14.     shmid = shmget(key, mem_len, IPC_CREAT);  
  15.     if (shmid < 0) {  
  16.         printf("shmget: %s\n", strerror(errno));  
  17.         return -1;  
  18.     }  
  19.     *mem = (void *)shmat(shmid, NULL, 0);  
  20.     if ((void *)-1 == *mem) {  
  21.         printf("shmat: %s\n", strerror(errno));  
  22.         return -1;  
  23.     }  
  24.     return shmid;  
  25. }  
  26. int main(int argc, const char *argv[])  
  27. {  
  28.     char *shm_file = NULL;  
  29.     char *shm_buf = NULL;  
  30.     int shmid;  
  31.     shmid = sln_shm_get(SHM_IPC_FILENAME, (void **)&shm_buf, SHM_IPC_MAX_LEN);  
  32.     if (shmid < 0) {  
  33.         return -1;  
  34.     }  
  35.     snprintf(shm_buf, SHM_IPC_MAX_LEN, "Hello system V shaare memory IPC! this is write by server.");  
  36.     sleep(15);    
  37.     printf("System V server delete share memory segment!\n");  
  38.     //shmdt(shm_buf);  
  39.     shmctl(shmid, IPC_RMID, NULL); //server在15秒之後destroy該片共享記憶體,此時客戶程序将擷取不到共享記憶體的内容  
  40.     return 0;  
  41. }  

client process:

  1. int sln_shm_get(char *shm_file, void **mem, int mem_len)  
  2. {  
  3.     int shmid;  
  4.     key_t key;  
  5.     key = ftok(shm_file, 0);  
  6.     if (key < 0) {  
  7.         printf("ftok: %s\n", strerror(errno));  
  8.         return -1;  
  9.     }  
  10.     shmid = shmget(key, mem_len, IPC_CREAT);  
  11.     if (shmid < 0) {  
  12.         printf("shmget: %s\n", strerror(errno));  
  13.         return -1;  
  14.     }  
  15.     *mem = (void *)shmat(shmid, NULL, 0);  
  16.     if ((void *)-1 == *mem) {  
  17.         printf("shmat: %s\n", strerror(errno));  
  18.         return -1;  
  19.     }  
  20.     return shmid;  
  21. }  
  22. int main(int argc, const char *argv[])  
  23. {  
  24.     char *shm_buf = NULL;  
  25.     int i;  
  26.     if (sln_shm_get(SHM_IPC_FILENAME, (void **)&shm_buf, SHM_IPC_MAX_LEN) < 0) {  
  27.         return -1;  
  28.     }  
  29.     printf("ipc client get: %s\n", shm_buf);  
  30.     return 0;  
  31. }  

運作時,首先執行server process,使用指令ipcs可以檢視目前系統共享記憶體:

  1. # ipcs   
  2. ------ Message Queues --------   
  3. key msqid owner perms used-bytes messages   
  4. ------ Shared Memory Segments --------   
  5. key shmid owner perms bytes nattch status   
  6. 0x0010a797 131072 root 0 4096 1   
  7. ------ Semaphore Arrays --------   
  8. key semid owner perms nsems   

可以看到存在一個共享記憶體區,其中key為:0x0010a797 ,共享記憶體ID為:131072 

  1. # ./client   
  2. ipc client get: Hello system V shaare memory IPC! this is write by server.   
  3. #  

當server程序destroy共享記憶體之後,再重複上面步驟,

  1. # ipcs   
  2. ------ Message Queues --------   
  3. key msqid owner perms used-bytes messages   
  4. ------ Shared Memory Segments --------   
  5. key shmid owner perms bytes nattch status   
  6. ------ Semaphore Arrays --------   
  7. key semid owner perms nsems   
  8.  此時共享記憶體已經不在了,但檔案依然存在。  
  9. # ./client  
  10. ipc client get:   
  11. #   

此時用戶端已經不能擷取之前共享記憶體内容了。

另外,ipcrm指令可以在指令行上删除指定共享記憶體區。

通過讀取檔案/proc/sys/kernel/shmmax可以擷取系統所支援共享記憶體最大值,

  1. # cat /proc/sys/kernel/shmmax  
  2. 33554432  
  3. #  

可以看到我目前系統支援最大個共享記憶體值為:32M。

通過上示例可以看到system V共享記憶體和posix共享記憶體類似,不過posix共享記憶體的大小可以随時通過ftruncate改變,而system V 的共享記憶體大小在shmget時就已經确定下來了。

同樣的,system V共享記憶體大多數時候也需要在多程序之間同步,system V 可以使用自己的信号量來實作,具體細節将在後面同步相關專欄詳細講解。

本節源碼下載下傳:

http://download.csdn.net/detail/gentleliu/8140887

  • 相關文章推薦:
  • 細說linux IPC(一):基于socket的程序間通信(上)
  • 細說linux IPC(二):基于socket的程序間通信(下)
  • 細說linux IPC(三):mmap系統調用共享記憶體
  • 本文來自:愛好技術網
  • 本文連結:http://www.ahlinux.com/c/9590.html