Linux 消息队列、共享内存、信号量(二)共享内存和信号量

Linux 消息队列、共享内存、信号量(二)共享内存和信号量

 

一般来说,进程间通信会加上一个进程间同步或者互斥的功能,比如管道,读进程没有东西读的时候会进入睡眠,写进程在缓冲区满的时候也会进入睡眠。虽然这两个过程从宏观上来说是并发的,但是从微观上来说是原子的。但是共享内存不一样,在一个进程对该内存读的时候,另一个进程能够对该内存执行写操作。所以,我们要通过信号量来达到对共享内存的同步和互斥。

 

回顾上一篇博文,我们知道V IPC的底层都是通过调用sys_ipc(),sys_ipc里通过给定的方法来执行不同的操作,这里再重复一遍。

 

#define SEMOP 1 //信号量的操作
#define SEMGET 2 //获得(创建信号量)
#define SEMCTL 3 //对信号机制的控制和设置
#define MSGSND 11 //报文发送
#define MSGRCV 12 //报文接收
#define MSGGET 13 //建立消息队列
#define MSGCTL 14 //队列机制的控制和设置
#define SHMAT 21 //共享内存的映射
#define SHMDT 22 //撤销共享内存的映射
#define SHMGET 23 //共享内存的创建和获取
#define SHMCTL 24 //共享内存的操作

 

我们先从共享内存开始说起

 

shmget() 共享内存的查找和获取

 

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

底层调用是sys_shmget(),还是老样子,key是其键值,size该内存的大小,shmflg是其标识号,PRIVATE位为1的话就是分配一个共享内存给该进程,CREATE位为1的话就是创建。

 

整个函数比较简单,先是检查是不是PRIVATE,接着看是不是要查找,如果是就查找,不是就创建。

 

创建过程也比较简单,先是检查需要的大小是否合格,创建一个共享内存独有的数据结构指针,接着调用kmalloc分配,再给该内存一个file数据结构标识了文件号,最后修改一些参数。

 

与消息队列一样,共享内存拥有一个全局的数组shm_ids,用来记录管理共享内存

 

前面提到了给了共享内存一个file,那么这个file的路径在proc/pid/map/shm下,这是一个特殊文件系统shm。用通俗的话来说,建立一个共享内存就是建立一个shm文件并且与物理内存的映射过程。

 

 

 

shmat()共享内存的映射

 

前面说了,建立一个共享内存就是建立一个shm文件并且与物理内存的映射过程,shmat()就是处理共享内存的映射的。它的底层是sys_shmat (int shmid,  char *shmaddr,  int shmflg,  ulong *raddr) 

 

shmaddr就是当前进程所要求映射的目标地址,地址应该与页面对齐。这个没什么好解释的,

函数具体来说就是通过do_mmap()来实现文件与物理页面的映射。do_mmap()比较复杂,在这里只挑重要的来说。先是检查文件和区间,包括地址长度、已经映射的次数,文件和区间的访问权限等等。最后从vm_area里找到第一个足以容纳给定长度的区间,如果找不到,那起始地址就是给定的地址。

 

是不是觉得看着很简单?不,这只是建立起这么一个机制。因为物理页面都是动态调整的。而且为了节省资源,系统往往到了非建不可的时候才会确定这么一个映射。比如我共享内存的大小是10个页面,但是我建好了就只用了一个页面,所以系统只会处理一个页面的映射。在我要用到第二个页面的时候,因为缺页异常,系统才会建立起第二个页面的映射。

 

 

shmdt()共享内存映射的撤销

 

一个进程在用不上共享内存的时候会先把映射脱钩,在删除释放掉资源,这是由于各个API所处理不同任务的原因造成的。这个函数很简单,找到共享内存的起始地址,通过do_ummap()直到共享内存结束。

 

shmctl()对共享内存的管理

V IPC的操作起始都一样,没什么好讨论的。

 

讨论完共享内存,再来讨论信号量

 

信号量的创建和共享内存的创建几乎一模一样,唯一不同就是创建的过程。

 

创建信号量只需要给信号量的数据结构sem_array分配空间,修改一些必要的参数。同时,信号量的全局数组是sem_ids

 

信号量的创建可以是一个,也可以创建多个形成信号集,这将导致该信号量(集)大小不同。

 

shmop()信号量操作

 

信号量操作通常是PV操作,并且符合(要么全有,要么全无)的规则。它的底层是sys_shemop()

 

现将数据拷贝到系统空间,然后检查权限、对信号量操作进行统计,检查所需的参数do_undo是否是负数,如果是,就不能够获得资源,就要进入睡眠状态。接下来调用函数获取资源。如果函数返回值为0,获取成功,进入临界区执行,如果返回值为负数,失败调用别的方法,如果返回值为1,那就是某个信号量获取失败了。

 

shmctl()这里就不讨论了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值