内存页的wait-wake机制

关于内存也在分配时的等待唤醒机制,include/linux/mmzone.h 389行的注释为我们指出了用户可用 的最基本的两个接口函数,__wait_on_page_locked /  unlock_page,定义在mm/filemap.c中。但经过检查发现,真正能提供使用的其实是wait_on_page_locked函数,并没有__wait_on_page_locked 的实现。其作为一个定义在头文件中的inline函数,可以被包含此头文件的c文件调用,其实现主要是通过wait_on_page_bit函数。
* __wait_on_page_locked() and unlock_page() in mm/filemap.c, are the
* primary users of these fields, and in mm/page_alloc.c
* free_area_init_core() performs the initialization of them.

wait_on_page_bit的调用流程如下:

     看起来很简单,不过里面暗藏玄机。
     1)DEFINE_WAIT_BIT做了很多事情
          它是标准的Linux等待机制中DEFINE_WAIT的变种。其中包含了标准的等待队列的成员变量wait_queue_t。另外包含了一个bit,通过word和bit_nr指定。
     2)__wait_on_bit通过page_waitqueue来找到队列头,这里面就牵涉到哈希的问题,具体可以参考linux内核哈希查找(1)_zhe_wang-ChinaUnix博客,这里简要介绍一下。
          哈希表共有表项size个,2^size >= pages / PAGES_PER_WAITQUEUE = pages/256
          用来计算哈希值的wait_table_bits,是size中第一个1位的索引
          哈希值的计算就是通过,该页的page结构指针(在mem_map数组中),wait_table_bits
          所谓的哈希计算就是将这两个变量,通过一个巧妙的计算,生成某个区间内的随机值,然后散列到哈希表中,如果哈希冲突,则同一个表项采用链表的形式表示若干个成员。所以哈希值又叫散列值。
          这里的哈希计算方法,就是(val * GOLDEN_RATIO_PRIME_32)  >>(32 - bits),GOLDEN_RATIO_PRIME_32是个很特别的值,经过研究,固定下来的,用它乘过之后,溢出后剩下来的值,随机性比较大。因为bits是size的第一个1,所以右移后的值一定<size,正好符合我们的要求。
     3)在__wait_on_bit中会不断测试page->flags的PageLocked位,即第0位。然后会在真正要休眠的时候调用传入的sync_page,这个函数不复杂,但牵涉很广,和address_space结构有关。
     4)在做完一系列的sync之后,最后调用io_schedule,让出处理器,进行等待。


unlock_page一样通过page结构指针和PageLocked位,找到哈希表中的等待队列。但一个哈希表项可以有256个page等在其中,所以不确定是怎么唤醒想要的那个page的。可能和wait->func=wake_bit_function有关。牵扯到wait queue机制。