Linux進程間通信六 Posix 共享內存簡介與示例

1. 共享內存簡介

共享內存主要用於不同進程之間相互通信,因爲操作的是同一塊地址,不需要內核和用戶層之間數據拷貝,屬於最快的進程間通信方式,不過,爲了防止讀寫衝突,一般需要額外的同步手段。之前介紹了SystemV共享內存的使用方式,今天介紹下Posix共享內存。Posix 共享內存API主要有這幾個,shm_open,用於獲取或者創建一個共享內存文件描述符,ftruncate,用於設置共享內存的大小,新建的共享內存大小爲0,mmap,用於將共享內存文件映射到進程的虛擬地址空間,其實共享內存真正核心的工作主要在於mmap,不過mmap是另外一個話題,暫時不討論。munmap,和mmap作用相反。shm_unlink,移除共享內存。close關閉shm_open獲取的文件描述符,通常mmap之後就可以關閉了。fstat獲取共享內存的信息,比如大小。下面介紹下相關API接口

2. API接口

2.1 創建共享內存

#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */

/**
* @brief 創建或獲取已存在的共享內存
*
* @params name 共享內存名字,以斜槓開頭'/',不超過NAME_MAX(255)字符
* @params oflag O_RDONLY| O_RDWR| O_CREAT| O_EXCL| O_TRUNC
* @params mode 設置一些權限,通常使用0即可
*
* @returns 成功返回文件描述符,失敗返回-1
**/

int shm_open(const char *name, int oflag, mode_t mode);

2.2 設置共享內存大小

#include <unistd.h>
#include <sys/types.h>

/**
* @brief 修改fd的大小
*
* @returns 成功返回0,失敗返回-1
**/

int ftruncate(int fd, off_t length);

2.3 查詢共享內存文件大小

#include <unistd.h>
#include <sys/types.h>

/**
* @brief 查詢fd的信息
* @param fd 要查詢的文件描述符
* @param buf 用於保存查詢結果的buffer
* @returns 成功返回0,失敗返回-1
**/

struct stat {
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};

int fstat(int fd, struct stat *buf);

2.4 映射共享內存

#include <sys/mman.h>


/**
* @brief 映射共享內存到進程自身虛擬地址空間
*
* @params addr 映射到的地址,通常設置爲NULL,表示內核自己選擇合適地址。
* @params length 映射的共享內存大小
* @params prot 保護標誌位,可選PROT_EXEC| PROT_READ| PROT_WRITE| PROT_NONE
* @params flags 可選
* MAP_SHARED,共享模式,修改對其它進程可見
* MAP_PRIVATE 私有映射,對共享內存的修改對其它進程不可見。
* @params fd 被映射的共享內存文件描述符
* @params offset 文件偏移值,通常填0
* @returns 成功返回共享內存地址,失敗返回-1
**/

void *mmap(void *addr, size_t length,int prot, int flags ,
           int fd, off_t offset);

/**
* @brief 刪除mmap的映射
*
* @params addr 映射到的地址
* @params length 映射的共享內存大小
* @returns 成功返回0址,失敗返回-1
**/

int munmap(void *addr, size_t length);

2.5 刪除共享內存

#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */

/**
* @brief 刪除共享內存,只有當所有進程都取消映射後才銷燬它
*
* @params name 共享內存名字
*
* @returns 成功返回0,失敗返回-1
**/

int shm_unlink(const char *name);

# 編譯加上 -lrt選項
Link with -lrt.

3. 共享內存示例

這裏僅簡單讀寫,沒有考慮同步。

3.1 寫共享內存

#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <error.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>

#define SHM_NAME "/shm0"
#define SHM_SIZE 4096

int main(int argc, char** argv)
{
    if (argc < 2)
    {
        printf("Usage: ./shm_write msg\n");
        return -1;
    }

    // 創建或打開已存在的共享內存
    int shm_fd = shm_open(SHM_NAME, O_RDWR|O_CREAT, 0);
    if (shm_fd == -1)
    {
        perror("shm_open error");
        return -1;
    }

    // 設置共享內存大小
    if (ftruncate(shm_fd, SHM_SIZE))
    {
        perror("ftruncate error");
        return -1;
    }

    // 映射共享內存
    char* buffer = (char *)mmap(NULL, SHM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (buffer == (char *)MAP_FAILED)
    {
        perror("mmap error");
        return -1;
    }

    // 關閉共享內存文件描述符,並不影響映射
    close(shm_fd);

    // 寫共享內存
    strncpy(buffer, argv[1], strlen(argv[1]));
    
    // 刪除共享內存
    //shm_unlink(shm_fd);
    return 0;
}

3.2 讀共享內存

#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <error.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define SHM_NAME "/shm0"
#define SHM_SIZE 4096

int main()
{
    // 創建或打開共享內存
    int shm_fd = shm_open(SHM_NAME, O_RDWR|O_CREAT, 0);
    if (shm_fd == -1)
    {
        perror("shm_open error");
        return -1;
    }

    // 獲取共享內存大小,讀取的時候要用
    struct stat stat={0};
    if (fstat(shm_fd, &stat))
    {
        perror("fstat error");
        return -1;
    }

    // 映射共享內存,大小爲上一步獲取的大小
    char* buffer = (char*)mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, shm_fd, 0);
    if (buffer == (char *)MAP_FAILED)
    {
        perror("mmap error");
        return -1;
    }
    // 關閉文件描述符
    close(shm_fd);

    // 讀取共享內存
    printf("Read Msg:%s\n", buffer);
    
    // 刪除共享內存
    shm_unlink(SHM_NAME);
    return 0;
}

3.3 編譯&運行

default:
	gcc -o shm_read shm_read.c -lrt
	gcc -o shm_write shm_write.c -lrt
clean:
	rm -rf shm_write shm_read

4. 參考文檔

https://www.man7.org/linux/man-pages/man7/mq_overview.7.html

================================================================================================

Linux應用程序、內核、驅動、後臺開發交流討論羣(745510310),感興趣的同學可以加羣討論、交流、資料查找等,前進的道路上,你不是一個人奧^_^。...
 

 

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