0%

《趣谈linux操作系统》小结(三十三) - 信号量

前言

进程间通过共享内存实现进程间通信,但是临界资源需要保护,可以使用信号量进行控制。信号量在这里就起到了锁的作用, 对临界资源进行协调。

信号量也是System V体系的一部分, 跟共享内存、消息队列类似。

共享内存

之前讲sysrepo是一个基于共享内存的数据库,sysrepo主要还是通过文件映射的方式实现共享内存的,属于高一个层级的使用。这里讲一下,共享内存的底层接口使用。

创建共享内存

首先,创建之前,我们要有一个 key 来唯一标识这个共享内存。这个 key 可以根据文件系统上的一个文件的 inode 随机生成。

创建一个共享内存,都是使用 xxxget 来创建。

1
2

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

key 就是前面生成的那个 key,shmflag 如果为 IPC_CREAT,就表示新创建,还可以指定读写权限 0777。这里返回一个共享内存的id。

内存映射

生成了共享内存以后,接下来就是将这个共享内存映射到进程的虚拟地址空间中。

1
2

void *shmat(int shm_id, const void *addr, int shmflg);

这里面的 shm_id,就是上面创建的共享内存的 id,addr 就是指定映射在某个地方。如果不指定,则内核会自动选择一个地址,作为返回值返回。

然后,我们通过强制类型转换,对该内存进行数据写入。

当共享内存使用完毕,我们可以通过 shmdt 解除它到虚拟内存的映射。

1
2

int shmdt(const void *shmaddr)

信号量

信号量,也是类似的。

首先,创建之前,我们同样需要有一个 key,来唯一标识这个信号量集合。这个 key 同样可以根据文件系统上的一个文件的 inode 随机生成。

然后,我们需要创建一个信号量集合,同样也是使用 xxxget 来创建,其中创建信号量集合使用的是下面这个函数。

1
2

int semget(key_t key, int nsems, int semflg);

这里面的 key,就是前面生成的那个 key,shmflag 如果为 IPC_CREAT,就表示新创建,还可以指定读写权限 0777。

信号量是以集合的形式存在,一次可以申请nsems个信号量,一般我们申请一个就可以了。

信号量对应的有P、V的操作,分别用于获取信号量和释放信号量。信号量可以设置同时并发的个数,每一个P操作就是对信号量的值减一,每一个V操作就是对信号量的值加一。当信号量的值为0的时候,P操作就会挂住,等待信号量的值大于0之后才继续执行。

我们设置信号量的初值为1,那么就可以实现同时仅能有一个访问者对资源操作的功能,就是锁的功能了。

示例程序

源码:

https://gitee.com/fishmwei/blog_code/tree/master/linux-kernel/sem

运行效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Keep:sem keep$ ./p
ftok error: No such file or directory
how many integers to caculate : 2
Input the 0 integer : 1
Input the 1 integer : 2
how many integers to caculate :


Keep:sem keep$ ./c
ftok error: No such file or directory
1+2=3
no tasks, waiting.
no tasks, waiting.

更多

前几天到公司的时间早了一点,下车前突然想到很久没有在极客时间上面学习了,打开趣谈linux操作系统,开始学了大概半小时,感觉效果挺不错,四周挺安静的。嗯,每天挤出来半个小时,学习一下,积少成多,加油。

行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。