進程間通訊---共享內存

1、IPC(Inter-Process Communication,進程間通訊)對象的介紹
 
System V 的IPC對象有共享內存、消息隊列、信號燈。



注意:在IPC的通訊模式下,不論是使用消息隊列仍是共享內存,甚至是信號燈,每一個IPC的對象都有惟一的名字,稱爲"鍵"(key)。經過"鍵",進程可以識別所用的對象。"鍵"與IPC對象的關係就如同文件名稱於文件,經過文件名,進程可以讀寫文件內的數據,甚至多個進程可以公用一個文件。而在 IPC的通信模式下,經過"鍵"的使用也使得一個IPC對象能爲多個進程所共用。
 
2、共享內存的介紹
 
<1>共享內存是一種最爲高效的進程間通訊方式,進程能夠直接讀寫內存,而不須要任何數據的拷貝。
 
<2> 爲了在多個進程間交換信息,內核專門留出了一塊內存區,能夠由須要訪問的進程將其映射到本身的私有地址空間。進程就能夠直接讀寫這一塊內存而不須要進行數據的拷貝,從而大大提升效率。
 
<3>因爲多個進程共享一段內存,所以也須要依靠某種同步機制。
 
3、共享內存的特色



4、共享內存的操做流程
 
<1>建立/打開共享內存
<2>映射共享內存,即把指定的共享內存映射到進程的地址空間用於訪問
<3>撤銷共享內存映射
<4>刪除共享內存對象
 
5、相關API 
 
A.獲取一塊共享內存



功能:分配一塊共享內存
 
返回值:
 
調用成功返回一個shmid(相似打開一個或建立一個文件得到的文件描述符同樣);
調用失敗返回-1。
 
參數說明:
 
<1>key標識共享內存的鍵值(就像文件的標識是文件名):0  / IPC_PRIVATE。
 
當key的取值爲IPC_PRIVATE,則函數shmget()將建立一塊新的共享內存;
 
若是key的取值爲0,而參數shmflg中設置了IPC_CREATE這個標誌,則一樣建立一塊新的共享內存。
 
經過這種方式分配的共享內存,通常用來親緣關係的進程間通訊。
 
注意:咱們通常是經過ftok這個函數獲取鍵值

功能 : 獲取一個IPC對象的鍵值
 
參數說明:
 
pthname就是你指定文件名的路徑(該文件必須是存在並且能夠訪問的),通常狀況咱們都寫一個目錄
 
proj_id  : 和pthname一塊兒完成建立鍵值的參數,雖然爲int,可是隻有8個比特被使用。通常咱們寫一個字符代替。
 
例如:
 
案例:


運行的結果:



<2> size是要創建共享內存的長度。全部的內存分配操做都是以頁爲單位的。因此若是一個進程只申請一塊只有一個字節的內存,內存也會分配整整一頁(在i386機器中一頁的缺省大小PACE_SIZE = 4096字節)。
 
<3>shmflg有效的標誌包括IPC_CREAT 和IPC_EXCL,他們的功能與open()的O_CREAT和O_EXCL至關。
 
IPC_CREAT      若是共享內存不存在,則建立一個共享內存,不然直接打開已存在的
IPC_EXCL        只有在共享內存不存在的時候,新的共享內存才創建,不然就產生錯誤



例子一:假設鍵值爲key,建立一個共享內存大小爲4k,訪問權限爲066,若是已經存在則返回其標識號 
 
int shmid;
if( (shmid = shmget(key,4 * 1024,0666 | IPC_CREAT)) < 0)
{
    perror("Fail to shmget");
    exit(EXIT_FAILURE)
}
 
例子2、假設鍵值爲key,建立一個共享內存大小爲1k,訪問權限爲0666,若是已經存在則報錯
 
int shmid;
if((shmid  = shmget(key,1024,0666 | IPC_CREAT | IPC_EXCL)) < 0)
{
    perror("Fail to shmget");
    exit(EXIT_FAILURE);
}
 
B.共享內存的映射



函數shmat將標識號爲shmid共享內存映射到調用進程的地址空間中。
 
參數說明:
 
shmid  :  要映射的共享內存區標識符
 
shmaddr  :  將共享內存映射到指定地址(若爲NULL,則表示由系統自動完成映射)
 
shmflg  :  SHM_RDONLY  共享內存只讀
                默認0:共享內存可讀寫。
 
返回值 :調用成功放回映射後的地址 ,出錯放回(void *)-1;
 
C.取消共享內存與用戶進程之間的映射



參數shmaddr是shmat映射成功放回的地址。
 
注意:當一個進程再也不須要共享內存段時,它將調用shmdt()系統調用取消這個段,可是,這並非從內核真正地刪除這個段, 而是把相關shmid_ds結構的shm_nattch域的值減1當這個值爲0時,內核才從物理上刪除這個共享段
 
D.控制共享內存



參數說明:
 
shmid  共享內存標識ID
 
cmd      IPC_STAT獲得共享內存的狀態
              IPC_SET改變共享內存的狀態
               IPC_RMID刪除共享內存
 
buf  是一個結構體指針。IPC_STAT的時候,取得的狀態放在這個結構體中。若是要改變共享內存的狀態,用這個結構體指定;



注意:
 
1.IPC_RMID命令實際上不從內核刪除一個段,而是僅僅把這個段標記爲刪除,實際的刪除發生最後一個進程離開這個共享段時
 
2.當cmd爲IPC_RMID時,第三個參數應爲NULL。呵呵,大部分咱們都是這樣作,用這個函數刪除共享內存。
 
 
案例探究:


點擊(此處)摺疊或打開css

  1. #include <sys/shm.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <semaphore.h>
  6. #include <fcntl.h>
  7. #include <sys/stat.h>
  8. #define BUFF_SIZE 1024
  9. int father_do_work(int shmid)
  10. {
  11.     char *buf;
  12.     void *shmaddr;
  13.     sem_t *prsem;
  14.     sem_t *pwsem;
  15.     //有名信號量
  16.     if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
  17.     {
  18.         perror("Fail to sem open");
  19.         return -1;
  20.     }
  21.         
  22.         //有名信號量
  23.     if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
  24.     {
  25.         perror("Fail to sem open");
  26.         return -1;
  27.     }
  28.     //映射共享內存
  29.     if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
  30.     {
  31.         perror("Fail to shmat");
  32.         exit(EXIT_FAILURE);
  33.     }
  34.     buf = (char *)shmaddr;
  35.     while(1)
  36.     {
  37.         if(sem_wait(pwsem) < 0)
  38.         {
  39.             perror("Fail to sem wait");
  40.             break;
  41.         }
  42.         printf(">");
  43.         fgets(buf,BUFF_SIZE,stdin);
  44.         buf[strlen(buf) - 1] = '\0';
  45.         
  46.         if(sem_post(prsem) < 0)
  47.         {
  48.             perror("Fail to sem post");
  49.             break;
  50.         }
  51.         
  52.         if(strncmp(buf,"quit",4) == 0)
  53.         {
  54.             if(shmdt(shmaddr) < 0)
  55.             {
  56.                 perror("Fail to shmaddr");
  57.                 exit(EXIT_FAILURE);
  58.             }
  59.             break;
  60.         }
  61.     
  62.         usleep(500);
  63.     }
  64.     
  65.     return 0;
  66. }
  67. int child_do_work(int shmid)
  68. {
  69.     char *buf;
  70.     void *shmaddr;
  71.     sem_t *prsem;
  72.     sem_t *pwsem;
  73.     //
  74.     if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
  75.     {
  76.         perror("Fail to sem open");
  77.         return -1;
  78.     }
  79.     if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
  80.     {
  81.         perror("Fail to sem open");
  82.         return -1;
  83.     }
  84.     //映射共享內存
  85.     if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
  86.     {
  87.         perror("Fail to shmat");
  88.         exit(EXIT_FAILURE);
  89.     }
  90.     buf = (char *)shmaddr;
  91.     while(1)
  92.     {
  93.         if(sem_wait(prsem) < 0)
  94.         {
  95.             perror("Fail to prsem");
  96.             break;
  97.         }
  98.         printf("read buf : %s.\n",buf);
  99.         if(sem_post(pwsem) < 0)
  100.         {
  101.             perror("Fail to pwsem");
  102.             break;
  103.         }
  104.         if(strncmp(buf,"quit",4) == 0)
  105.         {
  106.             if(shmdt(shmaddr) < 0)
  107.             {
  108.                 perror("Fail to shmaddr");
  109.                 exit(EXIT_FAILURE);
  110.             }
  111.             break;
  112.         }
  113.     }
  114.     
  115.     return 0;
  116. }
  117. int main()
  118. {
  119.     int shmid;
  120.     int pid;
  121.     void *shmaddr;
  122.     
  123.     //建立共享內存
  124.     if((shmid = shmget(IPC_PRIVATE,BUFF_SIZE,0666 | IPC_CREAT)) < 0)
  125.     {
  126.         perror("Fail to shmget");
  127.         exit(EXIT_FAILURE);
  128.     }
  129.     if((pid = fork()) < 0)
  130.     {
  131.         perror("Fail to fork");
  132.         exit(EXIT_FAILURE);
  133.     
  134.     }else if(pid == 0){
  135.     
  136.         child_do_work(shmid);
  137.     
  138.     }else{
  139.         
  140.         father_do_work(shmid);
  141.         wait(NULL);
  142.         if(shmctl(shmid,IPC_RMID,NULL) < 0)
  143.         {
  144.             perror("Fail to shmctl");
  145.             exit(EXIT_FAILURE);
  146.         }
  147.     }
  148.     exit(EXIT_SUCCESS);
  149. }

運行結果:

相關文章
相關標籤/搜索