mmap和shm共享內存的區別和聯繫

共享內存的建立html

1、區別linux

 1. 共享內存容許兩個或多個進程共享一給定的存儲區,由於數據不須要來回複製,因此是最快的一種進程間通訊機制。共享內存能夠經過mmap()映射普通文件(特殊狀況下還能夠採用匿名映射)機制實現,也能夠經過系統V共享內存機制實現。應用接口和原理很簡單,內部機制複雜。爲了實現更安全通訊,每每還與信號燈等同步機制共同使用。

mmap的機制如:就是在磁盤上創建一個文件,每一個進程存儲器裏面,單獨開闢一個空間來進行映射。若是多進程的話,那麼不會對實際的物理存儲器(主存)消耗太大。安全

shm的機制:每一個進程的共享內存都直接映射到實際物理存儲器裏面。函數

2、結論

一、mmap保存到實際硬盤,實際存儲並無反映到主存上。優勢:儲存量能夠很大(多於主存);缺點:進程間讀取和寫入速度要比主存的要慢。

二、shm保存到物理存儲器(主存),實際的儲存量直接反映到主存上。優勢,進程間訪問速度(讀寫)比磁盤要快;缺點,儲存量不能很是大(多於主存)

使用上看:若是分配的存儲量不大,那麼使用shm;若是存儲量大,那麼使用mmap。ui

 

 

mmap就是一個文件操做。spa

mmap函數是unix/linux下的系統調用,來看《Unix Netword programming》卷二12.2節有詳細介紹。

mmap系統調用並非徹底爲了用於共享內存而設計的。它自己提供了不一樣於通常對普通文件的訪問方式,進程能夠像讀寫內存同樣對普通文件的操做。而Posix或系統V的共享內存IPC則純粹用於共享目的,固然mmap()實現共享內存也是其主要應用之一。
          mmap系統調用使得進程之間經過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間後,進程能夠像訪問普通內存同樣對文件進行訪問,沒必要再 調用read(),write()等操做。mmap並不分配空間, 只是將文件映射到調用進程的地址空間裏, 而後你就能夠用memcpy等操做寫文件, 而不用write()了.寫完後用msync()同步一下, 你所寫的內容就保存到文件裏了. 不過這種方式沒辦法增長文件的長度, 由於要映射的長度在調用mmap()的時候就決定了.

簡單說就是把一個文件的內容在內存裏面作一個映像,內存比磁盤快些。
基本上它是把一個檔案對應到你的virtual memory 中的一段,並傳回一個指針。.net

 

系統調用mmap()用於共享內存的兩種方式: 
(1)使用普通文件提供的內存映射:適用於任何進程之間;此時,須要打開或建立一個文件,而後再調用mmap();典型調用代碼以下: 
fd=open(name, flag, mode); 
if(fd<0) 
... 
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 經過mmap()實現共享內存的通訊方式有許多特色和要注意的地方,咱們將在範例中進行具體說明。 
(2)使用特殊文件提供匿名內存映射:適用於具備親緣關係的進程之間;因爲父子進程特殊的親緣關係,在父進程中先調用mmap(),而後調用fork()。那麼在調用fork()以後,子進程繼承父進程匿名映射後的地址空間,一樣也繼承mmap()返回的地址,這樣,父子進程就能夠經過映射區域進行通訊了。注意,這裏不是通常的繼承關係。通常來講,子進程單獨維護從父進程繼承下來的一些變量。而mmap()返回的地址,卻由父子進程共同維護。設計

 

一、mmap()系統調用形式以下:unix

void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset ) 指針

參數fd爲即將映射到進程空間的文件描述字,通常由open()返回,同時,fd能夠指定爲-1,此時須指定flags參數中的MAP_ANON,代表進行的是匿名映射(不涉及具體的文件名,避免了文件的建立及打開,很顯然只能用於具備親緣關係的進程間通訊)。len是映射到調用進程地址空間的字節數,它從被映射文件開頭offset個字節開始算起。prot 參數指定共享內存的訪問權限。可取以下幾個值的或:PROT_READ(可讀) , PROT_WRITE (可寫), PROT_EXEC (可執行), PROT_NONE(不可訪問)。flags由如下幾個常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用。offset參數通常設爲0,表示從文件頭開始映射。參數addr指定文件應被映射到進程空間的起始地址,通常被指定一個空指針,此時選擇起始地址的任務留給內核來完成。函數的返回值爲最後文件映射到進程空間的地址,進程可直接操做起始地址爲該值的有效地址。這裏再也不詳細介紹mmap()的參數,讀者可參考mmap()手冊頁得到進一步的信息。

 

3、mmap()範例

下面將給出使用mmap()的兩個範例:範例1給出兩個進程經過映射普通文件實現共享內存通訊;範例2給出父子進程經過匿名映射實現共享內存。系統調用mmap()有許多有趣的地方,下面是經過mmap()映射普通文件實現進程間的通訊的範例,咱們經過該範例來講明mmap()實現共享內存的特色及注意事項。

範例1:兩個進程經過映射普通文件實現共享內存通訊

/*-------------map_normalfile1.c-----------*/
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
    char name[4];
    int  age;
}people;
main(int argc, char** argv) // map a normal file as shared mem:
{
    int fd,i;
    people *p_map;
    char temp;
    
    fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
    lseek(fd,sizeof(people)*5-1,SEEK_SET);
    write(fd,"",1);
    
    p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
    close( fd );
    temp = 'a';
    for(i=0; i<10; i++)
    {
        temp += 1;
        memcpy( ( *(p_map+i) ).name, &temp,2 );
        ( *(p_map+i) ).age = 20+i;
    }
    printf(" initialize over \n ");
    sleep(10);
    munmap( p_map, sizeof(people)*10 );
    printf( "umap ok \n" );
}
/*-------------map_normalfile2.c-----------*/
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
    char name[4];
    int  age;
}people;
main(int argc, char** argv)    // map a normal file as shared mem:
{
    int fd,i;
    people *p_map;
    fd=open( argv[1],O_CREAT|O_RDWR,00777 );
    p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    for(i = 0;i<10;i++)
    {
    printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
    }
    munmap( p_map,sizeof(people)*10 );
}

  

範例2:父子進程經過匿名映射實現共享內存

#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
    char name[4];
    int  age;
}people;
main(int argc, char** argv)
{
    int i;
    people *p_map;
    char temp;
    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<5;i++)
            printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);
        (*p_map).age = 100;
        munmap(p_map,sizeof(people)*10); //實際上,進程終止時,會自動解除映射。
        exit();
    }
    temp = 'a';
    for(i = 0;i<5;i++)
    {
        temp += 1;
        memcpy((*(p_map+i)).name, &temp,2);
        (*(p_map+i)).age=20+i;
    }
    sleep(5);
    printf( "parent read: the first people,s age is %d\n",(*p_map).age );
    printf("umap\n");
    munmap( p_map,sizeof(people)*10 );
    printf( "umap ok\n" );
}

  

 4、shm內存共享的主要函數講解

shmget函數

該函數用來建立共享內存:
int shmget(key_t key, size_t size, int shmflg);
參數:
key : 和信號量同樣,程序須要提供一個參數key,
      它有效地爲共享內存段命名。
      
      有一個特殊的鍵值IPC_PRIVATE, 
      它用於建立一個只屬於建立進程的共享內存,
      一般不會用到。
size: 以字節爲單位指定須要共享的內存容量。
shmflag: 包含9個比特的權限標誌,
         它們的做用與建立文件時使用的mode標誌是同樣。
         由IPC_CREAT定義的一個特殊比特必須和權限標誌按位或
         才能建立一個新的共享內存段。

NOTE:
權限標誌對共享內存很是有用,
由於它容許一個進程建立的共享內存能夠被共享內存的建立者所擁有的進程寫入,
同時其它用戶建立的進程只能讀取共享內存。

咱們能夠利用這個功能來提供一種有效的對數據進行只讀訪問的方法,
經過將數據放共享內存並設置它的權限,
就能夠避免數據被其餘用戶修改。

返回值:
建立成功,則返回一個非負整數,即共享內存標識;
若是失敗,則返回-1.

  

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.

共享內存的讀寫權限由它的屬主(共享內存的建立者),
它的訪問權限和當前進程的屬主決定。
共享內存的訪問權限相似於文件的訪問權限。

  

 shmdt函數

將共享內存從當前進程中分離。
int shmdt(const void *shm_addr);
shm_addr: shmat返回的地址指針。

成功時,返回0,
失敗時,返回-1.

NOTE:
共享內存分離並未刪除它,
只是使得該共享內存對當前進程再也不可用。

  

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內存共享可參考:http://blog.chinaunix.net/uid-26000296-id-3421346.html

相關文章
相關標籤/搜索