信号量工作原理

一、内核相关文件为include/linux/semaphore.h和kernel/semaphore.c

二、主要结构体


   
   
  1. struct semaphore {
  2. raw_spinlock_t lock;
  3. unsigned int count;
  4. struct list_head wait_list;
  5. };

结构体成员变量解读:

1、lock主要用于保护count和wait_list链表的访问;

2、count记录信号量等待进程的计数;

3、wait_list用于记录等待信号量的进程,串成一个链表来统一管理;

三、相关接口

1、初始化接口


   
   
  1. static inline void sema_init(struct semaphore *sem, int val)
  2. {
  3. static struct lock_class_key __key;
  4. *sem = ( struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
  5. lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
  6. }

接口主要完成互斥锁、信号量计数初始值、链表头结点的初始化操作;

2、down信号量接口

获取信号量接口总共有五个:


   
   
  1. extern void down(struct semaphore *sem);
  2. extern int __must_check down_interruptible(struct semaphore *sem);
  3. extern int __must_check down_killable(struct semaphore *sem);
  4. extern int __must_check down_trylock(struct semaphore *sem);
  5. extern int __must_check down_timeout(struct semaphore *sem, long jiffies);

1)down接口;uninterruptible

2)down信号量接口,一般都建议使用down_interruptible,此接口将会把当前任务状态设置为TASK_INTERRUPTIBLE,意味着一旦满足条件,任务将会在设定的超时时间到来之前被调度;

3)down_killable:可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR;

4)down_trylock:此接口上来就去获取信号量,不会设置超时时间,不做任何等待,获取到就返回0,获取不到就返回1;

5)down_timeout:如果上来就获取到信号量,则直接返回,获取不到则睡眠等待设置的超时时间时长;

这五个接口,除了down_trylock,最终都调用了__down_common这个接口,只不过传入的参数不同而已


   
   
  1. static noinline void __sched __down( struct semaphore *sem)
  2. {
  3. __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
  4. }
  5. static noinline int __sched __down_interruptible( struct semaphore *sem)
  6. {
  7. return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
  8. }
  9. static noinline int __sched __down_killable( struct semaphore *sem)
  10. {
  11. return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
  12. }
  13. static noinline int __sched __down_timeout( struct semaphore *sem, long jiffies)
  14. {
  15. return __down_common(sem, TASK_UNINTERRUPTIBLE, jiffies);
  16. }

我们可以看一下__down_common这个接口:首先会把当前进程挂接到信号量的Wait_list链表中,然后会通过schedule_timeout调度其他进程,当前任务挂起;


   
   
  1. struct semaphore_waiter {
  2. struct list_head list;
  3. struct task_struct *task;
  4. int up;
  5. };

   
   
  1. static inline int __sched __down_common( struct semaphore *sem, long state,
  2. long timeout)
  3. {
  4. struct task_struct *task = current;
  5. struct semaphore_waiter waiter;
  6. list_add_tail(&waiter.list, &sem->wait_list);
  7. waiter.task = task;
  8. waiter.up = 0;
  9. for (;;) {
  10. if ( signal_pending_state(state, task))
  11. goto interrupted;
  12. if (timeout <= 0)
  13. goto timed_out;
  14. __set_task_state(task, state);
  15. raw_spin_unlock_irq(&sem->lock);
  16. timeout = schedule_timeout(timeout);
  17. raw_spin_lock_irq(&sem->lock);
  18. if (waiter.up)
  19. return 0;
  20. }
  21. timed_out:
  22. list_del(&waiter.list);
  23. return -ETIME;
  24. interrupted:
  25. list_del(&waiter.list);
  26. return -EINTR;
  27. }


3、释放信号量接口:此接口主要做了以下工作,首先判断当前信号量的wait_list是否为空,如果为空,即没有任何进程等待此信号量,则直接sem->count++,否则,则把wait_list中第一个节点摘除,通过wake_up_process放置到激活任务队列中,等待执行。


   
   
  1. extern void up(struct semaphore *sem);
  2. void up(struct semaphore *sem)
  3. {
  4. unsigned long flags;
  5. raw_spin_lock_irqsave(&sem->lock, flags);
  6. if ( likely( list_empty(&sem->wait_list)))
  7. sem->count++;
  8. else
  9. __up(sem);
  10. raw_spin_unlock_irqrestore(&sem->lock, flags);
  11. }
  12. static noinline void __sched __up( struct semaphore *sem)
  13. {
  14. struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
  15. struct semaphore_waiter, list);
  16. list_del(&waiter->list);
  17. waiter->up = 1;
  18. wake_up_process(waiter->task);
  19. }


 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值