Linux多进程通讯之内存共享

  • Post author:
  • Post category:linux


共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。它是IPC对象的一种。为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。共享内存允许两个进程访问同一块内存区域,

它们使用同一个 key 值标记

。可运用Linux的系统命令 ipcs -m 查看共享内存信息。


1.1共享内存的创建


函数原型如下:

int shmget(key_t key, size_t size, int shmflg);

其中:

key:一个非零整数,两个进程需保持一致,即两个进程之间通信的钥匙;

size: 申请的共享区的大小,单位是字节。需要是内存页大小的整数倍;

shmflg:同 open 函数的 mode 参数,设置文件访问权限,这里额外多出一个 IPC_CREAT 和 IPC_EXCL,可与 mode 进行或操作。IPC_CREAT 表示共享区不存在则创建,IPC_EXCL 和 IPC_CREAT 共同使用,表示共享区已存在则返回错误。如 0644 | IPC_CREAT

返回值: 返回共享区域的 id 值,用于唯一识别该区域。


1.2共享内存映射


函数原型如下:

void *shmat(int shmid, const void *shmaddr, int shmflg);

其中:

shmid:创建时候返回的 id 值;

shmaddr:将共享内存映射到指定地址,可以为 NULL,此时系统将自动分配地址;

shmflg:同 shmget 函数中的参数,通常为 0;

返回值 : 成功执行后,返回该地址的起始地址,失败返回 -1


1.3撤销映射


函数原型如下:

int shmdt(const void *shmaddr);

其中:

shmaddr : shmat 函数返回的地址。

返回值 : 成功返回 0,失败返回 -1,errno 将被设置为相应的值。


1.4删除共享内存


函数原型如下:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

其中:

shmid : 创建时候返回的 id 值;

cmd : 控制命令;IPC_STAT 状态查询;IPC_SET 在权限允许下,将共享内存状态更新为 buf 中的数据;IPC_RMID 删除共享内存;

buf::指向一个保存着共享内存的模式状态和访问权限的数据结构。

返回值 : 成功返回 0,失败返回 -1,errno 将被设置为相应的值。


1.5缺陷


共享内存也并不完美,共享内存并未提供同步机制,也就是说,在一个服务进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程(客户进程)开始对它进行读取。这明显还达不到我们想要的,我们不单是在两进程间交互数据,还想实现多个进程对共享内存的同步访问,这也正是使用共享内存的窍门所在。基于此,我们通常会用平时常谈到和用到 信号量来实现对共享内存同步访问控制。


1.6共享内存样例代码


Write端:

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
int main(void) 
{
    void *shmaddr = NULL;
    const char data[] = "Hello World\n";
    int shmid;
    key_t key = (key_t) 666;
    long page_size = sysconf(_SC_PAGESIZE);
    int data_size =  (strlen(data) + page_size - 1) & (~(page_size - 1));
    printf("data size: %d, page size: %ld\n", data_size, page_size);
 
    // 1. create shared memory
    shmid = shmget(key, data_size, 0644 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 2. attach shared memory
    shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1) {
        perror("shmat failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 3. write data to shared memory
    memset(shmaddr, 0, data_size);
    memcpy(shmaddr, &data, strlen(data));
 
    // 4. detach shared memory
    if (shmdt(shmaddr) == -1) {
        perror("shmdt failed\n");
        exit(EXIT_FAILURE);
    }
 
    printf("write done !\n");
    return 0;
}

Read端:

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
int main(void) 
{
    void *shmaddr = NULL;
    const char data[] = "Hello World\n";
    int shmid;
    key_t key = (key_t) 666;
    long page_size = sysconf(_SC_PAGESIZE);
    int data_size =  (strlen(data) + page_size - 1) & (~(page_size - 1));
    printf("data size: %d, page size: %ld\n", data_size, page_size);
 
    // 1. create shared memory
    shmid = shmget(key, data_size, 0644 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 2. attach shared memory
    shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1) {
        perror("shmat failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 3. read data to shared memory
    printf("read form shead memory: %s\n", (char *)shmaddr);
 
    // 4. detach shared memory
    if (shmdt(shmaddr) == -1) {
        perror("shmdt failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 5. delete shared memory
    if (shmctl(shmid, IPC_RMID, 0) == -1) {
        perror("shmctl delete shared memory failed\n");
        exit(EXIT_FAILURE);
    }
 
    return 0;
}



版权声明:本文为u010304442原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。