文件系统中经常会有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. Sharepoint学习笔记—习题系列--70-573习题解析 -(Q63-Q65)

    Question 63You have a SharePoint farm that has more than 100 custom Features.You upgrade several Fea ...

  2. bash shell命令(1)

    本文地址:http://www.cnblogs.com/archimedes/p/bash-shell1.html,转载请注明源地址. ls命令 ls用来列出目录的内容,它是用户最常用的命令之一,ls ...

  3. android 跳转到系统设置界面的所有Intent

    Intent 的 意图: Intent intent = new Inetnt(Setings); Setings: 1. ACTION_ACCESSIBILITY_SETTINGS : // 跳转系 ...

  4. UnityShader之Shader格式篇【Shader资料1】

    关于Shader,在Unity里面我们一般叫做ShaderLab,只要你的职业是与渲染搭边,Unity就与ShaderLab有着直接的关联,你都应该试着去学会它,其实我们在新手未有入门的时候,我们总是 ...

  5. eclipse 中手动安装 subversive SVN

    为什么我选择手动安装呢?因为通过 eclipse market 下载实在太慢了.   1.下载离线安装包 http://www.eclipse.org/subversive/latest-releas ...

  6. nim2 取石头youxi

    a先把石头分堆,然后bababa的顺序取石头,只能取其中一堆中的若干颗(不能不取) 这种问题先考虑 先取者的胜态问题 (1,1)先取者必败, 所以(1,x),当x>1时可以转换为(1,1)使后取 ...

  7. Effective Java 56 Adhere to generally accepted naming conventions

    Typographical naming conventions Identifier Type Type Examples Package com.google.inject, org.joda.t ...

  8. MongoDB学习笔记——集合管理

    创建集合 使用db.createCollection(name, options) 方法创建集合 name 所创建的集合名称必选! options 可选.指定有关内存大小及索引的选项 db.creat ...

  9. 关于Redis持久化

    Redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件) RDB持久化方式是在一个特定的间隔保存某个时间点的一个数据快照. AOF(Append only file)持久化方式则会记 ...

  10. mongodb--java操作

    一.mongodb客户端 mongodb提供诸多语言的客户端,也包括java的客户端.通过这些客户端,我们可以很方便地使用编写代码的方式对mongodb进行操作.这里使用java客户端进行示例.使用j ...