wait_on_buffer 解析

Linux-0.12代码如下:

static inline void wait_on_buffer(struct buffer_head * bh)
{
    cli();    /*关中断*/
    while (bh->b_lock)    /*判断刚才申请的缓冲区是否加锁了*/
        sleep_on(&bh->b_wait);    /*由于进程1接下来的执行需要改缓冲块
                        的支持,所有将进程1挂起在该缓冲块的等待队列上*/
    sti();
}

问自己的问题:

  • 为什么需要cli()和sti(), 去掉行不行
  • while(bh->b_lock)该为if(bh->b_lock)行不行
  • 当调用wait_on_buffer的进程在关中断期间被挂起了,其他进程还能不能在系统中正常运行

分析如下:

  1. 首先要明确wait_on_buffer是工作在内核态的函数,它的调用有两种可能性

    • 用户态进程向设备请求数据,进入内核态而触发的
    • 内核态本身的某个操作触发的

      即:它一定是有某个处于内核态的进程调用的

  2. cli()和sti()为wait_on_buffer起到临界区的作用,没有这两个关中断与开中断的函数行不行?
    由于wait_on_buffer工作在内核态,cli()关闭了所有可屏蔽中断,包括时钟中断、硬盘中断等,这也就意味着:

    • 当前执行wait_on_buffer的进程不会因为时间片到了而被调度器轮转出去,保证了while(bh->b_lock)的执行,该函数执行期间也不会有其他进程对bh->b_lock进行修改,避免了一些不必要的错误
    • b_lock表示用于块设备驱动程序访问设备中数据时,并且会被驱动程序解锁。因此,当该进程因申请的缓冲区被锁住而挂起后,当块设备(如硬盘)数据到达时,会对b_lock解锁,该挂起的进程通过重新调度而获得该缓冲区,注意:这里并没有对该缓冲区加锁
  3. 如果将while(bh->b_lock)该为if(bh->b_lock),假设当前进程为A,bh->lock已被锁住,那么进程A进入睡眠状态,挂起在hb的等待队列上,并通过调度器调入进程B运行。在进程B运行期间,由于外部块设备传输数据完成而将hb->lock解锁,当buffer unlock后,唤醒队列头的进程A,而此时时间片在进程B中,B发现了该buffer unlock, 于是lock了它,然后主动放弃时间片,进入重调度。调度器将A调入,由于是if(bh->lock),因此不再进行bh->lock锁的检查,而直接开中断,继续运行。此时A、B两进程的状态为:A处于运行状态,并认为buffer hd没有被其他进程锁住,继续对hd进程操作; 进程B处于就绪/阻塞/挂起状态,并且锁住了buffer hd,这样就造成了系统的不一致。如果使用while(bh->b_lock),进程A被唤醒重新调入时,需要再一次对bh->b_lock进行检查,如果发现仍然被锁住,便再次进入挂起状态,从而保证了系统的一致性

  4. 对于问题: 当调用wait_on_buffer的进程在关中断期间被挂起了,其他进程还能不能在系统中正常运行?
    这个问题的答案是不会影响其他进程的正常运行。因为sleep_on将该进程挂起后,会触发调度器进行一次重调度,当选择新进程放入CPU运行时,需要对当前进程的上下文进程保存,当然也包括EFLAGS,而cli()改变的正是EFLAGS中的IF位。也就是说新切换进来的进程使用的是自己原有的EFLAGS,原有的IF是开中断状态,此时就是开中断状态,原有是关中断状态,此时就是关中断状态,和之前进程的中断是否开启无关。

  5. 另外,当cli()关中断时,会屏蔽时钟中断,这就会造成时钟信息丢失的问题,内核解决这个问题的办法是:关中断的时候jiffies会丢失,在后来会通过去读TSC寄存器进行重新设置! 调用rt_request_linux_irq注册一个Linux下的中断服务程序recover_jiffies,这个中断程序和Linux的时钟中断服务程序共享时钟中断,recover_jiffies用于补偿Linux所丢失的时钟中断(因为可能由于实时任务的运行,使Linux很长一段时间得不到运行的机会,无法响应时钟中断)。

你可能感兴趣的:(Linux,2.6.xx内核分析)