linux进程间的通信(C): 共享内存
生活随笔
收集整理的這篇文章主要介紹了
linux进程间的通信(C): 共享内存
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、共享內存介紹 共享內存是三個IPC(Inter-Process Communication)機制中的一個。 它允許兩個不相關的進程訪問同一個邏輯內存。 共享內存是在兩個正在進行的進程之間傳遞數據的一種非常有效的方式。 大多數的共享內存的實現, 都把由不同進程之間共享的內存安排為同一段物理內存。
共享內存是由IPC為進程創建一個特殊的地址范圍, 它將出現在該進程的地址空間中。 其他進程可以將同一段共享內存連接它們自己的地址空間中。 所有進程都可以訪問共享內存中的地址, 就好像它們是由malloc分配的一樣。
如果某個進程向共享內存寫入了數據, 所做的改動將立刻被可以訪問同一段共享內存的任何其他進程看到。
二、共享內存的同步 共享內存為在多個進程之間共享和傳遞數據提供了一種有效的方式。 但是它并未提供同步機制, 所以我們通常需要用其他的機制來同步對共享內存的訪問。 我們通常是用共享內存來提供對大塊內存區域的有效訪問, 同時通過傳遞小消息來同步對該內存的訪問。
在第一個進程結束對共享內存的寫操作之前, 并無自動的機制可以阻止第二個進程開始對它進行讀取。 對共享內存訪問的同步控制必須由程序員來負責。
下圖顯示了共享內存是如何共存的:
圖中的箭頭顯示了每個進程的邏輯地址空間到可用物理內存的映射關系。
三、共享內存使用的函數#include?<sys/shm.h>
int?shmget(key_t key,?size_t size,?int?shmflg);
void?*shmat(int?shm_id,?const?void?*shm_addr,?int?shmflg);
int?shmdt(const?void?*shm_addr);
int?shmctl(int?shm_id,?int?cmd,?struct shmid_ds?*buf);
1. shmget函數 該函數用來創建共享內存:int shmget(key_t key, size_t size, int shmflg); 參數: key : 和信號量一樣,程序需要提供一個參數key, ? ? ? 它有效地為共享內存段命名。 ? ? ?? ? ? ? 有一個特殊的鍵值IPC_PRIVATE,? ? ? ? 它用于創建一個只屬于創建進程的共享內存, ? ? ? 通常不會用到。 size: 以字節為單位指定需要共享的內存容量。 shmflag: 包含9個比特的權限標志, ? ? ? ? ?它們的作用與創建文件時使用的mode標志是一樣。 ? ? ? ? ?由IPC_CREAT定義的一個特殊比特必須和權限標志按位或 ? ? ? ? ?才能創建一個新的共享內存段。
NOTE: 權限標志對共享內存非常有用, 因為它允許一個進程創建的共享內存可以被共享內存的創建者所擁有的進程寫入, 同時其它用戶創建的進程只能讀取共享內存。
我們可以利用這個功能來提供一種有效的對數據進行只讀訪問的方法, 通過將數據放共享內存并設置它的權限, 就可以避免數據被其他用戶修改。
返回值: 創建成功,則返回一個非負整數,即共享內存標識; 如果失敗,則返回-1.
2. shmat函數 第一次創建共享內存段時,它不能被任何進程訪問。 要想啟動對該內存的訪問, 必須將其連接到一個進程的地址空間。 這個工作由shmat函數完成:void?*shmat(int?shm_id,?const?void?*shm_addr,?int?shmflg); 參數: shm_id : 由shmget返回的共享內存標識。 shm_add: 指定共享內存連接到當前進程中的地址位置。 ? ? ? ? ?它通常是一個空指針,? ? ? ? ? ?表示讓系統來選擇共享內存出現的地址。 shmflg : 是一組標志。 ? ? ? ? ?它的兩個可能取值是: ? ? ? ? ?SHM_RND, 和shm_add聯合使用, ? ? ? ? ? ? ? ? ? 用來控制共享內存連接的地址。 ? ? ? ? ?SHM_RDONLY, 它使連接的內存只讀 ? ? ? ? ? 返回值: 如果調用成功, 返回一個指向共享內存第一個字節的指針; 如果失敗,返回-1.
共享內存的讀寫權限由它的屬主(共享內存的創建者), 它的訪問權限和當前進程的屬主決定。 共享內存的訪問權限類似于文件的訪問權限。
3. shmdt 將共享內存從當前進程中分離。int?shmdt(const?void?*shm_addr); shm_addr: shmat返回的地址指針。
成功時,返回0, 失敗時,返回-1.
NOTE: 共享內存分離并未刪除它, 只是使得該共享內存對當前進程不再可用。
4. shmctl 共享內存的控制函數int?shmctl(int?shm_id,?int?cmd,?struct shmid_ds?*buf); shmid_ds結構至少包含以下成員: struct shmid_ds?{
??uid_t shm_perm.uid;
??uid_t shm_perm.gid;
??mode_t shm_perm.mode;
}
參數: shm_id : 是shmget返回的共享內存標識符。 command: 是要采取的動作, ? ? ? ? ?它可以取3個值:
IPC_STAT??把shmid_ds結構中的數據設置為共享內存的當前關聯值 IPC_SET ??如果進程有足夠的權限, ? ? ? ? ? 就把共享內存的當前關聯值設置為shmid_ds結構中給出的值 IPC_RMID??刪除共享內存段
buf ? ?: 是一個指針, ? ? ? ? ?包含共享內存模式和訪問權限的結構。
返回值: 成功時,返回0, 失敗時,返回-1.
四、示例 典型的消費者-生產者程序, 第一個程序(消費者)將創建一個共享內存段, 然后把寫到它里面的數據都顯示出來。 第二個程序(生產者)將連接一個已有的共享內存段, 并允許我們向其中輸入數據。
shm_com.h#define TEXT_SZ 2048
struct shared_use_st?{
??int?written_by_you;
??char some_text[TEXT_SZ];
}; 當有數據寫入這個結構中時, 我們用結構中的written_by_you標志來通知消費者。 需要傳輸的文本長度2K是隨意定的。
shm1.c 消費者程序#include?<unistd.h>
#include?<stdlib.h>
#include?<stdio.h>
#include?<string.h>
#include?<sys/shm.h>
#include?"shm_com.h"
int?main()
{
??int?running?=?1;
??void?*shared_memory?=?(void?*)0;
??struct shared_use_st?*shared_stuff;
??int?shmid;
??srand((unsigned?int)getpid());
??shmid?=?shmget((key_t)1234,?sizeof(struct shared_use_st),?0666?|?IPC_CREAT);
??if?(shmid?==?-1)?{
????fprintf(stderr,?"shmget failed\n");
????exit(EXIT_FAILURE);
??}
現在,讓程序可以訪問這個共享內存:? shared_memory?=?shmat(shmid,?(void?*)0,?0);
??if?(shared_memory?==?(void?*)-1)?{
????fprintf(stderr,?"shmat failed\n");
????exit(EXIT_FAILURE);
??}
??printf("Memory attached at %X\n",?(int)shared_memory);
程序的下一部分將shared_memory分配給shared_stuff, 然后它輸出written_by_you中的文本。 循環將一直執行到在written_by_you中找到end字符串為止。 sleep調用強迫消費者程序在臨界區域多待一會, 讓生產者程序等待:
? shared_stuff?=?(struct shared_use_st?*)shared_memory;
??shared_stuff->written_by_you?=?0;
??while(running)? ? {
????if?(shared_stuff->written_by_you)? ? ? {
??????printf("You wrote: %s",?shared_stuff->some_text);
??????sleep(?rand()?%?4?);?/* make the other process wait for us ! */
??????shared_stuff->written_by_you?=?0;
??????if?(strncmp(shared_stuff->some_text,?“end”,?3)?==?0)?{
????????running?=?0;
??????}
????}
??}
最后,共享內存被分離,然后被刪除:? if?(shmdt(shared_memory)?==?-1)? ? {
????fprintf(stderr,?"shmdt failed\n");
????exit(EXIT_FAILURE);
??}
??if?(shmctl(shmid,?IPC_RMID,?0)?==?-1)? ? {
????fprintf(stderr,?"shmctl(IPC_RMID) failed\n");
????exit(EXIT_FAILURE);
??}
??exit(EXIT_SUCCESS);
} shm2.c 生產者程序 通過它向消費者程序輸入數據。 #include?<unistd.h>
#include?<stdlib.h>
#include?<stdio.h>
#include?<string.h>
#include?<sys/shm.h>
#include?"shm_com.h"
int?main()
{
??int?running?=?1;
??void?*shared_memory?=?(void?*)0;
??struct shared_use_st?*shared_stuff;
??char buffer[BUFSIZ];
??int?shmid;
??shmid?=?shmget((key_t)1234,?sizeof(struct shared_use_st),?0666?|?IPC_CREAT); ??if?(shmid?==?-1)? ? {
????fprintf(stderr,?"shmget failed\n");
????exit(EXIT_FAILURE);
??}
??shared_memory?=?shmat(shmid,?(void?*)0,?0);
??if?(shared_memory?==?(void?*)-1)? ? {
????fprintf(stderr,?"shmat failed\n");
????exit(EXIT_FAILURE);
??}
??printf("Memory attached at %X\n",?(int)shared_memory);
??shared_stuff?=?(struct shared_use_st?*)shared_memory;
??while(running)? ? {
????while(shared_stuff->written_by_you?==?1)? ? ? {
??????sleep(1);
??????printf("waiting for client...\n");
????}
????printf("Enter some text: ");
????fgets(buffer,?BUFSIZ,?stdin);
????strncpy(shared_stuff->some_text,?buffer,?TEXT_SZ);
????shared_stuff->written_by_you?=?1;
????if?(strncmp(buffer,?"end",?3)?==?0)?{
??????running?=?0;
????}
??}
??if?(shmdt(shared_memory)?==?-1)?{
????fprintf(stderr,?"shmdt failed\n");
????exit(EXIT_FAILURE);
??}
??exit(EXIT_SUCCESS);
}
運行程序, 將看到如下所示的樣本輸出:$?./shm1?&
[1]?294
Memory attached at 40017000
$?./shm2
Memory attached at 40017000
Enter some text:?hello
You wrote:?hello
waiting?for?client...
waiting?for?client...
Enter some text:?
You wrote:?
waiting?for?client...
waiting?for?client...
waiting?for?client...
Enter some text:?end
You wrote:?end
$ 程序解析: 消費者程序 創建共享內存段, 然后將它連接到它自己的地址空間中, 并且, 我們在共享內存的開始處使用了一個結構shared_use_st. 該結構中有個標志written_by_you, 當共享內存中有數據寫入時,就設置這個標志。
這個標志被設置時, 程序就從共享內存中讀取文本, 將它打印出來, 然后清除這個標志,表示已經讀完數據。 我們用一個特殊字符串end來退出循環。
接下來, 程序分離共享內存段并刪除它。
生產者程序 使用相同的鍵值1234來取得并連接同一個共享內存段, 然后提示用戶輸入一些文本。 如果標志written_by_you被設置, 生產者就知道消費都進程還未讀完上一次的數據, 因此就繼續等待。 當其它進程清除了這個標志后, 生產者寫入新的數據并設置這個標志。 它還使用字符串end來終止并分離共享內存段。
這里提供的同步標志written_by_you, 它是一個非常缺乏效率的忙等待(不停地循環)。
但在實際編程中, 應該使用信號量, 或通過傳遞消息(使用管道或IPC消息), 或生成信號 的方法來提供讀寫之間的更有效的同步機制。
共享內存是由IPC為進程創建一個特殊的地址范圍, 它將出現在該進程的地址空間中。 其他進程可以將同一段共享內存連接它們自己的地址空間中。 所有進程都可以訪問共享內存中的地址, 就好像它們是由malloc分配的一樣。
如果某個進程向共享內存寫入了數據, 所做的改動將立刻被可以訪問同一段共享內存的任何其他進程看到。
二、共享內存的同步 共享內存為在多個進程之間共享和傳遞數據提供了一種有效的方式。 但是它并未提供同步機制, 所以我們通常需要用其他的機制來同步對共享內存的訪問。 我們通常是用共享內存來提供對大塊內存區域的有效訪問, 同時通過傳遞小消息來同步對該內存的訪問。
在第一個進程結束對共享內存的寫操作之前, 并無自動的機制可以阻止第二個進程開始對它進行讀取。 對共享內存訪問的同步控制必須由程序員來負責。
下圖顯示了共享內存是如何共存的:
圖中的箭頭顯示了每個進程的邏輯地址空間到可用物理內存的映射關系。
三、共享內存使用的函數
1. shmget函數 該函數用來創建共享內存:
NOTE: 權限標志對共享內存非常有用, 因為它允許一個進程創建的共享內存可以被共享內存的創建者所擁有的進程寫入, 同時其它用戶創建的進程只能讀取共享內存。
我們可以利用這個功能來提供一種有效的對數據進行只讀訪問的方法, 通過將數據放共享內存并設置它的權限, 就可以避免數據被其他用戶修改。
返回值: 創建成功,則返回一個非負整數,即共享內存標識; 如果失敗,則返回-1.
2. shmat函數 第一次創建共享內存段時,它不能被任何進程訪問。 要想啟動對該內存的訪問, 必須將其連接到一個進程的地址空間。 這個工作由shmat函數完成:
共享內存的讀寫權限由它的屬主(共享內存的創建者), 它的訪問權限和當前進程的屬主決定。 共享內存的訪問權限類似于文件的訪問權限。
3. shmdt 將共享內存從當前進程中分離。
成功時,返回0, 失敗時,返回-1.
NOTE: 共享內存分離并未刪除它, 只是使得該共享內存對當前進程不再可用。
4. shmctl 共享內存的控制函數
參數: shm_id : 是shmget返回的共享內存標識符。 command: 是要采取的動作, ? ? ? ? ?它可以取3個值:
IPC_STAT??把shmid_ds結構中的數據設置為共享內存的當前關聯值 IPC_SET ??如果進程有足夠的權限, ? ? ? ? ? 就把共享內存的當前關聯值設置為shmid_ds結構中給出的值 IPC_RMID??刪除共享內存段
buf ? ?: 是一個指針, ? ? ? ? ?包含共享內存模式和訪問權限的結構。
返回值: 成功時,返回0, 失敗時,返回-1.
四、示例 典型的消費者-生產者程序, 第一個程序(消費者)將創建一個共享內存段, 然后把寫到它里面的數據都顯示出來。 第二個程序(生產者)將連接一個已有的共享內存段, 并允許我們向其中輸入數據。
shm_com.h
shm1.c 消費者程序
現在,讓程序可以訪問這個共享內存:
程序的下一部分將shared_memory分配給shared_stuff, 然后它輸出written_by_you中的文本。 循環將一直執行到在written_by_you中找到end字符串為止。 sleep調用強迫消費者程序在臨界區域多待一會, 讓生產者程序等待:
最后,共享內存被分離,然后被刪除:
運行程序, 將看到如下所示的樣本輸出:
這個標志被設置時, 程序就從共享內存中讀取文本, 將它打印出來, 然后清除這個標志,表示已經讀完數據。 我們用一個特殊字符串end來退出循環。
接下來, 程序分離共享內存段并刪除它。
生產者程序 使用相同的鍵值1234來取得并連接同一個共享內存段, 然后提示用戶輸入一些文本。 如果標志written_by_you被設置, 生產者就知道消費都進程還未讀完上一次的數據, 因此就繼續等待。 當其它進程清除了這個標志后, 生產者寫入新的數據并設置這個標志。 它還使用字符串end來終止并分離共享內存段。
這里提供的同步標志written_by_you, 它是一個非常缺乏效率的忙等待(不停地循環)。
但在實際編程中, 應該使用信號量, 或通過傳遞消息(使用管道或IPC消息), 或生成信號 的方法來提供讀寫之間的更有效的同步機制。
總結
以上是生活随笔為你收集整理的linux进程间的通信(C): 共享内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 多线程为什么鸡肋?
- 下一篇: 多线程多进程解析:Python、os、s