文件系统中经常会有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 }

 void wait_on_page_bit(struct page *page, int bit_nr)
{
  DEFINE_WAIT_BIT(wait, &page->flags, bit_nr);   if (test_bit(bit_nr, &page->flags))
    __wait_on_bit(page_waitqueue(page), &wait, bit_wait_io, TASK_UNINTERRUPTIBLE);
}

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

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

 #define DEFINE_WAIT_BIT(name, word, bit) \
struct wait_bit_queue name = { \
  .key = __WAIT_BIT_KEY_INITIALIZER(word, bit), \
  .wait = { \
    .private = current, \
    .func = wake_bit_function, \
    .task_list = \
    LIST_HEAD_INIT((name).wait.task_list), \
  }, \
}

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

   struct wait_bit_queue {
struct wait_bit_key key;
wait_queue_t wait;
};

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

   struct wait_bit_key {
void *flags;
int bit_nr;
#define WAIT_ATOMIC_T_BIT_NR -1
unsigned long timeout;
};

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

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

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

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

 /*
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 */
int __sched
__wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q,
wait_bit_action_f *action, unsigned mode)
{
int ret = ; do {
prepare_to_wait(wq, &q->wait, mode);
if (test_bit(q->key.bit_nr, q->key.flags))
ret = (*action)(&q->key);
} while (test_bit(q->key.bit_nr, q->key.flags) && !ret);
finish_wait(wq, &q->wait);
return ret;
}
EXPORT_SYMBOL(__wait_on_bit);

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

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

 __sched int bit_wait_io(struct wait_bit_key *word)
{
if (signal_pending_state(current->state, current))
return ;
io_schedule();
return ;
}
EXPORT_SYMBOL(bit_wait_io);

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

 /*
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 */
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags; wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);

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

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

 void f2fs_wait_on_page_writeback(struct page *page, //等待页写回.
enum page_type type)
{
  if (PageWriteback(page)) {
1435     struct f2fs_sb_info *sbi = F2FS_P_SB(page);     if (is_merged_page(sbi, page, type))
1438       f2fs_submit_merged_bio(sbi, type, WRITE);
    wait_on_page_writeback(page);
  }
}

这下子明白了,如果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.

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

  1. 神秘代理-Proxy

    前言: 代理模式作为常见的设计模式之一,在项目开发中不可或缺.本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: [假设有个关于汽车移动(move)的计时需求]设计:Movea ...

  2. 深入理解javascript对象系列第三篇——神秘的属性描述符

    × 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...

  3. [BZOJ4408][Fjoi 2016]神秘数

    [BZOJ4408][Fjoi 2016]神秘数 试题描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1 ...

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

    在c++这片神秘的大陆上,有一个无往而不利的地下王国,据说其手段血腥残忍,却深得民心,因为,他们是侠,是剑胆琴心,诗肠酒骨的侠客,他们不知解决了多少疑难杂症,除去了多少问题漏洞,而他们的首领-> ...

  5. 揭开GrowingIO无埋点的神秘面纱

    揭开GrowingIO无埋点的神秘面纱   早在研究用户行为分析的时候,就发现国内的GrowingIO在宣传无埋点技术,最近正好抽出时间来研究一下所谓的无埋点到底是什么样的. 我分六部分来分析一下无埋 ...

  6. [bzoj4408][Fjoi2016]神秘数

    Description 一个可重复数字集合$S$的神秘数定义为最小的不能被$S$的子集的和表示的正整数. 例如$S={1,1,1,4,13}$, $1=1$, $2=1+1$, $3=1+1+1$, ...

  7. 揭开Sass和Compass的神秘面纱

    揭开Sass和Compass的神秘面纱 可能之前你像我一样,对Sass和Compass毫无所知,好一点儿的可能知道它们是用来作为CSS预处理的.那么,今天请跟我一起学习下Sass和Compass的一些 ...

  8. Java实现批量下载《神秘的程序员》漫画

    上周看了西乔的博客“西乔的九卦”.<神秘的程序员们>系列漫画感觉很喜欢,很搞笑.这些漫画经常出现在CSDN“程序员”杂志末页的,以前也看过一些. 后来就想下载下来,但是一张一张的点击右键“ ...

  9. ASP.NET 运行时详解 揭开请求过程神秘面纱

    对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...

随机推荐

  1. Java中的守护线程和非守护线程(转载)

    <什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...

  2. iOS 内存管理(一)之基础知识介绍

    1,什么是引用计数 所有OC对象都有一个计数器,叫做引用计数,引用计数就是目前有几个对象在使用该对象(持有该对象的引用): 2,什么是对象所有权 A对象拥有B对象的引用,A对象拥有B对象的所有权: 3 ...

  3. iOS 解决表单被键盘遮住的问题

    问题 处理表单的时候,一定会碰到的就是输入控件被键盘遮住的问题,如图: 实例 左边是普通表单,中间是2B表单,右边是文艺表单. 分析 处理这种问题无非就是2个步骤: 键盘弹出时,缩小UITableVi ...

  4. WPF x名称空间

    X是映射XML名称空间时给他取的名字.X名称空间里面的成员是专门给XAML编译器看的.用来引导XAML编译器把XAML代码编译成CLR代码的,他是取的XAML的首字母X. XAML有自己的编译器,会吧 ...

  5. LeetCode 1 Two Sum(二分法)

    题目来源:https://leetcode.com/problems/two-sum/ Given an array of integers, find two numbers such that t ...

  6. navicat 连接sqlserver提示要安装 sql server native client

    navicat 连接sqlserver提示要安装 sql server native client 解决方法:其实navicat自带sqlncli_x64.msi,就在安装目录下,安装后问题解决!

  7. 通过代码实现gz压缩,并保持原来的文件名

    写这篇博客主要是为了记录一下这两天来的研究成果-gz带原有文件名压缩.首先要说的是这个解决方案不是通过调用命令gzip来做的,而是通过java代码来实现的,其中用到了apache的common com ...

  8. web前端开发教程系列-2 - 前端开发书籍分享(转)

    目录: 前言 一. CSS 二. JavaScript 三. jQuery 四. 后记   前言 前端书籍在每个商城或书架上面都是琳琅满目,很多初学者又不能很好的判断书的质量或层次.因为今天给同学们分 ...

  9. 虚拟机Linux----Ubuntu1204----设置固定Ip

    1.介绍 环境:ubuntu版本是12.04,虚拟机是Oracle Vm VirtualBox 2.说明 需求:现在已经安装了一个ubuntu系统,网络配置是默认选择桥接,可以上网,物理机可以连接虚拟 ...

  10. 入门 ARM 汇编(一)—— 知识铺垫

    我读着史铁生的散文,零碎的牵扯起我生命中不曾出现过的记忆,一如北方的黄山厚土之中悠忽而来的忧伤的信天游,那些灿若信仰一样的阳光以及阳光下虔诚的子民.我想有一次远行,于细碎流淌的时光与路途之中,观察所有 ...