linux信号量机制

Linux 中的信号量(semphore)是一种资源锁,如果有一个任务试图获得一个已经被占用的信号量时,信号量会将其推到一个等待队列中,这时处理器会重获自由从而去执行其它代码,当持有信号量的进程将信号量释放后,处于等待队列中的那个任务将会被唤醒,并将获得该信号量。

  • 信号量分类
  • 信号量结构体定义
  • 信号量定义与初始化
  • 初始化一个计值信号量
  • 获取一个信号量的函数
  • down_interruptible
  • 唤醒函数up
信号量分类
信号量分为互斥信号量,以及计数信号量,区别在于 count的值,若为1,则为互斥信号量,若大于1 则为计数信号量。

信号量结构体

?
1
2
3
4
5
struct semaphore {
        spinlock_t              lock;    //防止其它进程同时访问该信号量,并禁止中断
        unsigned int            count; //计值信号量
        struct list_head        wait_list; //加入等待队列的指针
};

信号量定义与初始化

?
1
2
3
4
5
6
7
8
9
#define __SEMAPHORE_INITIALIZER(name,n)                                \
{                                                                       \
        .lock           =__SPIN_LOCK_UNLOCKED((name).lock),           \
        .count          = n,                                            \
        .wait_list      =LIST_HEAD_INIT((name).wait_list),            \
}
  
#define DEFINE_SEMAPHORE(name)  \
         struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)\
初始化一个计值信号量

?
1
2
3
4
5
6
static inline void sema_init(structsemaphore *sem, int val)
{
        static struct lock_class_key __key;
        *sem = ( struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
        lockdep_init_map(&sem->lock.dep_map, "semaphore->lock" , &__key, 0);
}
获取一个信号量

?
1
2
3
4
5
6
extern void down(structsemaphore *sem);   //内核说已经过时了
extern int __must_check down_interruptible( struct semaphore *sem); //acquirethe semaphore unless interrupted, If the sleep is interrupted by a signal, thisfunction will return -EINTR.
extern int __must_check down_killable( struct semaphore *sem); //acquirethe semaphore unless killed, If the sleep is interrupted by a fatal signal,this function will return -EINTR
extern int __must_check down_trylock( struct semaphore *sem); //try toacquire the semaphore, without waiting
extern int __must_check down_timeout( struct semaphore *sem, long jiffies); //acquire the semaphore within a specified time, If the semaphore is notreleased within the specified number of jiffies, this function returns -ETIME.
extern void up(structsemaphore *sem);
down_interruptible代码分析

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
  *down_interruptible - acquire the semaphore unless interrupted
  *@sem: the semaphore to be acquired
  *
  *Attempts to acquire the semaphore.  If nomore tasks are allowed to
  *acquire the semaphore, calling this function will put the task to sleep.
  * If the sleep is interrupted by a signal, thisfunction will return -EINTR.
  * Ifthe semaphore is successfully acquired, this function returns 0.
  */
int down_interruptible( struct semaphore*sem)
{
        unsigned long flags;
        int result = 0;
  
        spin_lock_irqsave(&sem->lock, flags);  //获得锁,禁止中断,防止该函数还没睡眠下来,就被中断了,
        if (likely(sem->count > 0))
                 sem->count--;      //计数值减1
        else
                 result = __down_interruptible(sem);  //在切换前会开中断,
        spin_unlock_irqrestore(&sem->lock, flags); //切换后开中断
  
        return result;
}
EXPORT_SYMBOL(down_interruptible);
  
static noinline int __sched __down_interruptible( struct semaphore *sem)
{
        return __down_common(sem, TASK_INTERRUPTIBLE,MAX_SCHEDULE_TIMEOUT);
}
TASK_INTERRUPTIBLE 可被信号中断唤醒
#define MAX_SCHEDULE_TIMEOUT    LONG_MAX 类似于无限睡下去
/*
  *Because this function is inlined, the 'state' parameter will be
  *constant, and thus optimised away by the compiler.  Likewise the
  *'timeout' parameter for the cases without timeouts.
  */
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)) //如果该进程被信号中断,则退出,task是在哪被设置了这个任号的呢?遗留问题
                         goto interrupted;
                 if (timeout <= 0)  //定时到了
                        goto timed_out;
                 __set_task_state(task, state);  //将task的TASK_RUNNING状态变成TASK_INTERRUPTIBEU状态,
                                    当该进程被切换出去时,如果没有被置为TASK_RUNNING状态,调度器则不会在调度它了
                spin_unlock_irq(&sem->lock); //吻合前面的spin_lock_irqsave,开中断
                 timeout =schedule_timeout(timeout); //将进程切换出去
                spin_lock_irq(&sem->lock); //关中断,吻合后面的spin_unlock_irqsave
                 if (waiter.up)
                         return 0;
        }
  
  timed_out:
        list_del(&waiter.list);
        return -ETIME;
  
  interrupted:
        list_del(&waiter.list);
        return -EINTR;
}
struct semaphore_waiter {
        struct list_head list;
        struct task_struct *task; //等待信号量的任务
        int up;
};

timeout= schedule_timeout(timeout); //将进程切换出去
当定时timeout时间到过后,返回值timeout变为0, 则__down_common返回-ETIME.
现在假设有4个进程A B C D, A已经获得一个信号量global,而B C D正在按顺序等待该信号量(如果确实是这样,会不会有点傻

唤醒函数,释放信号量

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
  * up- release the semaphore
  *@sem: the semaphore to release
  *
  *Release the semaphore.  Unlike mutexes,up() may be called from any
  *context and even by tasks which have never called down().
  */
void up( struct semaphore *sem)
{
        unsigned long flags;
  
        spin_lock_irqsave(&sem->lock, flags);
        if (likely(list_empty(&sem->wait_list)))   //
                 sem->count++; //如果队列为空的话,则将计数值加1
        else
                 __up(sem);   //唤醒
        spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);
  
static noinline void __sched __up(structsemaphore *sem)
{
        struct semaphore_waiter *waiter =list_first_entry(&sem->wait_list, struct semaphore_waiter, list);
        list_del(&waiter->list); //根据队列,先进先出的原则,第一个down的进程会被先唤醒,将该进程从信号量的等待队列中删除
        waiter->up = 1;   //与down里的if(waiter.up)对应
        wake_up_process(waiter->task); //唤醒进程,将其加入到可运行的进程链表中进程state的状态由TASK_WAKING逐渐变成TASK_RUNNING中去
}
/**
  *wake_up_process - Wake up a specific process
  *@p: The process to be woken up.
  *
  *Attempt to wake up the nominated process and move it to the set of runnable
  *processes.  Returns 1 if the process waswoken up, 0 if it was already
  *running.
  *
  * Itmay be assumed that this function implies a write memory barrier before
  *changing the task state if and only if any tasks are woken up.
  */
int wake_up_process( struct task_struct *p)
{
        return try_to_wake_up(p, TASK_ALL, 0);
}
EXPORT_SYMBOL(wake_up_process);

你可能感兴趣的:(linux信号量机制)