【Linux】学习记录_13_共享内存

13 共享内存

13.1 基本概念

共享内存就是将内存进行共享,允许多个不相关的进程访问同一个逻辑内存, 直接将一块裸露的内存放在需要数据传输的进程面前。

共享的内存需要进程自己去维护好,如同步、互斥等工作,共享内存是属于临界资源,在某一时刻最多只能有一个进程对其操作(读/写数据), 共享内存一般不能单独使用,而要配合信号量、互斥锁等协调机制,让各个进程在高效交换数据的同时, 不会发生数据践踏、破坏等意外。

共享内存

总的来说共享内存有以下特点:

  1. 共享内存是进程间通信中效率最高的方式之一。
  2. 共享内存是出于多个进程之间通讯的考虑,而预留的的一块内存区,因此共享内存是以传输数据为目的的。
  3. 共享内存允许两个或更多进程访问同一块内存,一个进程改变这块地址中的内容, 其它进程都会察觉到这
  4. 共享内存无同步无互斥。

共享内存的优缺点:

  • 优点:使用共享内存进行进程间的通信非常方便,函数接口简单,数据的共享使进程间的数据不用传送, 而是直接访问内存,加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的“血缘”关系, 只要是系统中的任意进程都可以对共享内存进行读写操作。
  • 缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时, 往往要借助其他的手段(如信号量、互斥量等)来进行进程间的同步工作。

13.2 shmget()创建共享内存函数

内核提供了shmget()函数的创建或获取一个共享内存对象,并返回共享内存标识符。函数原型如下:

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

参数说明:

  • key:标识共享内存的键值,可以有以下取值:
    • 0 或 IPC_PRIVATE。当key的取值为IPC_PRIVATE,则函数shmget()创建一块新的共享内存; 如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
    • 大于0的32位整数:视参数shmflg来确定操作。
  • size:共享内存的大小,所有的内存分配操作都是以页为单位
  • shmflg:表示创建的共享内存的模式标志参数,在真正使用时需要与IPC对象存取权限mode(如0600)进行“|”运算来确定共享内存的存取权限。 msgflg有多种情况:
    • IPC_CREAT:如果内核中不存在关键字与key相等的共享内存,则新建;如果存在,返回标识符。
    • IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建;如果存在这样的共享内存则报错。
    • SHM_HUGETLB:使用“大页面”来分配共享内存,所谓的“大页面”指的是内核为了提高程序性能,对内存实行分页管理时,采用比默认尺寸(4KB)更大的分页,以减少缺页中断。Linux内核支持以2MB作为物理页面分页的基本单位。
    • SHM_NORESERVE:不在交换分区中为这块共享内存保留空间。
  • 返回值:shmget()函数的返回值是共享内存的ID。

当调用shmget()函数失败时将产生错误代码,有如下取值:

  • EACCES:指定的消息队列已存在,但调用进程没有权限访问它
  • EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
  • EINVAL:创建共享内存时参数size小于SHMMIN或大于SHMMAX。
  • ENFILE:已达到系统范围内打开文件总数的限制。
  • ENOENT:给定的key不存在任何共享内存,并且未指定IPC_CREAT。
  • ENOMEM:内存不足,无法为共享内存分配内存。
  • EACCES:没有权限。

12.3 shmat()映射函数

如果一个进程想要访问这个共享内存,那么需要将其映射到进程的虚拟空间中, 然后再访问

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

参数说明:

  • shmid:共享内存ID,通常是由shmget()函数返回的。
  • shmaddr:如果不为NULL,则系统会根据shmaddr来选择一个合适的内存区域, 如果为NULL,则系统会自动选择一个合适的虚拟内存空间地址去映射共享内存。
  • shmflg:操作共享内存的方式:
    • SHM_RDONLY:以只读方式映射共享内存。
    • SHM_REMAP:重新映射,此时shmaddr不能为NULL。
    • NULLSHM:自动选择比shmaddr小的最大页对齐地址。

shmat()函数调用成功后返回共享内存的起始地址。

共享内存的映射有以下注意的要点:

  • 共享内存只能以只读或者可读写方式映射,无法以只写方式映射。
  • shmat()第二个参数shmaddr一般设为NULL,让系统找寻合适的地址。但当其确实不为空时, 那么要求SHM_RND在shmflg必须被设置,系统将会选择比shmaddr小而又最大的页对齐地址(即为SHMLBA的整数倍)作为共享内存区域的起始地址。 如果没有设置SHM_RND,那么shmaddr必须是严格的页对齐地址。

12.4 shmdt()解除映射函数

shmdt()函数与shmat()函数相反,是用来解除进程与共享内存之间的映射的,解除映射后,该进程不能再访问

int shmdt(const void *shmaddr);

参数说明:

  • shmaddr:映射的共享内存的起始地址。

shmdt()函数调用成功返回0,如果出错则返回-1,并且将错误原因存于error中。

虽然shmdt()函数很简单,但是还是有注意要点的:该函数并不删除所指定的共享内存区, 而只是将先前用shmat()函数映射好的共享内存脱离当前进程,共享内存还是存在于物理内存中。

13.5 shmctl()获取或设置属性函数

内核提供了shmctl()用于获取或者设置共享内存的相关属性。函数原型:

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

参数说明:

  • shmid:共享内存标识符。
  • cmd:函数功能的控制命令,其取值如下:
    • IPC_STAT:获取属性信息,放置到buf中。
    • IPC_SET:设置属性信息为buf指向的内容。
    • IPC_RMID:删除这该共享内存。
    • IPC_INFO:获得关于共享内存的系统限制值信息。
    • SHM_INFO:获得系统为共享内存消耗的资源信息。
    • SHM_STAT:与IPC_STAT具有相同的功能,但shmid为该SHM在内核中记录所有SHM信息的数组的下标, 因此通过迭代所有的下标可以获得系统中所有SHM的相关信息。
    • SHM_LOCK:禁止系统将该SHM交换至swap分区。
    • SHM_UNLOCK:允许系统将该SHM交换至swap分。
  • buf:共享内存属性信息结构体指针,设置或者获取信息都通过该结构体,shmid_ds结构如下:

注意:选项SHM_LOCK不是锁定读写权限,而是锁定SHM能否与swap分区发生交换。 一个SHM被交换至swap分区后如果被设置了SHM_LOCK,那么任何访问这个SHM的进程都将会遇到页错误。 进程可以通过IPC_STAT后得到的mode来检测SHM_LOCKED信息。

struct shmid_ds {
    struct ipc_perm shm_perm;    /* 所有权和权限 */
    size_t          shm_segsz;   /* 共享内存尺寸(字节) */
    time_t          shm_atime;   /* 最后一次映射时间 */
    time_t          shm_dtime;   /* 最后一个解除映射时间 */
    time_t          shm_ctime;   /* 最后一次状态修改时间 */
    pid_t           shm_cpid;    /* 创建者PID */
    pid_t           shm_lpid;    /* 后一次映射或解除映射者PID */
    shmatt_t        shm_nattch;  /* 映射该SHM的进程个数 */
    ...
};

其中权限信息结构体如下:

struct ipc_perm {
    key_t          __key;    /* 该共享内存的键值key */
    uid_t          uid;      /* 所有者的有效UID */
    gid_t          gid;      /* 所有者的有效GID */
    uid_t          cuid;     /* 创建者的有效UID */
    gid_t          cgid;     /* 创建者的有效GID */
    unsigned short mode;     /* 读写权限 + SHM_DEST + SHM_LOCKED 标记 */
    unsigned short __seq;    /* 序列号 */
};

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值