zoukankan      html  css  js  c++  java
  • 没有什么好神秘的: wait_on_page_bit

     文件系统中经常会有wait_on_page_bit函数的封装,比如f2fs中就会有如下的代码:

    1431 void f2fs_wait_on_page_writeback(struct page *page, //等待页写回.
    1432 enum page_type type)
    1433 {
    1434 if (PageWriteback(page)) {
    1435 struct f2fs_sb_info *sbi = F2FS_P_SB(page);
    1436
    1437 if (is_merged_page(sbi, page, type))
    1438 f2fs_submit_merged_bio(sbi, type, WRITE);
    1439 wait_on_page_writeback(page);
    1440 }
    1441 }

    wait_on_page_writeback是会发生io重新调度的,跟踪下wait_on_page_writeback的代码:

    520 static inline void wait_on_page_writeback(struct page *page)
    521 {
    522 if (PageWriteback(page)) //如果一个页设置了PageWriteback的标志,那么就等待,看什么时候这个位置写完了
    523 wait_on_page_bit(page, PG_writeback);
    524 }

    主要的函数是wait_on_page_bit:

    520 static inline void wait_on_page_writeback(struct page *page)
    521 {
    522 if (PageWriteback(page)) //如果一个页设置了PageWriteback的标志,那么就等待,看什么时候这个位置写完了
    523 wait_on_page_bit(page, PG_writeback);
    524 }

    705 void wait_on_page_bit(struct page *page, int bit_nr)
    706 { 
    707   DEFINE_WAIT_BIT(wait, &page->flags, bit_nr);
    708 
    709   if (test_bit(bit_nr, &page->flags))
    710     __wait_on_bit(page_waitqueue(page), &wait, bit_wait_io, TASK_UNINTERRUPTIBLE);
    712 }

    wait_on_page_bit 是很重要的一个函数, 以前每次看os源码, 看到wait_on_page_bit 必然停止, 仅仅知道它大体干了件什么事情,

    但是现在要深入理解文件系统了,这一关过不了,怎么能深入? 更进一步,首先分析第一句话:

    943 #define DEFINE_WAIT_BIT(name, word, bit) 
    944 struct wait_bit_queue name = { 
    945   .key = __WAIT_BIT_KEY_INITIALIZER(word, bit), 
    946   .wait = { 
    947     .private = current, 
    948     .func = wake_bit_function, 
    949     .task_list = 
    950     LIST_HEAD_INIT((name).wait.task_list), 
    951   }, 
    952 }

    DEFINE_WAIT_BIT 是个宏, 定义了结构体wait_bit_queue:

      34 struct wait_bit_queue {
      35     struct wait_bit_key key;
      36     wait_queue_t        wait;
      37 };

    wait_bit_key是个结构体封装了bit的相关信息:包括这个数组的bitmap, 以及这个具体的wait是等待第几个bit, 还有等待的过期时间. 

      27 struct wait_bit_key {
      28     void            *flags;
      29     int         bit_nr;
      30 #define WAIT_ATOMIC_T_BIT_NR    -1
      31     unsigned long       timeout;
      32 };

    然后就是 wait_queue_t 结构体了, 这个结构体在内核代码中俯拾即是啊:

      20 struct __wait_queue {
      21     unsigned int        flags;
      22     void            *private;
      23     wait_queue_func_t   func; // 唤醒挂在task_list上的进程
      24     struct list_head    task_list;
      25 };

    好了,到这里wait_bit_queue的结构就相当明显,他在里面放置了两个元素:1)一个元素负责"bit", 2)一个元素负责queue,并且包括queue的参数;

    然后是__wait_on_bit 函数,这个函数首先判断bitmap中是否设置了我们的关心的那一个位,如果设置了这样就需要等待,实现等待的代码就是大名鼎鼎的__wait_on_bit了:

    381 /*
    382  * To allow interruptible waiting and asynchronous (i.e. nonblocking)
    383  * waiting, the actions of __wait_on_bit() and __wait_on_bit_lock() are
    384  * permitted return codes. Nonzero return codes halt waiting and return.
    385  */
    386 int __sched
    387 __wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q,
    388           wait_bit_action_f *action, unsigned mode)
    389 {
    390     int ret = 0;
    391 
    392     do {
    393         prepare_to_wait(wq, &q->wait, mode);
    394         if (test_bit(q->key.bit_nr, q->key.flags))
    395             ret = (*action)(&q->key);
    396     } while (test_bit(q->key.bit_nr, q->key.flags) && !ret);
    397     finish_wait(wq, &q->wait);
    398     return ret;
    399 }
    400 EXPORT_SYMBOL(__wait_on_bit);
    401 

    算法的主要思路仍然检测检测一个bit位, 然后如果这个位仍然没有被清除,那么非常不好意思,你就要调用action了, 这里我们注册的action是函数 bit_wait_io:

    bit_wait_ioh函数进行了一次io的调度,我们当前的进程直接bye-bye了.

    593 __sched int bit_wait_io(struct wait_bit_key *word)
    594 {
    595     if (signal_pending_state(current->state, current))
    596         return 1;
    597     io_schedule();
    598     return 0;
    599 }
    600 EXPORT_SYMBOL(bit_wait_io);
    60

    这个时候,你要怀疑了.进程被调度出去了,那么我们说好的把当前的进程放到等待队列中呢?在哪里放的?  函数__wait_on_bit里面 prepare_to_wait函数:

    159 /*
    160  * Note: we use "set_current_state()" _after_ the wait-queue add,
    161  * because we need a memory barrier there on SMP, so that any
    162  * wake-function that tests for the wait-queue being active
    163  * will be guaranteed to see waitqueue addition _or_ subsequent
    164  * tests in this thread will see the wakeup having taken place.
    165  *
    166  * The spin_unlock() itself is semi-permeable and only protects
    167  * one way (it only protects stuff inside the critical region and
    168  * stops them from bleeding out - it would still allow subsequent
    169  * loads to move into the critical region).
    170  */
    171 void
    172 prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
    173 {
    174     unsigned long flags;
    175 
    176     wait->flags &= ~WQ_FLAG_EXCLUSIVE;
    177     spin_lock_irqsave(&q->lock, flags);
    178     if (list_empty(&wait->task_list))
    179         __add_wait_queue(q, wait);
    180     set_current_state(state);
    181     spin_unlock_irqrestore(&q->lock, flags);
    182 }
    183 EXPORT_SYMBOL(prepare_to_wait);

    看到没有, __add_wait_queue会把wait结构体中的等待队列头加入到与page关联的等待队列中去, 如果这次被阻塞了,那么后面静静等待着被唤醒. 如果侥幸没有被设置位,那么finish_wait函数会将你从page关联的等待队列中拉出来啦.

    上面的分析有点随心所欲, 我们的起点在哪里来着? 再抄一遍,函数f2fs_wait_on_page_writeback

    1431 void f2fs_wait_on_page_writeback(struct page *page, //等待页写回.
    1432 enum page_type type)
    1433 {
    1434   if (PageWriteback(page)) {
    1435     struct f2fs_sb_info *sbi = F2FS_P_SB(page);
    1436 
    1437     if (is_merged_page(sbi, page, type))
    1438       f2fs_submit_merged_bio(sbi, type, WRITE);
    1439     wait_on_page_writeback(page);
    1440   }
    1441 }

    这下子明白了,如果PageWriteback设置了, 那么当前线程就要调用wait_on_page_writeback 函数, 然后进入一个while循环判断,如果依然被置位,那么你就被仍在队列中接着睡觉, 否则进入下个竞争过程.

    -----------------------------------

    有个疑问:  在上面的f2fs_wait_on_page_writeback函数中, 如果两个进程此时同时经过f2fs_wait_on_page_writeback函数, 前后脚,就差一条指令, 那么岂不是都能顺利通过f2fs_wait_on_page_writeback的屏障喽:

    那么f2fs_wait_on_page_writeback后面要做什么事情呢? 就是为了等这个页都写回了,才能干下面的事情呗,下面一般都是什么事情?

    在对这个page进行写操作之前,都要进行wait_on_write.

  • 相关阅读:
    .Net反编译软件
    Windows下Node.js安装及环境配置
    Servlet处理日期
    Servlet的文件上传
    Eclipse错误:Syntax error on tokens, delete these tokens问题解决
    Servlet中操作数据库
    Servlet的会话(Session)跟踪
    Servlet的Cookies处理
    Servlet的异常处理
    Servlet的过滤器(Filter)
  • 原文地址:https://www.cnblogs.com/honpey/p/4931962.html
Copyright © 2011-2022 走看看