基于上诉信号量实现可以认为,当多个(>1)进程可以进行互斥或同步合作时,一个进程会由于无法满足信号量设置的某条件而在某一位置停止,直到它接收到一个特定的信号(表明条件满足了)。为了发信号,需要使用一个称作信号量的特殊变量。为通过信号量s传送信号,信号量的V操作采用进程可执行原语semSignal(s);为通过信号量s接收信号,信号量的P操作采用进程可执行原语semWait(s);如果相应的信号仍然没有发送,则进程被阻塞或睡眠,直到发送完为止。

    ucore中信号量参照上述原理描述,建立在开关中断机制和wait_queue的基础上进行了具体实现。信号量的数据结构定义如下:

    1. int value; //信号量的当前值
    2. wait_queue_t wait_queue; //信号量对应的等待队列
    3. } semaphore_t;

    在ucore中最重要的信号量操作是P操作函数down(semaphore_t *sem)和V操作函数 up(semaphore_t *sem)。但这两个函数的具体实现是__down(semaphore_t *sem, uint32_t wait_state) 函数和__up(semaphore_t *sem, uint32_t wait_state)函数,二者的具体实现描述如下:

    ● __down(semaphore_t *sem, uint32_t wait_state, timer_t *timer):具体实现信号量的P操作,首先关掉中断,然后判断当前信号量的value是否大于0。如果是>0,则表明可以获得信号量,故让value减一,并打开中断返回即可;如果不是>0,则表明无法获得信号量,故需要将当前的进程加入到等待队列中,并打开中断,然后运行调度器选择另外一个进程执行。如果被V操作唤醒,则把自身关联的wait从等待队列中删除(此过程需要先关中断,完成后开中断)。具体实现如下所示:

    1. digraph "__down" {
    2. graph [bgcolor="#F7F5F3", fontname="Arial", fontsize="10", label="", rankdir="LR"];
    3. node [shape="box", style="filled", color="blue", fontname="Arial", fontsize="10", fillcolor="white", label=""];
    4. edge [color="#CC0044", fontname="Arial", fontsize="10", label=""];
    5. graph [bgcolor="#F7F5F3"];
    6. __N1 [color="red", label="__down"];
    7. __N2 [label="__intr_save"];
    8. __N3 [label="__intr_restore"];
    9. __N4 [label="wait_current_set"];
    10. __N5 [label="schedule"];
    11. __N6 [label="wait_in_queue"];
    12. __N7 [label="wait_queue_del"];
    13. __N8 [label="down"];
    14. __N9 [label="phi_take_forks_sema"];
    15. __N10 [label="cond_signal"];
    16. __N11 [label="phi_put_forks_sema"];
    17. __N12 [label="cond_wait"];
    18. __N13 [label="lock_mm"];
    19. __N14 [label="phi_take_forks_condvar"];
    20. __N1 -> __N2;
    21. __N1 -> __N3;
    22. __N1 -> __N4;
    23. __N1 -> __N5;
    24. __N1 -> __N6;
    25. __N1 -> __N7;
    26. __N9 -> __N8;
    27. __N10 -> __N8;
    28. __N11 -> __N8;
    29. __N12 -> __N8;
    30. __N13 -> __N8;
    31. __N14 -> __N8;
    32. __N15 -> __N8;
    33. __N8 -> __N1;
    34. }

    ● __up(semaphore_t *sem, uint32_t
    wait_state):具体实现信号量的V操作,首先关中断,如果信号量对应的wait
    queue中没有进程在等待,直接把信号量的value加一,然后开中断返回;如果有进程在等待且进程等待的原因是semophore设置的,则调用wakeup_wait函数将waitqueue中等待的第一个wait删除,且把此wait关联的进程唤醒,最后开中断返回。具体实现如下所示:

    __up相关的调用和被调用函数关系图如下所示:

    1. digraph "__up" {
    2. graph [bgcolor="#F7F5F3", fontname="Arial", fontsize="10", label="", rankdir="LR"];
    3. node [shape="box", style="filled", color="blue", fontname="Arial", fontsize="10", fillcolor="white", label=""];
    4. edge [color="#CC0044", fontname="Arial", fontsize="10", label=""];
    5. graph [bgcolor="#F7F5F3"];
    6. __N1 [color="red", label="__up"];
    7. __N2 [label="__intr_save"];
    8. __N3 [label="wait_queue_first"];
    9. __N7 [label="up"];
    10. __N8 [label="phi_test_sema"];
    11. __N9 [label="phi_take_forks_sema"];
    12. __N10 [label="cond_signal"];
    13. __N11 [label="phi_put_forks_sema"];
    14. __N12 [label="cond_wait"];
    15. __N13 [label="unlock_mm"];
    16. __N14 [label="phi_take_forks_condvar"];
    17. __N15 [label="phi_put_forks_condvar"];
    18. __N1 -> __N2;
    19. __N1 -> __N3;
    20. __N1 -> __N5;
    21. __N1 -> __N6;
    22. __N8 -> __N7;
    23. __N9 -> __N7;
    24. __N10 -> __N7;
    25. __N11 -> __N7;
    26. __N12 -> __N7;
    27. __N13 -> __N7;
    28. __N14 -> __N7;
    29. __N15 -> __N7;
    30. __N7 -> __N1;
    31. }
    • value>0,表示共享资源的空闲数
    • value=0,表示等待队列为空