0%

L18-信号量的代码实现

  • 由于 linux-0.11 使用的是单核 CPU,所以我们可以利用关中断法来对信号量临界区进行保护,信号量的代码实现如下

L18 信号量的代码实现

应用层信号量代码实现

  • 由于 linux-0.11 使用的是单核 CPU,所以我们可以利用关中断法来对信号量临界区进行保护,信号量的代码实现如下图:

    • 首先我们应该要有一个能够表示信号量的东西,这里是用 semtable[i] 来表示信号量,semtable 是一个内核中的信号量表,其中每一个”数据“都包含名称 name 、资源个数 value 和 PCB(即这里的 queue),通过 sys_sem_open 来从信号量表中找到对应于 name 的信号量,比如 sd=sem_open("empty") 就利用系统调用 sem_open 从内核中找到了名为 empty 的信号量;
    • 在找到了信号量以后,就要通过 P(semaphore s)V(semaphore s) 对其进行操作,例如这里的 sys_sem_wait(int sd) 就对应于 P(semaphore s) ,首先要 cli() 关中断,以对 sd 信号量进行保护,然后进行 P(semaphore s) 对应的操作,操作结束后,再 sti() 关中断,就在代码上实现了 P(semaphore s)V(semaphore s) 同理。

操作系统内部的信号量应用

  • 信号量不仅可以用于上层应用,使上层应用的进程同步,也可以用于操作系统内部,来保证操作系统内部可以以正确的顺序运行。

  • 以操作系统内核读磁盘块为例,在进程启动磁盘读以后就要睡眠,等待磁盘读完后由磁盘中断将其唤醒,sleep_on 就是让进程睡眠的函数,同样用关中断法来保护“信号量”,然后调用 sleep_on 函数。

    • sleep_on 函数是将当前进程加入阻塞队列的队首,并将当前进程的状态改为阻塞态(不可中断的睡眠状态),然后通过 schedule() 调度别的进程。

      下面的图就展示了如何将当前进程加入阻塞队列的队首

    • sleep_on 函数用于让进程睡眠,而 wake_up 函数则用于唤醒进程,如下图所示的 wake_up 函数,通过 (**p).state=0 将进程的状态改为就绪态,并通过 *p=NULL 将处于就绪态的进程从阻塞队列中移出,这样就唤醒了进程。

      由于进程在陷入睡眠时,是从 schedule 函数出去的,所以当进程被唤醒时,就要从中断出去的地方继续向下执行,于是 if(tmp) tmp->state=0;将阻塞队列中的下一个睡眠进程唤醒,形成链式反应,最终将阻塞队列中的所有进程都唤醒了,所有被唤醒的进程都处于就绪态,这时就会根据优先级来决定是将哪个进程变为运行态。

      所以为什么 lock_buffer(buffer_head *bh)while(bh->b_lock) 就说得通了,因为在选择了一个进程使其成为运行态以后,bh->b_lock=1,相当于除了处于运行态的进程,其他被唤醒处于就绪态的进程要再一次陷入睡眠,等待下一次被唤醒。

---------------The End---------------