没有什么好神秘的: 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.

时间: 2024-12-17 04:09:49

没有什么好神秘的: wait_on_page_bit的相关文章

【BZOJ-4408】神秘数 可持久化线段树

4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 475  Solved: 287[Submit][Status][Discuss] Description 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13}, 1 = 1 2 = 1+1 3 = 1+1+1 4 = 4 5 = 4+1 6 = 4+1+1 7 = 4+1+1+1 8无法表示为集合S的子集的

在c++这片神秘的大陆上

在c++这片神秘的大陆上,有一个无往而不利的地下王国,据说其手段血腥残忍,却深得民心,因为,他们是侠,是剑胆琴心,诗肠酒骨的侠客,他们不知解决了多少疑难杂症,除去了多少问题漏洞,而他们的首领->类,则是一个更为神秘的存在,传说其有九千张面孔,其手下赫赫有名的分别是:构造,析构,静态, 常,普通,当然还有一把绝世神兵this,相当于召唤门,除了静态没有,其他成员都有 ,以及其侠客团->对象. 静态就像一个妖娆的美女 , 看似静如月 ,实则动如烈火,心思玲珑,变幻莫测,潜 伏类的身侧 ,对外她是直

工信部曝光周鸿祎四款神秘新机

周鸿祎的神秘新机终于在新浪微博首露真容,不过这到底是未来的奇酷手机还是酷派新品并未得到证实.近日,工信部同时出现四款酷派神秘产品的入网信息,其中有一款采用联发科热门的MT6753八核芯片,和魅蓝Note2配置相近,不过并没有像魅族那样省成本,而是直接一款手机就可支持支持全网通,从配置来看,这款应该是主打低端品牌. 奇酷手机官方微博曝光的新手机 但未确认身份 至于具体会对应哪款手机目前还不得而知,由于有奇酷手机的介入,大部分猜测都是即将发布的是360手机,然而背面的酷派logo则表明其很可能还是酷

EJB到底是什么,真的那么神秘吗??

1. 我们不禁要问,什么是"服务集群"?什么是"企业级开发"? 既然说了EJB 是为了"服务集群"和"企业级开发",那么,总得说说什么是所谓的"服务集群"和"企业级开发"吧!这个问题其实挺关键的,因为J2EE 中并没有说明白,也没有具体的指标或者事例告诉广大程序员什么时候用EJB 什么时候不用.于是大家都产生一些联想,认为EJB"分布式运算"指得是"负载均衡

揭开观察者设计模式的神秘面纱,手把手教你写监听器

我们在写代码的时候,遇到最常用的就是监听器了.那么实际中,我们也要进行事件的监听.而有些事件是业务逻辑需要实现的,跟随事物变化动态变化的.假如说我们要实现一个事件,有位置的监听,有颜色的监听,有坐标的监听,有速度的监听,那么这么多监听的事件.那么我们就需要这么多个监听器.这些监听器如何被管理呢.我们可以创造一个类似管理员身份的神秘角色,这个角色就是一个监听器池说一个监听器池,可以移除和增加监听器.当我们触发某一事件的时候,需要这些监听器全部执行监听. 现在我们来模拟一下按钮Button的实现.

经过多次强攻之后,赫柏带领的军团不仅没能击败鲁卡斯,反而被鲁卡斯打得七零八落,赫柏终于体会到了高阶天之驱逐者的强大实力。 不过,赫柏最终还是找到了鲁卡斯的致命弱点,他发现鲁卡斯喜欢收集上古卷轴,因为上古卷轴能够让鲁卡斯获得神秘之力。 卢卡斯决定使用上古卷轴将卢卡斯引诱到绝域之门,利用绝域之门的力量消灭卢卡斯。 赫柏注意到卢卡斯喜欢收集不同的卷轴,如果总是捡到相同的上古卷轴,它的兴趣就会逐渐降低。

经过多次强攻之后,赫柏带领的军团不仅没能击败鲁卡斯,反而被鲁卡斯打得七零八落,赫柏终于体会到了高阶天之驱逐者的强大实力.不过,赫柏最终还是找到了鲁卡斯的致命弱点,他发现鲁卡斯喜欢收集上古卷轴,因为上古卷轴能够让鲁卡斯获得神秘之力.卢卡斯决定使用上古卷轴将卢卡斯引诱到绝域之门,利用绝域之门的力量消灭卢卡斯.赫柏注意到卢卡斯喜欢收集不同的卷轴,如果总是捡到相同的上古卷轴,它的兴趣就会逐渐降低.赫柏现在拥有N种不同的卷轴,每种卷轴有Ai个.现在他要将这N个卷轴分散在鲁卡斯领地到绝域之门的路上,每一种排

了解黑客的关键工具---揭开Shellcode的神秘面纱

ref:  http://zhaisj.blog.51cto.com/219066/61428/ 对于初期接触网络安全的人来说,Shellcode是很神秘的东西,对于网络攻击过程中的嗅探信息.漏洞剖析都是可以理解的,但真正利用漏洞入侵时,通过把一段二进制码送入后并执行,就可以获得目标机器的控制权,之后的事情是属于爱好者学习技术,还是黑客的行为,就看攻击者的一念之差了.Shellcode就好象神秘的武器,安全防护变得如此不堪一击.Shellcode究竟是什么样的程序?是什么特殊代码?如何才能学会编

神秘的Flash Translation Layer (FTL)

如果仅仅是SSD的使用者,一定不会在意在SSD内部居然还存在一个复杂的软件层Flash Translation Layer(FTL).其实就是这个FTL才是SSD固态硬盘的软件核心技术.正因为有了FTL,NAND Flash才能被当成硬盘来使用:文件系统才可以直接把SSD当成普通块设备来使用.由于FTL是SSD设计厂商最为重要的核心技术,因此,没有一家厂商愿意透露这方面的技术信息,并且也一直没有业内的技术规范.标准存在. FTL的重要程度在于决定了一个SSD的使用寿命.性能和可靠性.一旦FTL出

【Foreign】神秘物质 [Splay]

神秘物质 Time Limit: 10 Sec  Memory Limit: 256 MB Description Input Output Sample Input Sample Output 1 2 1 5 HINT Main idea 每个点有一个权值,维护一个结构,支持合并相邻两点,删除单点,加入单点,查询区间子集极差的最大值和最小值. Solution 首先我们可以发现,区间子集极差的最大值显然就是整个区间的最大值-区间最小值,然后区间子集极差最小值只有相邻点的才会产生贡献. 那么我们