System V共享内存概念以及相关函数(shmget、shmat、shmdt、shmctl)介绍

System V共享内存

  • 共享内存的特点:
    1. 共享内存允许多个进程共享一块内存段
    2. 共享内存(一块物理内存)会被映射到进程地址空间,成为虚拟内存的一部分
    3. 该IPC机制无需内核的介入
    4. 相比于其他IPC机制,该技术没有内核空间和用户空间的数据交换,速度更快
1.1 创建或者打开一个共享内存段
  • 函数功能:创建一个共享内存段或者获取一个既有共享内存段的标识符,新创建的共享内存段会被初始化为0

  • 返回值:成功返回该共享内存段的标识符,失败返回-1

  • 函数原型:

    #include <sys/ipc.h>
    #include <sys/shm.h>
    int shmget(key_t key, size_t size, int shmflg);
    
  • 参数:

    1. key:用来生成标识符的值,通常是IPC_PRIVATE或者ftok() 来生成一个key
    2. size:需要分配的共享内存的大小,单位Bytes,但是实际上会被分配到系统分页大小的整倍数
    3. shmflg:施加于共享内存段的权限或者对既有共享内存段的权限检查
      • IPC_CREATE:如果不存在指定key值得共享内存段,机会创建一个共享内存
      • IPC_EXCL:如果同时指定了IPC_CREATE标记,指定key值的共享内存还存在的话就会返回EEXIST错误
1.2 使用共享内存
  • 函数功能:该系统调用将shmid 标识的共享内存段映射到进程的虚拟地址空间。映射的前提,进程拥有该共享内存的读写权限

  • 返回值:成功返回一个虚拟地址表示共享内存段的开始位置,错误返回一个指针-1

  • 函数原型:

    #include <sys/types.h>
    #include <sys/shm.h>
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    
  • 参数:

    1. shmid:共享内存段的标识符

    2. shmaddr:

      • 如果为NULL,该共享内存段会被内核分配到一个合适的地址处,最优映射的方法
      • 如果不为NULL:非最优映射的方法
        • 没有设置SHM_RND:段会被映射到一个shmaddr指定的地址处,必须是系统分页的整数倍,否则发生EINVAL错误
        • 设置了SHM_RND:段会被映射到shmaddr指定的地址舍入为最近的SHMLBA的整数倍,SHMLBA是一个常量,他为系统分页的整数倍
        • 非最优映射的原因:
          1. 降低了系统的可移植性能,其它系统中shmaddr可能不再是合法地址
          2. 该地址在可能被使用或者已经被映射
    3. shmflg: 位掩码之间用 | 连接

      在这里插入图片描述

  • 函数功能:当不在使用该共享内存段的时候,分离共享内存和虚拟地址

  • 返回值:成功返回0,失败返回-1

  • 函数原型:

    #include <sys/types.h>
    #include <sys/shm.h>
    int shmdt(const void *shmaddr);
    
  • 参数:

    1. shmaddr:shmat() 返回的一个值
  • 注意:

    1. fork()创建的子进程会继承父进程映射的共享内存段
    2. exec 之后所有映射的共享内存都会被分离,进程结束之后也会被分离
1.4 共享内存控制操作
  • 函数功能:在shmid 标识的共享内存段上面执行一组操作

  • 返回值:成功返回0,失败返回-1

  • 函数原型:

    #include <sys/ipc.h>
    #include <sys/shm.h>
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
  • 函数参数:

    1. shmid:既有共享内存段的标识
    2. cmd:控制操作
      • IPC_RMID:标记一个共享内存段及其关联数据结构shmid_ds 以便删除
        • 如果没有进程映射该共享内存段,那么立即删除该共享内存段,否则等待所有进程与该共享内存段分离之后在进行删除
        • 进程在映射一块共享内存之后,立即使用IPC_RMID标记该段,在进程与之分离之后,可以快速干净的删除该共享内存段
        • 在被IPC_RMID标记之后,如果因为还有进程与之关联没有被删除,那么此时也可以在继续被其他进程映射,但是这种做法我们推荐
      • IPC_STAT:将该共享内存的一个shmid_ds关联数据结构副本放到buf指向的缓冲区
      • IPC_SET:使用buf指向的缓冲区的值来更新shmid_ds关联数据结构中相应指定的值
1.5 共享内存关联数据结构
 struct shmid_ds {
      struct ipc_perm shm_perm;    /* Ownership and permissions */
      size_t          shm_segsz;   /* Size of segment (bytes) */
      time_t          shm_atime;   /* Last attach time */
      time_t          shm_dtime;   /* Last detach time */
      time_t          shm_ctime;   /* Last change time */
      pid_t           shm_cpid;    /* PID of creator */
      pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
      shmatt_t        shm_nattch;  /* No. of current attaches */
};
  1. shm_segsz:该字段会被设置为共享内存段所需要的字节数,但因为共享内存是按照分页来进行分配的,所以实际大小可能不会等于该值
  2. shm_nattch:映射到该共享内存的进程数量,一次shmat()调用会加1,一次shmdt() 调用会减1
1.6 共享内存在虚拟进程空间中的位置

在这里插入图片描述

1.7 共享内存中存储指针
  • 在共享内存中存储段其它位置的指针的时候,不能直接存放指针值,因为该共享内存如果被其它进程附加之后,可能会被映射到其它地址处,存储的指针就不再准确了

    在这里插入图片描述

  1. 比如在进程A中,*p = target ,在另外一个进程中,附加该共享内存段之后,那么*p 存储的指针值已经没有任何意义了,所以正确的做法是让*P存储一个偏移量,只要baseaddr 每次都是起始地址,那么*P = target - baseaddr 的值,在哪个进程中都会表示,target这个位置的指针
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值