📄 4.html
字号:
jle 2f 如果小于或等于0,表示该semaphore有进程在等待,就去调用__up_wakeup<br>
1:<br>
.section .text.lock,"ax"<br>
2: call __up_wakeup<br>
jmp 1b<br>
.previous<br>
...<br>
__down_failed:<br>
pushl % eax<br>
pushl % edx<br>
pushl % ecx ; eax,edx,ecx是3个可用于函数参数的寄存器<br>
call __down<br>
popl % ecx<br>
popl % edx<br>
popl % eax<br>
ret<br>
__up_wakeup:<br>
pushl % eax<br>
pushl % edx<br>
pushl % ecx<br>
call __up<br>
popl % ecx<br>
popl % edx<br>
popl % eax<br>
ret<br>
; semaphore.c<br>
void __down(struct semaphore * sem)<br>
{<br>
struct task_struct *tsk = current;<br>
DECLARE_WAITQUEUE(wait, tsk);<br>
tsk->state = TASK_UNINTERRUPTIBLE;<br>
add_wait_queue_exclusive(&sem->wait, &wait);<br>
// 将当前进程加入到该semaphore的等待队列中<p>
spin_lock_irq(&semaphore_lock);<br>
sem->sleepers++;<br>
for (;;) {<br>
int sleepers = sem->sleepers;<p>
/*<br>
* Add "everybody else" into it. They aren't<br>
* playing, because we own the spinlock.<br>
*/<p>
// atomic_add_negative(int i,atomic_t *v)将i + v->counter相加,<br>
// 结果为负返回1,否则返回0<br>
if (!atomic_add_negative(sleepers - 1, &sem->count)) {<br>
// 如果(sleepers - 1 + sem->count.counter)非负,则说明<br>
// semaphore已经被释放,可以返回<br>
sem->sleepers = 0;<br>
break;<br>
}<br>
sem->sleepers = 1; /* us - see -1 above */<p>
spin_unlock_irq(&semaphore_lock);<br>
// 当semaphore被up()唤醒时,schedule()返回<br>
schedule();<br>
// 虽然已线程被up恢复,但为防止碰巧又有一个线程获得了semaphore,<br>
// 因此将它们放在循环体中<br>
tsk->state = TASK_UNINTERRUPTIBLE;<br>
spin_lock_irq(&semaphore_lock);<br>
}<br>
spin_unlock_irq(&semaphore_lock);<br>
// 该进程获得了semaphore,将它从等待队列中删除<br>
remove_wait_queue(&sem->wait, &wait);<br>
tsk->state = TASK_RUNNING;<br>
// 为什么这里要调用wake_up,是因为调用它没有副作用从而防止潜在的死锁吗?<br>
wake_up(&sem->wait);<br>
}<br>
void __up(struct semaphore *sem)<br>
{<br>
扩展为<br>
__wake_up_common(&sem->wait,TASK_UNINTERRUPTIBLE|TASK_INTERRUPTIBLE,1,0);<br>
唤醒队列中第1个进程,即将第1个进程放入运行队列<br>
wake_up(&sem->wait);<br>
}<br>
; sched.c<br>
static inline void __wake_up_common (wait_queue_head_t *q, unsigned int<br>
mode,<br>
int nr_exclusive, const int sync)<br>
{<br>
struct list_head *tmp, *head;<br>
struct task_struct *p;<br>
unsigned long flags;<p>
if (!q)<br>
goto out;<p>
wq_write_lock_irqsave(&q->lock, flags);<p>
#if WAITQUEUE_DEBUG<br>
CHECK_MAGIC_WQHEAD(q);<br>
#endif<p>
head = &q->task_list;<br>
#if WAITQUEUE_DEBUG<br>
if (!head->next || !head->prev)<br>
WQ_BUG();<br>
#endif<br>
tmp = head->next;<br>
while (tmp != head) {<br>
unsigned int state;<br>
wait_queue_t *curr = list_entry(tmp, wait_queue_t,<br>
task_list);<br>
tmp = tmp->next;<p>
#if WAITQUEUE_DEBUG<br>
CHECK_MAGIC(curr->__magic);<br>
#endif<br>
p = curr->task;<br>
state = p->state;<br>
if (state & mode) {<br>
#if WAITQUEUE_DEBUG<br>
curr->__waker = (long)__builtin_return_address(0);<br>
#endif<br>
if (sync)<br>
wake_up_process_synchronous(p);<br>
else<br>
wake_up_process(p);<br>
if ((curr->flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)<br>
break;<br>
}<br>
}<br>
wq_write_unlock_irqrestore(&q->lock, flags);<br>
out:<br>
return;<br>
}<br>
; sched.c<br>
inline void wake_up_process(struct task_struct * p)<br>
{<br>
unsigned long flags;<p>
/*<br>
* We want the common case fall through straight, thus the goto.<br>
*/<br>
spin_lock_irqsave(&runqueue_lock, flags);<br>
p->state = TASK_RUNNING;<br>
if (task_on_runqueue(p))<br>
goto out;<br>
add_to_runqueue(p);<br>
reschedule_idle(p);<br>
out:<br>
spin_unlock_irqrestore(&runqueue_lock, flags);<br>
}<br>
; sched.c<br>
static inline void wake_up_process_synchronous(struct task_struct * p)<br>
{<br>
unsigned long flags;<p>
/*<br>
* We want the common case fall through straight, thus the goto.<br>
*/<br>
spin_lock_irqsave(&runqueue_lock, flags);<br>
p->state = TASK_RUNNING;<br>
if (task_on_runqueue(p))<br>
goto out;<br>
add_to_runqueue(p);<br>
out:<br>
spin_unlock_irqrestore(&runqueue_lock, flags);<br>
}<br>
; sched.h<br>
static inline int task_on_runqueue(struct task_struct *p)<br>
{<br>
return (p->run_list.next != NULL);<br>
}<br>
; sched.c<br>
static inline void add_to_runqueue(struct task_struct * p)<br>
{<br>
list_add(&p->run_list, &runqueue_head);<br>
nr_running++;<br>
}<br>
static LIST_HEAD(runqueue_head);<p>
; fork.c<br>
void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *<br>
wait)<br>
{<br>
unsigned long flags;<p>
wq_write_lock_irqsave(&q->lock, flags);<br>
wait->flags = WQ_FLAG_EXCLUSIVE;<br>
__add_wait_queue_tail(q, wait);<br>
wq_write_unlock_irqrestore(&q->lock, flags);<br>
}<br>
; wait.h<br>
static inline void __add_wait_queue_tail(wait_queue_head_t *head,<br>
wait_queue_t *new)<br>
{<br>
#if WAITQUEUE_DEBUG<br>
if (!head || !new)<br>
WQ_BUG();<br>
CHECK_MAGIC_WQHEAD(head);<br>
CHECK_MAGIC(new->__magic);<br>
if (!head->task_list.next || !head->task_list.prev)<br>
WQ_BUG();<br>
#endif<br>
list_add_tail(&new->task_list, &head->task_list);<br>
}<br>
正执行调度的函数是schedule(void),它选择一个最合适的进程执行,并且真正进行上下文切换,<br>
使得选中的进程得以执行。而reschedule_idle(struct task_struct *p)的作用是为进程选择<br>
一个合适的CPU来执行,如果它选中了某个CPU,则将该CPU上当前运行进程的need_resched标志<br>
置为1,然后向它发出一个重新调度的处理机间中断,使得选中的CPU能够在中断处理返回时执行<br>
schedule函数,真正调度进程p在CPU上执行。在schedule()和reschedule_idle()中调用了goodness()<br>
函数。goodness()函数用来衡量一个处于可运行状态的进程值得运行的程度。此外,在schedule()<br>
函数中还调用了schedule_tail()函数;在reschedule_idle()函数中还调用了reschedule_idle_slow()。<p>
<p>
<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -