進程間通信——IPC之共享內存


 
 
共享內存是三個IPC機制中的一個。它允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在進行的進程之間傳遞數據的一種非常有效的方式。
 
大多數的共享內存的實現,都把由不同進程之間共享的內存安排為同一段物理內存.
首先我們都知道我們執行的每一個程序,它看到的內存其實都是虛擬內存,虛擬內存需要進行頁表的映射將進程地址映射到物理內存,具體處理大致如下面的圖
 
  
 
共享內存特點和優勢
 
當中共享內存的大致原理相信我們可以看明白了,就是讓兩個進程地址通過頁表映射到同一片物理地址以便於通信,
你可以給一個區域里面寫入數據,理所當然你就可以從中拿取數據,這也就構成了進程間的雙向通信,而且共享內存
是IPC通信當中傳輸速度最快的通信方式沒有之一,理由很簡單,客戶進程和服務進程傳遞的數據直接從內存里存取、放入,
數據不需要在兩進程間復制,沒有什么操作比這簡單了。再者用共享內存進行數據通信,它對數據也沒啥限制。
 
最后就是共享內存的生命周期隨內核。
即所有訪問共享內存區域對象的進程都已經正常結束,共享內存區域對象仍然在內核中存在(除非顯式刪除共享內存
區域對象),在內核重新引導之前,對該共享內存區域對象的任何改寫操作都將一直保留;簡單地說,共享內存區域對象
的生命周期跟系統內核的生命周期是一致的,而且共享內存區域對象的作用域范圍就是在整個系統內核的生命周期之內。
 
 
缺陷
 
但是,共享內存也並不完美,共享內存並未提供同步機制,也就是說,在一個服務進程結束對共享內存的寫操作
 
之前,並沒有自動機制可以阻止另一個進程(客戶進程)開始對它進行讀取。這明顯還達不到我們想要的,我們不單
是在兩進程間交互數據,還想實現多個進程對共享內存的同步訪問,這也正是使用共享內存的竅門所在。基於此,
我們通常會用平時常談到和用到 信號量來實現對共享內存同步訪問控制。


 

相關函數


 

1.創建共享內存shmget

  

原型:int shmget(key_t key, size_t size, int shmflg)

返回值: 創建成功,則返回一個非負整數,即共享內存標識;

       如果失敗,則返回-1.

 

參數:

  key:    //程序需要提供一個參數key,它為共享內存段提供一個外部名。(每個IPC對象都與一個鍵 即key相關聯,然后此鍵再由內核變換為標識符)。還有一個特殊的鍵值IPC_PRIVATE, 它用創建一個只屬於該創建進程的新共享內存,通常不會用到;

 

   size:   //以字節為單位指定需要共享的內存容量。

 

  shmflag: //包含9個比特的權限標志,它們的作用與創建文件時使用的mode標志是一樣。由IPC_CREAT定義的一個特殊比特位,同時必須和權限標志按位或才能創建一個新的共享內存段。
(注意:若想創建的新IPC結構沒有引用具有同一標識符的現有的IPC結構,就要同時指定IPC_CREAT 和 IPC_EXCL;共享內存屬IPC中一種,它同樣如此

 

注:
  權限標志對共享內存非常有用,因為它允許一個進程創建的共享內存可以被共享內存的創建者所擁有的進程寫入,同時其它用戶創建的進程只能讀取共享內存。我們可以利用這個功能來提供一種有效的對數據進行只讀訪問的方法,通過將數據放共享內存並設置它的權限,就可以避免數據被其他用戶修改。

 

 

  2.將共享內存端掛載到自己地址空間shmat
  
第一次創建共享內存段時,它不能被任何進程訪問。要想啟動對該內存的訪問必須將其連接到一個進程的地址空間

  

該函數原型:void *shmat(int shmid, const void *shmaddr, int shmflg) 

  返回值:調用成功返回掛載的虛擬地址空間起始地址,失敗返回NULL

 

 

參數:

  int shmid            //是由shmget函數返回的共享內存標識。

 

  const void *shmaddr  //指定共享內存連接到當前進程中的地址位置,通常為0,表示讓系統來選擇          共享內存的地址。

 

  int shmflg     //是一組標志位,通常為0。它還可取:SHM_RND,用以決定是否將當前共享內存段連接到指定的shmaddr上。該參數和shm_addr聯合使用,用來控制共享內存連接的地址,除非只計划在一種硬件上運行應用程序,否則不要這樣指定。填0讓操作系統自己選擇是更好的方式。

  SHM_RDONLY單獨使用則是指讓它使連接的內存段只讀,否則以讀寫方式連接此內存段

 

            

3. 與共享內存段分離 shmdt 

原型:int shmdt(const void *shmaddr)

 

 

參數:

  shm_addr: shmat返回的地址指針。
 
  成功時,返回0,
  失敗時,返回-1.
 
NOTE:
 
  僅僅是共享內存分離但並未刪除它,其標識符及其相關數據結構都在 直到某個進程的IPC_RMID命令的調用shmctl特地刪除它為止  

  只是使得該共享內存對當前進程不再可用。

 

4. shmctl 共享內存控制函數

 

    #include <sys/ipc.h>
    #include <sys/shm.h>

    原型: int shmctl(int shmid, int cmd, struct shmid_ds *buf)

參數: 

  shm_id : 是shmget返回的共享內存標識符。
 
  cmd: 它可以取3個值:
    IPC_STAT  把shmid_ds結構中的數據設置為共享內存的當前關聯值
    IPC_SET   如果進程有足夠的權限就把共享內存的當前關聯值設置為shmid_ds結構中給出的值
    IPC_RMID  刪除共享內存段
 
  buf:是一個指針,包含共享內存模式和訪問權限的結構。
 
  buf指向的shmid_ds結構體 一定要包含下列一些參數:
    struct shmid_ds {  
      uid_t shm_perm.uid;  
      uid_t shm_perm.gid;  
      mode_t shm_perm.mode;  
    }  

 

 

簡單使用


 

簡單用共享內存來再兩進程間交換數據,比如交換一個結構體

代碼如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <sys/ipc.h>
 6 #include <sys/shm.h>
 7 typedef struct Stu
 8 { 
 9     int age;
10     char name[10];
11 }Stu;
12 
13 int main( void)
14 { 
15     Stu s;
16     strcpy(s.name, "jack");
17     //創建共享內存段
18     int id = shmget(1234, 8, IPC_CREAT|0644);
19     if( id == -1)perror( " shmget"),exit( 1);
20     //掛載到進程的地址空間
21     Stu* p = ( Stu*)shmat( id, NULL, 0);
22 
23     int i =0;
24     while( 1)
25     {    
26         s.age = i++;
27         memcpy(p, &s, sizeof(Stu));  //寫到共享段中
28         sleep( 2);
29     }
30     return 0;
31 } 
write.c
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/ipc.h>
 5 #include <sys/shm.h>
 6 typedef struct Stu
 7 { 
 8     int age;
 9     char name[10];
10 }Stu;
11 int main( void)
12 { 
13     int id = shmget(1234, 8, 0);
14     if( id == -1)perror( " shmget"),exit( 1);
15 
16     Stu* p = ( Stu*)shmat( id, NULL, 0);
17     while( 1)
18     {    
19         printf(" age= %d, name= %s\n", p->age, p->name);
20         sleep(2);
21     }
22     return 0;
23 }  
read.c

 

執行結果如下: 

    

   

小結

 

  優點:我們可以看到使用共享內存進行進程間的通信真的是方便而高效,而且函數的接口也簡單,數據的共享還使進程間的數據不用傳送,而是直接訪問內存,也加快了程序的效率。同時,它也不像匿名管道那樣要求通信的進程有一定的父子關系。

 
  缺點:共享內存沒有提供同步的機制,這使得我們在使用共享內存進行進程間通信時,往往要借助其他的手段來進
 行進程間的同步工作。
 
 

上面只是共享內存的一些簡單的應用,當多個進程對共享內存進行訪問時,並沒有保證同步,所以我們還需要用其它的機制來實現它的同步機制,要解決此,通常會用到信號量(PV操作)來實現。但要基於此的實現,前提還需要熟悉信號量的操作以及這里的共享內存使用。要用共享內存模擬做出一個帶同步機制"先進先出"的消息通道,對我等萌新並不太容易,所以還得放到以后再實現了。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM