linux page 管理分析(1)

 681/*
 682 * Get the lock to a page atomically.
 683 */
 684struct page * __find_lock_page (struct address_space *mapping,
 685                                unsigned long offset, struct page **hash)
 686{
 687        struct page *page;
 688
 689        /*
 690         * We scan the hash list read-only. Addition to and removal from
 691         * the hash-list needs a held write-lock.
 692         */
 693repeat:
 694        spin_lock(&pagecache_lock);
 695        page = __find_page_nolock(mapping, offset, *hash);
 696        if (page) {
 697                page_cache_get(page);
 698                spin_unlock(&pagecache_lock);
 699
 700                lock_page(page);
 701
 702                /* Is the page still hashed? Ok, good.. */
 703                if (page->mapping)
 704                        return page;
 705
 706                /* Nope: we raced. Release and try again.. */
 707                UnlockPage(page);
 708                page_cache_release(page);
 709                goto repeat;
 710        }
 711        spin_unlock(&pagecache_lock);
 712        return NULL;
 713}

代码如上, 30来行, 很简单。 

这段代码反映了页面管理中的一个很重要的标志位 PG_locked, 也就是 lock_page(page) 那一句。 下面注释在没有背景的情况下, 比较难理解, 什么叫 still hashed, 什么时候又回不 hashed 呢? 从第一遍看相关代码时疑问就一直存在, 到现在, 至少也有3年了吧, 幸好这次貌似有点明白了, 赶紧记下来; 但也有可能是错误的。

PG_locked 的解释一直找不到权威的, 就网上搜到的观点看, 这个位表明这个页面在处于 IO 当中,所以应当不要修改之类的, 英文叫 don't touch, 中文不知怎么翻译了。 就现在理解似乎这个说法是正确的。 需要整体解释下面代码片段:

 697                page_cache_get(page);
 698                spin_unlock(&pagecache_lock);
 699
 700                lock_page(page);
 701
 702                /* Is the page still hashed? Ok, good.. */
 703                if (page->mapping)
 704                        return page;

首先将页面引用计数加一, 这个是在 pagecache_lock 保护下做的, 由于 pagecache 本身会保持页面的一个引用, 因此, 在这里肯定可以放心大胆加引用, 而加引用的目的, 则可以将 page 握在手里, 防止它回归伙伴系统。 那么怎么会回归呢? 要回归, 肯定是 cache 的引用没有了, 那么就是发生在  spin_unlock(&pagecache_lock) 之后, 如果没有提前 page_cache_get(page) 的话。 但这个引用只能保护 page 不回归伙伴系统, 不能保护别的, 至少从 pagecache 中脱离是保护不了的, page->mapping 正是 page 位于 pagecache 的一个标志; 于是可以明白, 在 lock_page 期间, 这个页面是有可能从 pagecache 中脱离的。

lock_page 可能会睡眠, 他的实现逻辑为, 尝试设定 page 的 PG_locked 位, 如果发现已经设置了, 则睡眠等待这个标志位被清空, 然后唤醒, 再次尝试设置后, 如果发现又有人捷足先登, 则接着睡眠, 否则设置该位后返回。

PG_locked 位是 IO 的标志, 表明这个页面正处于 IO 过程中, 那么很可能 IO 处理的一部分就包括 page 的脱 pagecache, 降低引用计数, 而后回归伙伴系统(如果没有其他引用的话), 既然回归伙伴系统, 则表明这个页面的数据已经不需要了, 为何不需要了, 因为之前的 IO 已经将数据写到存储上了。 这样理解, 貌似整体脉络就清楚了。

page 涉及IO, 刚刚想了一下, 就目前我所读代码而言, 没有太多发起的情景, 似乎只有两个:

1. 通过 mmap 之类调用被映射到用户空间的页面, 一般是通过页交换逻辑, 逐渐的从各个进程的页表中脱离, 而后从内核的 active lru 队列进入到 inactive 队列, 经过一段时间缓冲后, 通过 page->mapping->a_op->writepage 函数将数据写到存储

2. sys_write 系统调用最终会通过 address_space_operations 的 prepare_write/commit_write 将页面写到存储。

在写的过程中, 肯定会 lock_page,  写完毕后, 则 unlock 之, 并尝试释放页面到伙伴系统; 哦, 不对, 应该是先尝试释放页面到伙伴系统, 而后 unlock 页面, 否则, 上面 703 行的判断就有问题了。

无论如何, 如果到达 704 行, 则页面在 700 行被锁定的情况下, 也就不担心从  pagecache 中脱离了, 可以安全返回, 这里的一个推论是, 所有脱离 pagecache 的操作, 定然是在页面被锁定的情况下进行的。

而一旦到达 705 行, 则表明在 page_lock 成功之前, 这个页面就已经成功在另一个 page_lock 的保护下脱离 pagecache 了, 那么这个页面貌似也不能咬了, 只好放弃锁定, 重试一遍, 很显然, 重试多半失败, 除非在重试之前, 又有谁将这个页面又从存储读到了内存, 并加入了 pagecache 中, 那么失败后, 则返回 NULL, 由调用者逻辑来做处理。

最后尝试找一下 IO 完毕后的释放 page 的代码, 看看推断是否正确, 结果是, 找到了一点线索, 但是貌似是驱动框架里面又有辗转, 驱动目前不熟, 只能到此为止, 不算成功。

这是 rw_swap_page_base 中的一段, 这个函数是页面交换到 swap 分区中的启动硬件 IO 的那个函数:

  77        if (!wait) {
  78                SetPageDecrAfter(page);
  79                atomic_inc(&nr_async_pages);
  80        }
  81
  82        /* block_size == PAGE_SIZE/zones_used */
  83        brw_page(rw, page, dev, zones, block_size);
  84
  85        /* Note! For consistency we do all of the logic,
  86         * decrementing the page count, and unlocking the page in the
  87         * swap lock map - in the IO completion handler.
  88         */
  89        if (!wait)
  90                return 1;

最后一段注释, 貌似在说, 这些工作又交给 IO completion 例程了, 这个东西应该就是 linux aio 完毕后的通知用户相关的玩意



原文地址:https://www.cnblogs.com/zylthinking/p/2862932.html