linux下的進程間通信之共享內存


概念:這種機制允許兩個或多個進程通過把公共數據結構放入一個共享內存區來訪問它們。如果進程要訪問這種數據結構所在的共享內存區,就必須在自己的地址空間中增加一個新線性區,新線性區映射與這個共享內存區相關的頁框。這樣的頁框可以由內核通過請求調頁進行簡單的處理。

優點:共享內存(shared memory)是最簡單的最大自由度的Linux進程間通信方式之一。使用共享內存,不同進程可以對同一塊內存進行讀寫。由於所有進程對共享內存的訪問就和訪問自己的內存空間一樣,而不需要進行額外系統調用或內核操作,同時還避免了多余的內存拷貝,所以,這種方式是效率最高、速度最快的進程間通信方式。

缺點:內核並不提供任何對共享內存訪問的同步機制,比如同時對共享內存的相同地址進行寫操作,則后寫的數據會覆蓋之前的數據。所以,使用共享內存一般還需要使用其他IPC機制(如信號量)進行讀寫同步與互斥。

基本原理

了解Linux內存管理機制,就很容易知道共享內存的原理了。大家知道,內核對內存的管理是以頁(page)為單位的,Linux下一般一個page大小是4k。而程序本身的虛擬地址空間是線性的,所以內核管理了進程從虛擬地址空間到起對應的頁的映射。創建共享內存空間后,內核將不同進程虛擬地址的映射到同一個頁面:所以在不同進程中,對共享內存所在的內存地址的訪問最終都被映射到同一頁面。下圖演示了共享內存的工作機制:

 

使用方法

共享內存的使用過程可分為 創建->連接->使用->分離->銷毀 這幾步。

共享內存的創建使用shmget函數(SHared Memory GET)函數,使用方法如下:

int segment_id = shmget (shm_key, getpagesize (),IPC_CREAT | S_IRUSR | S_IWUSER);
shmget根據shm_key創建一個大小為page_size的共享內存空間,參數3是一系列的創建參數。如果shm_key已經創建,使用該shm_key會返回可以連接到該以創建共享內存的id。
創建后,為了使共享內存可以被當前進程使用,必須緊接着進行連接操作。使用函數shmat(SHared Memory ATtach),參數傳入通過shmget返回的共享內存id即可:

shared_memory = (char*) shmat (segment_id, 0, 0);
shmat返回映射到進程虛擬地址空間的地址指針,這樣進程就能像訪問一塊普通的內存緩沖一樣訪問共享內存。例子中后兩個參數我們全部傳入0,采用默認的連接方式。實際上,第二個參數是希望連接的進程虛擬地址空間的地址,如果使用0,Linux會自己選用一個合適的地址;第三個參數是連接選項,可以是:
SHM_RND:將第二個參數指向的內存空間自動提升到page size的整數倍,如果不知明該參數,你需要自己控制第二個參數所指內存空間的大小。

SHM_RDONLY:該共享內存是只讀的,不可寫。

當共享內存使用完畢后,使用函數shmdt (SHared Memory DeTach)進行解連接。該函數以shmat返回的內存地址作為參數。每最后一個使用該共享內存的進程分離該共享內存后,內核將會對該共享內存自動銷毀。當然,我們最好能顯式的進行銷毀,以避免不必要的共享內存資源浪費。 函數shmctl (SHared Memory ConTroL)可以返回共享內存的信息並對其進行控制,如

shmctl (segment_id, IPC_STAT, &shmbuffer);
可以返回該共享內存的信息,並將信息保存在第三個參數指向的shmid_ds結構體中。
當向第二個參數傳入IPC_RMID時,共享內存將會在最后一個使用該共享內存的進程分離共享內存是銷毀共享內存。

shmctl還有很多其他使用方法, 不再贅述。

示例程序

下面的示例程序,a進程每一秒的向共享內存寫入一個隨機數,b進程每隔一秒從該共享內存讀出該數。

/*
* a.c
* write a random number between 0 and 999 to the shm every 1 second
*/
#include<stdio.h>
#include<unistd.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<error.h>
int main(){
int shm_id;
int *share;
int num;
srand(time(NULL));
shm_id = shmget (1234, getpagesize(), IPC_CREAT);
if(shm_id == -1){
perror("shmget()");
}
share = (int *)shmat(shm_id, 0, 0);
while(1){
num = random() % 1000;
*share = num;
printf("write a random number %d\n", num);
sleep(1);
}
return 0;
}

/*
* b.c
* read from the shm every 1 second
*/
#include<stdio.h>
#include<unistd.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<error.h>
int main(){
int shm_id;
int *share;
shm_id = shmget (1234, getpagesize(), IPC_CREAT);
if(shm_id == -1){
perror("shmget()");
}
share = (int *)shmat(shm_id, 0, 0);
while(1){
sleep(1);
printf("%d\n", *share);
}
return 0;
}

 


免責聲明!

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



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