天天看點

程序間通信之-共享記憶體Shared Memory--linux核心剖析(十一) 共享記憶體 記憶體模型 示例

共享記憶體是程序間通信中最簡單的方式之一。

共享記憶體是系統出于多個程序之間通訊的考慮,而預留的的一塊記憶體區。

共享記憶體允許兩個或更多程序通路同一塊記憶體,就如同 malloc() 函數向不同程序傳回了指向同一個實體記憶體區域的指針。當一個程序改變了這塊位址中的内容的時候,其它程序都會察覺到這個更改。

當一個程式加載進記憶體後,它就被分成叫作頁的塊。

通信将存在記憶體的兩個頁之間或者兩個獨立的程序之間。

總之,當一個程式想和另外一個程式通信的時候,那記憶體将會為這兩個程式生成一塊公共的記憶體區域。這塊被兩個程序分享的記憶體區域叫做共享記憶體

因為所有程序共享同一塊記憶體,共享記憶體在各種程序間通信方式中具有最高的效率。通路共享記憶體區域和通路程序獨有的記憶體區域一樣快,并不需要通過系統調用或者其它需要切入核心的過程來完成。同時它也避免了對資料的各種不必要的複制。

如果沒有共享記憶體的概念,那一個程序不能存取另外一個程序的記憶體部分,因而導緻共享資料或者通信失效。因為系統核心沒有對通路共享記憶體進行同步,您必須提供自己的同步措施。

解決這些問題的常用方法是通過使用信号量進行同步。不過,我們的程式中隻有一個程序通路了共享記憶體,是以在集中展示了共享記憶體機制的同時,我們避免了讓代碼被同步邏輯搞得混亂不堪。

為了簡化共享資料的完整性和避免同時存取資料,核心提供了一種專門存取共享記憶體資源的機制。這稱為互斥體或者mutex對象

例如,在資料被寫入之前不允許程序從共享記憶體中讀取資訊、不允許兩個程序同時向同一個共享記憶體位址寫入資料等。

當一個程序想和另外一個程序通信的時候,它将按以下順序運作:

擷取mutex對象,鎖定共享區域。

将要通信的資料寫入共享區域。

釋放mutex對象。

當一個程序從從這個區域讀資料時候,它将重複同樣的步驟,隻是将第二步變成讀取。

要使用一塊共享記憶體

程序必須首先配置設定它

随後需要通路這個共享記憶體塊的每一個程序都必須将這個共享記憶體綁定到自己的位址空間中

當完成通信之後,所有程序都将脫離共享記憶體,并且由一個程序釋放該共享記憶體塊

在<code>/proc/sys/kernel/</code>目錄下,記錄着共享記憶體的一些限制,如一個共享記憶體區的最大位元組數<code>shmmax</code>,系統範圍内最大共享記憶體區辨別符數<code>shmmni</code>等,可以手工對其調整,但不推薦這樣做。

程式間通信之-共享記憶體Shared Memory--linux核心剖析(十一) 共享記憶體 記憶體模型 示例

了解 linux 系統記憶體模型可以有助于解釋這個綁定的過程。

在 linux 系統中,每個程序的虛拟記憶體是被分為許多頁面的。這些記憶體頁面中包含了實際的資料。每個程序都會維護一個從記憶體位址到虛拟記憶體頁面之間的映射關系。盡管每個程序都有自己的記憶體位址,不同的程序可以同時将同一個記憶體頁面映射到自己的位址空間中,進而達到共享記憶體的目的。

配置設定一個新的共享記憶體塊會建立新的記憶體頁面。因為所有程序都希望共享對同一塊記憶體的通路,隻應由一個程序建立一塊新的共享記憶體。再次配置設定一塊已經存在的記憶體塊不會建立新的頁面,而隻是會傳回一個辨別該記憶體塊的辨別符。

一個程序如需使用這個共享記憶體塊,則首先需要将它綁定到自己的位址空間中。

這樣會建立一個從程序本身虛拟位址到共享頁面的映射關系。當對共享記憶體的使用結束之後,這個映射關系将被删除。

當再也沒有程序需要使用這個共享記憶體塊的時候,必須有一個(且隻能是一個)程序負責釋放這個被共享的記憶體頁面。

所有共享記憶體塊的大小都必須是系統頁面大小的整數倍。系統頁面大小指的是系統中單個記憶體頁面包含的位元組數。在 linux 系統中,記憶體頁面大小是4kb,不過您仍然應該通過調用 getpagesize 擷取這個值。

共享記憶體的實作分為兩個步驟: 建立共享記憶體,使用shmget函數。 映射共享記憶體,将這段建立的共享記憶體映射到具體的程序空間去,使用shmat函數。

共享記憶體的使用,主要有以下幾個api:<code>ftok()</code>、<code>shmget()</code>、<code>shmat()</code>、<code>shmdt()</code>及shmctl()。

1

2

3

4

5

程式間通信之-共享記憶體Shared Memory--linux核心剖析(十一) 共享記憶體 記憶體模型 示例

與信号量相類似,通常需要在包含shm.h檔案之前包含sys/types.h與sys/ipc.h這兩個頭檔案。

應用說明,在ipc中,我們經常用用key_t的值來建立或者打開信号量,共享記憶體和消息隊列。

參數

描述

pathname

一定要在系統中存在并且程序能夠通路的

proj_id

一個1-255之間的一個整數值,典型的值是一個ascii值。

當成功執行的時候,一個key_t值将會被傳回,否則-1被傳回。我們可以使用strerror(errno)來确定具體的錯誤資訊。

考慮到應用系統可能在不同的主機上應用,可以直接定義一個key,而不用ftok獲得:

程序通過調用shmget(shared memory get,擷取共享記憶體)來配置設定一個共享記憶體塊。

key

一個用來辨別共享記憶體塊的鍵值

size

指定了所申請的記憶體塊的大小

shmflg

操作共享記憶體的辨別

傳回值:如果成功,傳回共享記憶體表示符,如果失敗,傳回-1。

該函數的第二個參數key是一個用來辨別共享記憶體塊的鍵值。

彼此無關的程序可以通過指定同一個鍵以擷取對同一個共享記憶體塊的通路。不幸的是,其它程式也可能挑選了同樣的特定值作為自己配置設定共享記憶體的鍵值,進而産生沖突。

用特殊常量ipc_private作為鍵值可以保證系統建立一個全新的共享記憶體塊。|

key辨別共享記憶體的鍵值:0/ipc_private。當key的取值為ipc_private,則函數shmget将建立一塊新的共享記憶體;如果key的取值為0,而參數中又設定了ipc_private這個标志,則同樣會建立一塊新的共享記憶體。

該函數的第二個參數size指定了所申請的記憶體塊的大小。

因為這些記憶體塊是以頁面為機關進行配置設定的,實際配置設定的記憶體塊大小将被擴大到頁面大小的整數倍。

第三個參數shmflg是一組标志,通過特定常量的按位或操作來shmget。這些特定常量包括:

ipc_creat:這個标志表示應建立一個新的共享記憶體塊。通過指定這個标志,我們可以建立一個具有指定鍵值的新共享記憶體塊。

ipc_excl:這個标志隻能與 ipc_creat 同時使用。當指定這個标志的時候,如果已有一個具有這個鍵值的共享記憶體塊存在,則shmget會調用失敗。也就是說,這個标志将使線程獲得一個“獨有”的共享記憶體塊。如果沒有指定這個标志而系統中存在一個具有相同鍵值的共享記憶體塊,shmget會傳回這個已經建立的共享記憶體塊,而不是重新建立一個。

模式标志:這個值由9個位組成,分别表示屬主、屬組和其它使用者對該記憶體塊的通路權限。

其中表示執行權限的位将被忽略。指明通路權限的一個簡單辦法是利用

shmat()是用來允許本程序通路一塊共享記憶體的函數,将這個記憶體區映射到本程序的虛拟位址空間。

shmid

那塊共享記憶體的id,是shmget函數傳回的共享存儲辨別符

shmaddr

是共享記憶體的起始位址,如果shmaddr為0,核心會把共享記憶體映像到調用程序的位址空間中標明位置;如果shmaddr不為0,核心會把共享記憶體映像到shmaddr指定的位置。是以一般把shmaddr設為0。

shmflag

是本程序對該記憶體的操作模式。如果是shm_rdonly的話,就是隻讀模式。其它的是讀寫模式

成功時,這個函數傳回共享記憶體的起始位址。失敗時傳回-1。

要讓一個程序擷取對一塊共享記憶體的通路,這個程序必須先調用 shmat(shared memory attach,綁定到共享記憶體)。

将 shmget 傳回的共享記憶體辨別符 shmid 傳遞給這個函數作為第一個參數。

該函數的第二個參數是一個指針,指向您希望用于映射該共享記憶體塊的程序記憶體位址;如果您指定null則linux會自動選擇一個合适的位址用于映射。第三個參數是一個标志位,包含了以下選項:

shm_rnd表示第二個參數指定的位址應被向下靠攏到記憶體頁面大小的整數倍。如果您不指定這個标志,您将不得不在調用shmat的時候手工将共享記憶體塊的大小按頁面大小對齊。

shm_rdonly表示這個記憶體塊将僅允許讀取操作而禁止寫入。 如果這個函數調用成功則會傳回綁定的共享記憶體塊對應的位址。通過 fork 函數建立的子程序同時繼承這些共享記憶體塊;

如果需要,它們可以主動脫離這些共享記憶體塊。 當一個程序不再使用一個共享記憶體塊的時候

當一個程序不再需要共享記憶體時,需要把它從程序位址空間中多裡。

那塊共享記憶體的起始位址

成功時傳回0。失敗時傳回-1。

應通過調用 shmdt(shared memory detach,脫離共享記憶體塊)函數與該共享記憶體塊脫離。将由 shmat 函數傳回的位址傳遞給這個函數。如果當釋放這個記憶體塊的程序是最後一個使用該記憶體塊的程序,則這個記憶體塊将被删除。對 exit 或任何exec族函數的調用都會自動使程序脫離共享記憶體塊。

shmctl控制對這塊共享記憶體的使用

函數原型

是共享記憶體的id。

cmd

控制指令

buf

一個結構體指針。ipc_stat的時候,取得的狀态放在這個結構體中。如果要改變共享記憶體的狀态,用這個結構體指定。

其中cmd的取值如下

ipc_stat

得到共享記憶體的狀态

ipc_set

改變共享記憶體的狀态

ipc_rmid

删除共享記憶體

傳回值: 成功:0 失敗:-1

調用 shmctl(”shared memory control”,控制共享記憶體)函數會傳回一個共享記憶體塊的相關資訊。同時 shmctl 允許程式修改這些資訊。

該函數的第一個參數是一個共享記憶體塊辨別。

要擷取一個共享記憶體塊的相關資訊,則為該函數傳遞 ipc_stat 作為第二個參數,同時傳遞一個指向一個 struct shmid_ds 對象的指針作為第三個參數。

要删除一個共享記憶體塊,則應将 ipc_rmid 作為第二個參數,而将 null 作為第三個參數。當最後一個綁定該共享記憶體塊的程序與其脫離時,該共享記憶體塊将被删除。

您應當在結束使用每個共享記憶體塊的時候都使用 shmctl 進行釋放,以防止超過系統所允許的共享記憶體塊的總數限制。調用 exit 和 exec 會使程序脫離共享記憶體塊,但不會删除這個記憶體塊。 要檢視其它有關共享記憶體塊的操作的描述,請參考shmctl函數的手冊頁。

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

程式間通信之-共享記憶體Shared Memory--linux核心剖析(十一) 共享記憶體 記憶體模型 示例

在使用共享記憶體,結束程式退出後。如果你沒在程式中用shmctl()删除共享記憶體的話,一定要在指令行下用ipcrm指令删除這塊共享記憶體。你要是不管的話,它就一直在那兒放着了。

簡單解釋一下ipcs指令和ipcrm指令。

取得ipc資訊:

删除ipc

77

78

79

80

程式間通信之-共享記憶體Shared Memory--linux核心剖析(十一) 共享記憶體 記憶體模型 示例

轉載:http://blog.csdn.net/gatieme/article/details/51005811

繼續閱讀