進程間通信之共享內存-系統調用mmap詳解(IPC範例篇)

     IPC-進程間通信之共享內存-系統調用mmap詳解(IPC範例篇)
(基礎知識和範例1看我前一篇博客)
範例2:兩個進程通過映射普通文件實現共享內存通信
  適用於任何進程之間; 此時,需要打開或創建一個文件,然後再調用mmap();
  mmap_write.c首先打開或創建一個文件,並把文件的長度設置爲3個people結構大小。然後從mmap()的返回地址開始,設置了 10個people結構的值,然後進程睡眠5秒鐘,等待其他進程映射同一個文件,最後解除映射;
  mmap_read.c只是簡單的映射一個文件,並以people數據結構的格式從mmap()返回的地址處讀取10個people結構,並輸出讀取的值,然後解除映射。
源代碼:

/* mmap_write.c */
#include <stdio.h>
#include  <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct{
  char name[4];
  int  age;
}people;

int main(int argc, char** argv) 
{
    int fd,i;
    people *p_map;
    char s[4]={0};
    // map a normal file as shared mem:
    const char  *file ="mmap_test";
    //fd=open(file,O_CREAT|O_RDWR |O_TRUNC,0777); /* O_TRUNC */
    fd=open(file,O_RDWR|O_TRUNC ); 
    if(fd<0)
    {
        perror("open");
        exit(-1);
    }
    /* from  SEEK_SET  search  sizeof(people)*10-1  offset */
    lseek(fd,sizeof(people)*3-1,SEEK_SET);
    write(fd,"",1);

    p_map = (people*) mmap( 0,sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
    printf("mmap address:%#x\n",(unsigned int)&p_map);
    close( fd );

    s[0] = 'B';
    s[1] = '\0';
    for(i=0; i<10; i++)
    {
        s[0] ++;
        memcpy( ( *(p_map+i) ).name, &s,sizeof(s) );
        ( *(p_map+i) ).age = 20+i;
    }
    printf(" initialize over \n ");
    sleep(5);

    munmap( p_map, sizeof(people)*10 );
    printf( "umap ok \n" );
    return 0;
}
/* mmap_read.c */
#include  <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
  char name[4];
  int  age;
}people;

int main(int argc, char** argv)  
{
    int fd,i;
    // map a normal file as shared mem:
    const char  *file ="mmap_test";
    people *p_map;

    //fd=open( file,O_CREAT|O_RDWR,0777 );
    fd=open( file,O_RDWR );
    if(fd<0)
    {
        perror("open");
        exit(-1);
    }
    p_map = (people*)mmap(0, sizeof(people)*10,  PROT_READ|PROT_WRITE,  MAP_SHARED, fd,0);
    printf("mmap address:%#x\n",(unsigned int)&p_map);
    close(fd);

    for(i = 0;i<10;i++)
    {
        printf( "name: %s age %d\n",p_map[i].name,p_map[i].age/*(*(p_map+i)).name, (*(p_map+i)).age*/ );
    }

    munmap( p_map,sizeof(people)*10 );
    return 0;
  }

測試:
這裏寫圖片描述
注:mmap_write輸出initialize over 之後,輸出umap ok之前的輸出和mmap_write輸出initialize over 和umap ok之後的輸出;
總結:
1. 最終被映射文件的內容的長度不會超過文件本身的初始大小,即映射不能改變文件的大小;
2. 可以用於進程通信的有效地址空間大小大體上受限於被映射文件的大小,但不完全受限於文件大小。打開文件被截短爲3個people結構大小,而在mmap_write中初始化了10個people數據結構,在恰當時候(mmap_write輸出initialize over 之後,輸出umap ok之前)調用mmap_read會發現mmap_read將輸出全部10個people結構的值.
注:在linux中,內存的保護是以頁爲基本單位的,即使被映射文件只有一個字節大小,內核也會爲映射分配一個頁面大小(X86 4K)的內存。當被映射文件小於一個頁面大小時,進程可以對從mmap()返回地址開始的一個頁面大小進行訪問,而不會出錯;但是,如果對一個頁面以外的地址空間進行訪問,則導致錯誤發生。因此,可用於進程間通信的有效地址空間大小不會超過文件大小及一個頁面大小的和。
3、 文件一旦被映射後,調用mmap()的進程對返回地址的訪問是對某一內存區域的訪問,暫時脫離了磁盤上文件的影響。所有對mmap()返回地址空間的操作 只在內存中有意義,只有在調用了munmap()後或者msync()時,才把內存中的相應內容寫回磁盤文件,所寫內容仍然不能超過文件的大小
4. 普通文件內存映射的創建如下所示:
fd=open( file,O_RDWR );
if(fd<0)
{
perror(“open”);
exit(-1);
}
p_map = (people*)mmap(0, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);

範例3:父子進程通過匿名映射實現共享內存
  適用於具有親緣關係的進程之間; 由於父子進程特殊的親緣關係,在父進程中先調用mmap(),然後調用fork()。那麼在調用fork()之後,子進程繼承父進程匿名映射後的地址空間,同樣也繼承mmap()返回的地址,這樣,父子進程就可以通過映射區域進行通信了。注意,這裏不是一般的繼承關係。一般來說,子進程單獨維護從父進程繼承下來的一些變量。而mmap()返回的地址,卻由父子進程共同維護。
  對於具有親緣關係的進程實現共享內存最好的方式應該是採用匿名內存映射的方式。此時,不必指定具體的文件,只要設置相應的標誌即可,
源代碼:

#include  <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct{
    char name[4];
    int  age;
}people;

int main(int argc, char** argv)
{
    int i;
    people *p_map;
    char s[4]={0};

    p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);

    if(fork() == 0)
    {
        sleep(2);
        for(i = 0;i<10;i++)
        printf("child read: the %d people's name: %s,age:  %d\n",i+1,(*(p_map+i)).name,(*(p_map+i)).age);
        (*p_map).age = 18;
        (*p_map).name[0]='m';
        (*p_map).name[1]='a';
        (*p_map).name[2]='\0';
        munmap(p_map,sizeof(people)*10); 
        return 0;
    }

    s[0] = 'B';
    s[1] = '\0';
    for(i=0; i<10; i++)
    {
        s[0] ++;
        memcpy( ( *(p_map+i) ).name, &s,sizeof(s) );
        ( *(p_map+i) ).age = 20+i;
    }

    sleep(5);
    printf( "parent read: the first people's name:  %s,age:  %d\n\n",(*p_map).name,(*p_map).age );
    munmap( p_map,sizeof(people)*10 );
    printf( "umap ok/n" );
    return 0;
}

測試:
這裏寫圖片描述
總結:
1. 匿名內存映射與使用 /dev/zero類型,都不需要真實的文件。要使用匿名映射之需要向 mmap 傳入MAP_ANONYMOUS標誌,並且 fd 參數置爲-1 。
2. 所謂匿名,指的是映射區並沒有通過 fd與磁盤文件相關聯。匿名內存映射用在有血緣關係的進程間。
3. 匿名內存映射的創建如下所示:
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
if(fork() == 0)
 {/子進程處理/}
/父進程處理/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章