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);
|
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);
|