- Introduction : Semaphore(后面简称Sema)是在多线程锁中常常用到的锁的一种
STEP-1-生成一些Sema
方式1 -> 生成key + 生成Sema集合
创建semaphore的集合:semget() - 返回Sema集合的id
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
- key:由不同的进程去识别这个sema集合的一个标识符
- fname是指定的文件名,这个文件必须是存在而且可以访问的
- nsems: 集合里sema的数量
- semflg:这个告诉semget集合应该有什么样的许可(例如可读可写这样的权限集合)
//示例:先生成key,再生成Sema集合并将id返回来
key_t key;
int semid;
key = ftok("/home/beej/somefile", 'E');
semid = semget(key, 10, 0666 | IPC_CREAT)
- 可以看到这里的ftok用的是这个文件名,在实际练习中,key=123这样赋值的方式也是可以的,但是为了正规操作,推荐还是使用ftok的方式获得
- 这些刚刚生成的Sema们还没有被初始化,创造一些可以被使用的Sema不是一步就能完成的
方式2 -> 直接声明 + 直接初始化
var s:Semaphore
...
init (s,1)
- init(s,i)代表首先将关于sema的初始计数设置成 i + sema的队列={ }
- 也就是init之后关于sema的队列被设置成全空
STEP-2-利用你的Sema们
内容1 -> 利用semctl去初始化sema集合中的特定sema
一旦创建了Sema集合,把他们初始化成一些值,使得他们可以获取到资源
//方程semctl允许你去修改sema集合中某一个sema的值
int semctl(int semid, int semnum, int cmd, ... /*arg*/)
- semid是你刚刚在创建sema集合的时候返回的Sema集合的id
- senum是你想改变值的那个sema的id
- [目前还没搞清楚这个sema集合里的某个特定sema的id应该是什么样的]
- cmd是指你想对这个sema执行什么样的操作
- /args/,在需要使用的情况下是一个union semun,而semun形式如下
union semun {
int val; /* 仅仅在cmd是SETVAL的时候写上 */
struct semid_ds *buf; /* 用于 IPC_STAT 与 IPC_SET */
ushort *array; /* 用于 GETALL 用于 SETALL */
};
cmd命令集
SETVAL 将特定的sema值设定成上面的val
GETVAL 返回给定sema的值
SETALL 将sema集合的值设定成上面array数组里的值
GETALL 将sema集合的值收集起来存到上面的array数组里
IPC_RMID 将这个sema集合删除
PART-2 -> Sema工作原理解释
所以为什么要创建Sema集合?
// 首先看init(s,i) / semctl 中我们将某个sema的值设定成i
- semaphore的初值代表一次能有多少个线程能接触到共享资源
- 同时也代表了,能有多少线程执行下去不被阻挡
//其次再看down(s)的执行规则 -> if( counter(s)>0 ) counter(s)-=1;
else -> add P to queue(s)
suspend current process P
//最后再看up(s)的执行规则 -> 如果queue非空
-> resume one process in queue(s)
如果queue为空
-> counter+=1;
- 简单介绍就是说:所谓放下来(down)也就是
- -> 在count大于0的时候放进去,小于0的时候进不去(获得不到共享资源)
- -> 进去以后就可以往公共资源里添加内容了,或者修改公共资源
- 所谓升起来(up)也就是给队列中的数量加一
- 正好进入的时候减一,退出的时候加一抵消
这一套利用semctl也是可以使用的
struct sembuf[]={num,-1,SEM_UNDO}
sem(id,op,1) -> 等价于down
struct sembuf[]={num,1,SEM_UNDO}
semop(id,op,1) ->等价于up
另一种方式执行sema的初始化:semop()
struct sembuf {
ushort sem_num; ->在这个sema集合里你想操作的sema的数量
short sem_op; ->你想执行的操作
short sem_flg;
};
最后,删掉一个sema集合: semctl(semid,0,IPC_RMID)