话说小明驾车来到银行后,因为恰逢周末,大家的想法都类似,取点钱,出去happy一下,所以,人特别多。到银行领到号后,发现前面已经排了七、八十号人。幸好现在银行改进了办事策略,领到号后,我可以先处理一下我自己的事情,例如,上上网,聊聊天,找个地方坐坐,若是前面排的人比较多,我还可以出去就近办理一下短暂的业务,相比以前大家排队一直等着排队办事的效率高多了:)由于小明是嵌入式软件工程师,从事操作系统研究,职业的原因,不禁又联想到计算机中互斥机制的信号量机制,其定义如下:
struct semaphore {
spinlock_t lock;//用于是实现对count计数的保护,可以想象成一个办事窗口一个时刻只允许一个人在办事,在这个人没有办完事之前,不允许其他人上前办事,否则,银行保安人员会把你请走。
unsigned int count;//允许进入临界资源的执行路径,可以想象为银行处理窗口的个数
struct list_head wait_list;//管理所有在该信号量上睡眠的进程个数,想象成所有的拿到号等待办事的人都位于这个队列上
};
看到上面的数据结构和注解,我们就可以很容易的理解信号的这个数据结构了,现在的银行一般办事窗口都是大于1的(即count),当然,若是地方上的小县城的话,有可能出现count为1的情况。所有排队等待的人无形中都位于wait_list队列上,当一个人(进程)在一个窗口办完事情的话,银行(内核)会唤醒等待队列上的一个已经拿到号在排队的人(进程),由于大家都是按号排队的,所以,不会出现惊群效应(即一个人办完后,银行业务员一招呼:“下一个”,一帮人都来拥挤等待办事)。当然,并不是所有进入银行的人都可以到柜台办事,必须的拿到号之后,你才有到柜台办事的资格,对应到内核中就是必须的经过获取信号的动作:DOWN操作
extern void down(struct semaphore *sem);//进程无法获得信号量后,将一直处于等待状态,无法通过一些强制措施来结束该进程。就像有一些人是偏执狂一样,不取到钱决不罢休,就算此时,有人告诉他你女朋友来找你了,你先离开一下,他也会给你回答:让她先等着,取完钱再说。
extern int __must_check down_interruptible(struct semaphore *sem);//与上面类似,但是,进程可以被中断。也就是取钱的人更灵活一点。
extern int __must_check down_killable(struct semaphore *sem);//睡眠进程可以因收到一些致命性信号被唤醒而导致信号量的操作被中断。就像某人正在银行等待取钱,别人说你家XX出事了,那他一定不取钱了,先去看XX
extern int __must_check down_trylock(struct semaphore *sem);//进程获取不到信号量时,直接返回1而不进入睡眠状态,正如某人通过直接排队取不到钱时,直接回去了。
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);//进程获取不到信号量时,会等待timeout时间,若此时,还获得不了信号量,就返回-ETIME错误码。正如,某人到银行后,取到号,等了一上午,还没有排到队,这个人就回去了。难免人家会抱怨一下,像大的银行,工作人员会给你道个歉。
由于每个人(进程)的要求不同,所以,有上面一系列的down函数的变形。内核的核心实现函数__down_common大致如下:
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
struct task_struct *task = current;
struct semaphore_waiter waiter;
list_add_tail(&waiter.list, &sem->wait_list);//把获取排队号有资格到柜台办事的人(进程)放入等待队列中
waiter.task = task;
waiter.up = 0;
for (;;) {
if (signal_pending_state(state, task))
goto interrupted;
if (timeout <= 0)
goto timed_out;
__set_task_state(task, state);
spin_unlock_irq(&sem->lock);
timeout = schedule_timeout(timeout);//排队的人暂时不能进入临界区(柜台)办事,可以自由安排一下自己的活动,到可以办事的时候,再回来
spin_lock_irq(&sem->lock);
if (waiter.up)//已经可以办事了
return 0;
}
timed_out:
list_del(&waiter.list);
return -ETIME;
interrupted:
list_del(&waiter.list);
return -EINTR;
}
拿到这个票后,你就可以正大光明的在银行等待进入柜台(临界区)办理自己的业务了。等待你办完自己的业务后,你要离开你的办事柜台,给下一个等待办事的人,这时,就相当于你先前获得的号作废了,占用柜台的权力就释放了,银行办事人员会招呼下一个人,在linux内核中时通过UP操作实现的:
extern void up(struct semaphore *sem);
这个函数实现很简单,就是增加count技术,唤醒等待队列上等待的进程。代码很简单,在此不累述。
这时,小明自己都佩服自己,太牛了,这个联想太形象了。好了,既然现在还有那么多人排队,我不如开车到附近的商业区转转,想到做到,转瞬间,离开了银行。。。。。。
阅读(1984) | 评论(0) | 转发(0) |