POSIX共享記憶體

嚇人的猿發表於2018-02-27

3.3 POSIX共享記憶體

​ 在Linux中,POSIX共享記憶體物件駐留在tmpfs偽檔案系統中。系統預設掛載在/dev/shm目錄下。當呼叫shm_open函式建立或開啟POSIX共享記憶體物件時,系統會將建立/開啟的共享記憶體檔案放到/dev/shm目錄下。

​ 建立共享記憶體的基本步驟是:

  1. 程式執行shm_open函式建立了共享記憶體區域,此時會在/dev/shm/建立共享記憶體檔案.

    #include <sys/mman.h>
    int shm_open(const char *name, int oflag, mode_t mode);
    /*
    建立或開啟一個共享記憶體,成功返回一個整數的檔案描述符,錯誤返回-1。
    1.name:共享記憶體區的名字;
    2.oflag標誌位: open的標誌一樣,一般填寫O_CREAT|O_TRUNC|O_RDWR。
    3.mode許可權位:  open的mode一樣
    */

  2. 通過ftruncate函式改變shm_open建立共享記憶體的大小為頁大小sysconf(_SC_PAGE_SIZE)整數倍,如果不執ftruncate函式的話,會報Bus error的錯誤。

    #include <unistd.h>
    int ftruncate(int fd, off_t length);
    
    /*函式說明:ftruncate()會將引數fd指定的檔案大小改為引數length指定的大小。引數fd為已開啟的檔案描述詞,而且必須是以寫入模式開啟的檔案。如果原來的檔案大小比引數length大,則超過的部分會被刪去。
    返 回  值:0、-1
    錯誤原因:errno
              EBADF     引數fd檔案描述詞為無效的或該檔案已關閉
              EINVAL    引數fd為一socket並非檔案,或是該檔案並非以寫入模式開啟
     */
  1. 通過mmap函式將建立的共享記憶體檔案對映到記憶體。

    #include <sys/mman.h>
    void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
    
    /*函式說明:mmap()必須以PAGE_SIZE為單位進行對映。
      start:    對映區的開始地址,設定為0時表示由系統決定對映區的起始地址。
      length:   對映區的長度,長度單位是以位元組為單位,不足一記憶體頁按一記憶體頁處理
      prot:     期望的記憶體保護標誌,不能與檔案的開啟模式衝突。是以下的某個值,可以通過或運算合理地組合在一起
                PROT_EXEC  頁內容可以被執行
                PROT_READ  頁內容可以被讀取
                PROT_WRITE 頁可以被寫入
                PROT_NONE  頁不可訪問
      flags:    指定對映物件的型別,對映選項和對映頁是否可以共享。它的值可以是一個或者多個以下位的組合體
                MAP_SHARED 與其它所有對映這個物件的程式共享對映空間。對共享區的寫入,相當於輸出到檔案。            直到msync()或者munmap()被呼叫,檔案實際上不會被更新。
                MAP_PRIVATE 建立一個寫入時拷貝的私有對映。記憶體區域的寫入不會影響到原檔案。這個標誌和以           上標誌是互斥的,只能使用其中一個。
                MAP_LOCKED 鎖定對映區的頁面,從而防止頁面被交換出記憶體。
      fd:      有效的檔案描述符。
      offset:   被對映物件內容的起點。
      
      成功返回共享記憶體地址,失敗返回MAP_FAILED
    */


  2. 通過munmap解除安裝共享記憶體

  3. int munmap(void* start,size_t length);
    /*
    * start:共享記憶體地址
    * length:共享記憶體大小
    */
  4. 通過shm_unlink刪除記憶體共享檔案

    int shm_unlink(const char *name);
    /*
    *name:記憶體共享檔案
    */

我們用下面的源程式對POSIX共享記憶體進行測試,如下shmen_write.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>
void error_print(char *msg)
{
        perror(msg);
        exit(-1);
}

int main (int argc, char *argv[])
{
        int ret, i;
        const char *memname = "/mymem";
        size_t mem_size = sysconf(_SC_PAGE_SIZE);
        int fd = shm_open(memname, O_CREAT|O_TRUNC|O_RDWR, 0666);
        if (fd == -1)
            error_print("shm_open");
        ret = ftruncate(fd, mem_size);
        if (ret != 0)
             error_print("ftruncate");
  
        void *ptr = mmap(0, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED)
             error_print("MMAP");
        close(fd);
        for(i = 0; i < 20; i++)
        {
            sprintf((char*)ptr, "data %d", i);
            printf("write data %s\n", (char*)ptr);
        }
        ret = munmap(ptr, mem_size);
        if (ret != 0)
             error_print("munmap");
        ret = shm_unlink(memname);
        if (ret != 0)
             error_print("shm_unlink");
        return 0;
}

編譯程式shmen_write.c

gcc shmen_write.c -o shmen_write -lrt

shmen_read.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>
void error_print(char *msg)
{
        perror(msg);
        exit(-1);
}

int main (int argc, char *argv[])
{
        int ret, i;
        const char *memname = "/mymem"; //共享記憶體檔案以/開頭,可以更好相容不同系統
        struct stat statbuf;
        size_t mem_size = 2 * sysconf(_SC_PAGE_SIZE);
        int fd = shm_open(memname, O_CREAT|O_TRUNC|O_RDWR, 0666);
        if (fd == -1)
            error_print("shm_open");
        ret = ftruncate(fd, mem_size);
        if (ret != 0)
             error_print("ftruncate");

        void *ptr = mmap(0, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED)
             error_print("MMAP");
        close(fd);

        for(i = 0; i < 20; i++)
        {
            printf("read data %s\n", (char*)ptr);
            sleep(1);
        }
        ret = munmap(ptr, mem_size);
        if (ret != 0)
             error_print("munmap");
        ret = shm_unlink(memname);
        if (ret != 0)
             error_print("shm_unlink");
        return 0;
}

編譯程式shmen_read.c

gcc shmen_read.c -o shmen_read -lrt

先執行shmem_write,再執行shmem_read.

相關文章