Linux进程通信---共享内存

时间:2021-12-03 01:07:12

概念
共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

共享内存优缺点
由于进程可以直接读写内存,所以效率高。像匿名管道和命名管道通信方式,需要在用户空间和内核空间之间进行数据拷贝,而共享内存通信方式只在内存中操作,要高效得多。

另外一方面,由于多个进程对同一段内存区域都具有读写权限,在共享内存通信中,进程间的同步问题就显得尤为重要。必须保证同一时刻只有一个进程对共享内存进行写操作,否则会导致数据被覆盖。而共享内存通信又没有提供同步机制,需要使用诸如信号量等手段进行同步控制,这又增加了其复杂性。

写端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    // 创建共享内存
    // int shmget(key_t key, size_t size,int shmflg);
    // key:进程间通信键值
    // size:该共享存储段的长度(字节)
    // shmflg:标识函数的行为及共享内存的权限,其取值如下:
    // IPC_CREAT:如果不存在就创建
    // IPC_EXCL: 如果已经存在则返回失败
    // 位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,
    // 格式和 open() 函数的 mode_t 一样,但可执行权限未使用。
    int shmid = shmget((key_t)123, BUFSIZ, 0666|IPC_CREAT);
    printf("shmid: %u\n", shmid);

    if(shmid < 0)
    {
        perror("shmget error!");
        exit(EXIT_FAILURE);
    }

    // 内存映射
    // void *shmat(int shmid, const void *shmaddr, int shmflg);
    // shmid:共享内存标识符,shmget() 的返回值。
    // shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。
    // shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:
    // 0:共享内存具有可读可写权限。
    // SHM_RDONLY:只读。
    // SHM_RND:(shmaddr 非空时才有效)
    void *shm_addr = shmat(shmid, NULL, 0);

    if(shm_addr == (void*)-1)
    {
        perror("shmat error!");
        exit(EXIT_FAILURE);
    }

    char buf[BUFSIZ];
    bzero(buf, BUFSIZ);
    sprintf(buf, "PID: %u\n", (unsigned int)getpid());

    // 拷贝数据到共享内存
    memcpy(shm_addr, buf, strlen(buf));
    printf("send: %s\n", buf);

    usleep(1000);

    // 解除共享内存映射
    // int shmdt(const void *shmaddr);
    // shmaddr:共享内存映射地址
    if(shmdt(shm_addr) == -1)
    {
        printf("shmdt error!\n");
        exit(1);
    }
    return 0;
}

读端

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>

int main()
{
    // 创建共享内存
    int shmid = shmget((key_t)123, BUFSIZ, IPC_CREAT);
    printf("shmid: %u\n", shmid);
    if(shmid == -1)
    {
        perror("shmget error!");
        exit(EXIT_FAILURE);
    }

    // 内存映射
    void *shmaddr = shmat(shmid, NULL, 0);
    if(shmaddr == (void*)-1)
    {
        perror("shmatt error!");
        exit(EXIT_FAILURE);
    }

    char buf[BUFSIZ];
    bzero(buf, BUFSIZ);

    // 读取共享内存数据
    memcpy(buf, shmaddr, BUFSIZ);
    printf("read: %s\n", buf);

    usleep(1000);

    // 解除共享内存映射
    if(shmdt(shmaddr) == -1)
    {
        printf("shmdt error!\n");
        exit(EXIT_FAILURE);
    }

    // 删除共享内存
    // int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    // shmid:共享内存标识符
    // cmd:函数功能的控制,其取值如下:
    // IPC_RMID:删除(常用 )
    // IPC_SET:设置 shmid_ds 参数,相当于把共享内存原来的属性值替换为 buf 里的属性值
    // IPC_STAT:保存 shmid_ds 参数,把共享内存原来的属性值备份到 buf 里
    // SHM_LOCK:锁定共享内存段( 超级用户 )
    // SHM_UNLOCK:解锁共享内存段
    // SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问
    // 其真正的意义是:被锁定的内存不允许被交换到虚拟内存中
    // 这样做的优势在于让共享内存一直处于内存中,从而提高程序性能
    // buf:shmid_ds 数据类型的地址,用来存放或修改共享内存的属性
    if(shmctl(shmid, IPC_RMID, 0) == -1)
    {
        printf("shmctl error!\n");
        exit(EXIT_FAILURE);
    }
    return 0;
}

Linux进程通信---共享内存
Linux进程通信---共享内存

参考
https://blog.csdn.net/tennysonsky/article/details/46425485
https://blog.csdn.net/xiejingfa/article/details/50888870