1653|6

6386

帖子

8

TA的资源

管理员

楼主
 

阅读打卡第五站: 内存管理之不错主题 ——《奔跑吧Linux内核(第2版)卷1》 [复制链接]

>>点此进入阅读打卡总站,查看所有打卡题目

 

阅读小伙伴们,第五站打卡 题目开启:@paope    、@费炜@lemonboard@chejia12@maskmoo@硬核王同学@小默叔叔@yin_wu_qing@meiyao

预计阅读完《奔跑吧Linux内核(第2版)卷1:基础架构》第五章可解答。

作者笨叔给了的本章打卡题目:

1.page数据结构中的_refcount和_mapcount有什么区别?
2.匿名页面和高速缓存页面有什么区别?
3.page数据结构中有一个锁,我们称为页锁,请问trylock_page()和lock_page()有什么区别?
4.请画出page数据结构中flags成员的布局示意图。
5.请列举page数据结构中_refcount和_mapcount计数的使用案例。

 

 

论坛有开设嵌入式交流群,欢迎感兴趣的小伙伴,微信添加“helloeeworld”为好友,对话:加入嵌入式交流群,入群交流。

扫码,对话:加入嵌入式交流群。

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

最新回复

本帖最后由 yin_wu_qing 于 2024-1-7 20:21 编辑 1. page数据结构中的_refcount和_mapcount有什么区别? _refcount和_mapcount是page数据结构中非常重要的两个引用计数,且都是atomic_t类型的变量。 A _refcount表示内核中引用该页面的次数。 当_refcount的值为0时,表示该页面为空闲页面或即将要被释放的页面。 当_refcount的值大于0时,表示该页面已经被分配且内核正在使用,暂时不会被释放。内核中提供加/减_refcount的接口函数,读者应该使用这些接口函数来使用_refcount引用计数。 get_page():_refcount加1;put_page():_refcount减1。若_refcount减1后等于0,那么会释放该页面。 get_page()函数调用page_ref_inc()来增加引用计数,最后使用atomic_inc()函数原子地增加引用计数。 put_page()首先使用put_gage_testzero()函数来使_refcount减1并且判断其是否为0。如果_refcount减1之后等于0,就会调用_put_page()来释放这个页面。 B _mapcount表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。每个用户进程都拥有各自独立的虚拟空间(256TB)和一份独立的页表,所以可能出现多个用户进程地址空间同时映射到一个物理页面的情况,RMAP系统就是利用这个特征来实现的。_mapcount主要用于RMAP系统中。 若_mapcount等于-1,表示没有PTE映射到页面。 若_mapcount等于0,表示只有父进程映射到页面。匿名页面刚分配时,_mapcount初始化为0。例如,当do_anonymous_page()产生的匿名页面通过page_add_new_anon_map()添加到rmap系统中时,会设置_mapcount为0,这表明匿名页面当前只有父进程的PTE映射到页面。 若_mapcount大于0,表示除了父进程外还有其他进程映射到这个页面。同样以创建子进程时共享父进程地址空间为例,设置父进程的PTE内容到子进程中并增加该页面的_mapcount。 2.匿名页面和高速缓存页面有什么区别? 匿名页面的产生:从内核的角度来看,在如下情况会产生匿名页面。 用户空间通过malloc()/mmap()接口函数来分配内存,在内核空间中发生缺页中断时,do_anonymous_page()会产生匿名页面。 发生写时复制。当缺页中断出现写保护错误时,新分配的页面是匿名页面,下面又分两种情况。 a 调用do_wp_page(). 分配只读的特殊映射的页面,如映射到零页面的页面。 分配非单身匿名页面(有多个映射的匿名页面,即page->_mapcount>0)。 分配只读的私有映射的内容缓存页面。 分配KSM页面。 b 调用do_cow_page()共享的匿名映射(Shared Anonymous Mapping,SHMM)页面。 上述这些情况在发生写时复制时会新分配匿名页面。 do_swap_page(),从交换分区读回数据时会分配匿名页面。 迁移页面。以do_anonymous_page()分配一个匿名页面为例,匿名页面刚分配时的状态如下。 page->_refcount = 1。 page->_mapcount = 0。 设置PG_swapbacked标志位。 加入LRU_ACTIVE_ANON链表中,并设置PG_lru标志位。 page->mapping指向VMA中的anon_vma数据结构。 匿名页面在缺页中断中分配完成之后,就建立了进程虚拟地址空间和物理页面的映射关系,用户进程访问虚拟地址即访问匿名页面的内容。 高速缓存页面 假设现在系统内存紧张,需要回收一些页面来释放内存,匿名页面刚分配时会加入活跃LRU链表(LRU_ACTIVE_ANON)的头部,在活跃LRU链表移动一段时间后,该匿名页面到达活跃LRU链表的尾部,shrink_active_list()函数把该页面加入不活跃LRU链表(LRU_INACTIVE_ANON)。Linux内核为页面迁移提供了一个系统调用migrate_pages,它可以迁移一个进程的所有页面到指定内存节点上。该系统调用最早是为了在UMA系统中提供一种迁移进程到任意内存节点的能力。现在内核除了为NUMA系统提供页面迁移能力外,其他的一些模块也可以利用页面迁移功能做一些事情。如内存规整和内存热插拔等。页面迁移的设计初衷是在UMA系统中提高内存访问性能,把一些页面从一个内存节点迁移到另外一个内存节点。它还有一个应用场景(内存规整)。这些迁移的页面都是LRU链表上的页面。但是,最近几年Linux内核引入了一些新的特性,如zsmalloc和virtio-balloon页面。以virtio-balloon页面为例,它也有页面迁移的需求,之前的做法是在virtio-balloon驱动中进行迁移操作和相应的逻辑。如果其他的驱动也想做类似的页面迁移,那么它们就不能复用与virtio-balloon驱动相关的代码,必须重新写一套代码,这样会造成很多代码的重复与冗余。为了解决这个问题,内存管理的页面迁移机制提供相应的接口来支持这些非LRU页面的迁移。因此,页面迁移机制支持两大类内存页面。 传统LRU页面,如匿名页面和文件映射页面。 非LRU页面,如zsmalloc或者virtio-balloon页面。 3. page数据结构中有一个锁,我们称为页锁,请问trylock_page()和lock_page()有什么区别? Page数据结构中的成员flags定义了一个标志PG_locked,内核通常利用PG_locked来设置一个页锁。lock_page()函数用于申请页锁,如果页锁被其他进程占用了,那么它会睡眠等待。从lock_page()函数的声明和实现代码来看,lock_page()函数首先会调用trylock_page()函数,然后调用__lock_page()函数。trylock_page()和lock_page()这两个函数看起来很相似,但有很大的区别。trylock_page()定义在include/linux/pagemap.h文件中,它使用test_and_set_bit_lock()尝试为page的flags设置PG_locked标志位,并且返回原来标志位的值。如果page的PG_locked位已经置位了,那么当前进程调用trylock_page()时返回false,说明有其他进程已经锁住了page。因此,若trylock_page()返回false,表示获取锁失败;若返回true,表示获取锁成功。 trylock_page()尝试给页面加锁。若trylock_page()返回false,表示别的进程已持有了这个页面的锁;否则,表示当前进程已经成功获取锁。 如果尝试获取页面不成功,当前不是强制迁移(force=0)或迁移模式为MIGRATE_ASYNC,则会直接忽略这个页面,因为这种情况下没有必要睡眠等待页面释放锁。 如果当前进程设置了PF_MEMALLOC标志位,表示当前进程可能处于直接内存压缩的内核路径上,通过睡眠等待页锁是不安全的,所以直接忽略该页面。例如,在文件预读中,预读的所有页面都会加锁并被添加到LRU链表中,等到预读完成后,这些页面会标记PG_uptodate并释放锁,这个过程中块设备层会把多个页面合并到一个BIO设备中。如果在分配第2个或者第3个页面时发生内存短缺,内核会运行到直接内存压缩的内核路径上,导致一个页面加锁之后又等待这个锁,产生死锁,因此直接内存压缩的内核路径会标记PF_MEMALLOC。PF_MEMALLOC标志位一般在直接内存压缩、直接内存回收以及kswapd中设置,这些场景下可能会有少量的内存分配行为,因此若设置PF_MEMALLOC标志位,表示允许它们使用系统预留的内存,即不用考虑zone水位问题,可以参见__perform_reclaim()、__alloc_pages_direct_compact()和kswapd()等函数。除了上述情况外,其余情况下只能调用lock_page()函数来等待页锁被释放。 4. 请画出page数据结构中flags成员的布局示意图。[attach]770961[/attach] num pageflags { PG_locked, /* Page is locked. Don't touch. */ //表示页面已经上锁;如果该比特位置位,说明已经被锁,内存管理其他模块不能访问这个页面,防止竞争 PG_error, //表示页面操作过程中发生错误时会设置该位; PG_referenced, //同PG_active一起,用于控制页面的活跃程度,在kswapd页面回收中使用; PG_uptodate, //表示页面的数据已经从块设备成功读取到内存页面; PG_dirty, //表示页面内容发生改变,这个页面为脏的,即页面内容被改写,还没同步到外部存储器 PG_lru, //表示页面加入了LRU链表中,内核使用LRU链表来管理活跃和不活跃页面; PG_active, PG_workingset, PG_waiters, /* Page has waiters, check its waitqueue. Must be bit #7 and in the same byte as "PG_locked" */ PG_slab, //页面用于slab分配器 PG_owner_priv_1, /* Owner use. If pagecache, fs may use*/ PG_arch_1, PG_reserved, PG_private, /* If pagecache, has fs-private data */ PG_private_2, /* If pagecache, has fs aux data */ PG_writeback, /* Page is under writeback */ //表示页面的内容正在向块设备进行会写 PG_compound, PG_swapcache, PG_mappedtodisk, /* Has blocks allocated on-disk */ PG_reclaim, /* To be reclaimed asap */ //表示这个页面马上要被回收 PG_swapbacked, /* Page is backed by RAM/swap */ //表示页面具有swap缓存功能,通过匿名页面才可以写回swap分区 PG_unevictable, /* Page is "unevictable" */ //表示这个页面不能回收 #ifdef CONFIG_MMU PG_mlocked, /* Page is vma mlocked */ //表示页面对应的vma处于mlocked状态; #endif __NR_PAGEFLAGS, }; 5.  请列举page数据结构中_refcount和_mapcount计数的使用案例。 _refcount通常在内核中用于跟踪页面的使用情况,常见的用法归纳总结如下: 初始状态下,空闲页面的_refcount是0 分配页面时,_refcount会变成1。页面分配接口函数alloc_pages()在成功分配页面后,_refcount应该为0,这里使用VM_BUG_ON_PAGE()来判断,然后设置这些页面的_refcount为1。 加入LRU链表时,页面会被kswapd内核线程使用,因此_refcount会加1。以malloc为用户程序分配内存为例,发生缺页中断后,do_anonymous_page()函数成功分配出来一个页面,在设置硬件PTE之前,调用lru_cache_add()函数把这个匿名页面添加到LRU链表中。在这个过程中,使用page_cache_get()宏来增加_refcount。当页面已经添加到LRU链表后,_refcount会减1,这样做的目的是防止页面在添加到LRU链表过程中被释放。 被映射到其他用户进程的PTE时,_refcount会加1。如在创建子进程时共享父进程的地址空间,设置父进程的PTE内容到子进程中并增加该页面的_refcount,详见do_fork()->copy_process()->copy_mm()->dup_mmap()->copy_pte_range()->copy_one_pte()函数。 在copy_one_pte()函数中,通过vm_normal_page()找到父进程的PTE对应的页面,然后增加这个页面的_refcount。 页面的private成员指向私有数据。 对于PG_swapable的页面,__add_to_swap_cache()函数会增加_refcount。 对于PG_private的页面,主要在块设备的buffer_head中使用,如buffer_migrate_page()函数中会增加_refcount。 内核对页面进行操作等关键路径上也会使_refcount加1,如内核的follow_page()函数和get_user_pages()函数。以follow_page()函数为例,调用者通常需要设置FOLL_GET标志位来使其增加_refcount。如KSM中获取可合并的页面函数get_mergeable_page(),另一个例子是DIRECT_IO,详见write_protect_page()函数。 _mapcount表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。每个用户进程都拥有各自独立的虚拟空间(256TB)和一份独立的页表,所以可能出现多个用户进程地址空间同时映射到一个物理页面的情况,RMAP系统就是利用这个特性来实现的。_mapcount主要用于RMAP系统中。 若_mapcount等于-1,表示没有PTE映射到页面。 若_mapcount等于0,表示只有父进程映射到页面。匿名页面刚分配时,_mapcount初始化为0。例如,当do_anonymous_page()产生的匿名页面通过page_add_new_anon_rmap()添加到rmap系统中时,会设置_mapcount为0,这表明匿名页面当前只有父进程的PTE映射到页面。 若_mapcount大于0,表示除了父进程外还有其他进程映射到这个页面。同样以创建子进程时共享父进程地址空间为例,设置父进程的PTE内容到子进程中并增加该页面的_mapcount,详见do_fork()->copy_process()->copy_mm()->dup_mmap()->copy_pte_range()->copy_one_pte()函数。 [attach]770963[/attach]   详情 回复 发表于 2024-1-7 20:18
点赞 关注
个人签名微信搜索公众号“EEWORLDBBS”快去添加关注吧!

回复
举报

19

帖子

0

TA的资源

一粒金砂(中级)

沙发
 
 
 

回复

110

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
 
 
 

回复

208

帖子

1

TA的资源

一粒金砂(高级)

4
 

1.page数据结构中的_refcount和_mapcount有什么区别?
_refcount和_mapcount是page数据结构中非常重要的两个引用计数器,其都是atomic_t类型的变量。_refcount是page数据结构的命根子,_mapcount是page数据结构的幸福指数。
_refcount:表示内核中引用该页面的次数,通常在内核中用于跟踪页面的使用情况。
_mapcount:表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。
2.匿名页面和高速缓存页面有什么区别?
匿名页面和高速缓存页面是操作系统中的两个概念。
匿名页面是指没有对应文件的页面,通常用于存储程序的堆栈和动态分配的内存。
高速缓存页面是指从文件系统中读取的页面,通常用于存储程序的代码和静态数据。
简而言之,匿名页面用于存储程序运行时动态分配的内存,而高速缓存页面用于存储程序的代码和静态数据。
3.page数据结构中有一个锁,我们称为页锁,请问trylock_page()和lock_page()有什么区别?
lock_page()函数用于申请页锁,如果页锁被其他进程占用了,那么它会睡眠等待。
lock_page()函数会先调用trylock_page()函数,trylock_page()函数无法获取锁时,当前进程会调用其他函数让其在等待队列中睡眠,等待这个锁。并且这个状态时不可中断的状态。
4.请画出page数据结构中flags成员的布局示意图。

5.请列举page数据结构中_refcount和_mapcount计数的使用案例。
_refcount 用于记录页面被引用的次数,常见的使用案例包括:
1. 在内存管理中,用于跟踪页面的引用计数,以便在页面不再被引用时释放页面。
2. 在内存回收算法中,用于确定页面是否可以被回收,即当 _refcount 为 0 时表示页面不再被引用。
_mapcount 用于记录页面被映射的次数,常见的使用案例包括:
1. 在内存管理中,用于跟踪页面的映射计数,以便在页面不再被映射时释放页面。
2. 在内存回收算法中,用于确定页面是否可以被回收,即当 _mapcount 为 0 时表示页面不再被映射。

 

 
 
 

回复

64

帖子

0

TA的资源

一粒金砂(中级)

5
 
本帖最后由 硬核王同学 于 2023-12-30 21:18 编辑

1.page数据结构中的_refcount和_mapcount有什么区别?

答:_refcount和_mapcount是page数据结构中非常重要的两个引用计数器,其都是atomic_t类型的变量。
_refcount:表示内核中引用该页面的次数。
_mapcount:表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。

2.匿名页面和高速缓存页面有什么区别?

答:两者在页面数据结构中mapping指向不同。对于匿名页面来说,mapping指向VMA的anon_vma数据结构;对于高速缓存页面mapping则指向交换分区的swapper_spaces。

3.page数据结构中有一个锁,我们称为页锁,请问trylock_page()和lock_page()有什么区别?

答:lock_page()函数用于申请页锁,如果页锁被其他进程占用了,那么它会睡眠等待。
lock_page()函数会先调用trylock_page()函数,trylock_page()函数无法获取锁时,当前进程会调用其他函数让其在等待队列中睡眠,等待这个锁。并且这个状态时不可中断的状态。
4.请画出page数据结构中flags成员的布局示意图。

答:


5.请列举page数据结构中_refcount和_mapcount计数的使用案例。

答:

_refcount 用于记录页面被引用的次数,常见的使用案例包括:

  • 1. 初始状态下,空闲页面的_refcount是0。
  • 2. 分配页面时,_refcount会变成1。
  • 3. 加入LRU链表时,页面会被kswapd内核线程使用,因此_refcount会加1。
  • 4. 被映射到其他用户进程的PTE时,_refcount会加1。
  • 5. 在copy_one_pte()函数中,通过vm_normal_page()找到父进程的PTE对应的页面,然后增加这个页面的_refcount。
  • 6. 页面的private成员指向私有数据。
  • 7. 内核对页面进行操作等关键路径上也会使_refcount加1,如内核的follow_page()函数和get_user_pages()函数。

_mapcount 表示这个页面被映射的次数,常见的使用案例包括:

  • 1. 若_mapcount等于-1,表示没有PTE映射到界面。
  • 2. 若_mapcount等于0,表示只有父进程映射到页面。
  • 3. 若_mapcount大于0,表示除了父进程外还有其他进程映射到这个页面。
 
 
 

回复

66

帖子

0

TA的资源

一粒金砂(高级)

6
 

打卡题目:

1.page数据结构中的_refcount和_mapcount有什么区别?

_refcount 是页面的引用计数。这个计数器表示有多少地方正在引用这个页面。当 _refcount 为零时,表示没有任何地方在引用这个页面,这时可以安全地释放它,回收内存。在内核中,这个计数器经常用于管理页面缓存、进程的用户空间等,确保页面在不再被引用时能够被正确释放。mapcount 表示页面在映射中的计数。这个计数器告诉我们有多少个线性地址范围映射到了这个页面。当 _mapcount 为零时,表示没有任何线性地址范围映射到这个页面。
2.匿名页面和高速缓存页面有什么区别?

匿名页面表示没有实际映射的页面,通常用于进程的堆栈、用户态分配的内存等。高速缓存页面表示已经映射了的页面。

3.page数据结构中有一个锁,我们称为页锁,请问trylock_page()和lock_page()有什么区别

trylock_page尝试获取页面的锁,如果锁已经被其他线程持有,则不会阻塞,而是返回失败。

lock_page获取页面的锁,如果锁已经被其他线程持有,则会阻塞直到锁可用。

4.请画出page数据结构中flags成员的布局示意图。

 
5.请列举page数据结构中_refcount和_mapcount计数的使用案例。

内核中的页面缓存被多个地方引用时,_refcount 会增加;在进程的页表中,每个映射到该页面的线性地址都会使 _mapcount 增加。

 
 
 

回复

221

帖子

0

TA的资源

一粒金砂(高级)

7
 
本帖最后由 yin_wu_qing 于 2024-1-7 20:21 编辑
  • 1. page数据结构中的_refcount_mapcount有什么区别?
_refcount和_mapcount是page数据结构中非常重要的两个引用计数,且都是atomic_t类型的变量。
A _refcount表示内核中引用该页面的次数。
  1. 当_refcount的值为0时,表示该页面为空闲页面或即将要被释放的页面。
  2. 当_refcount的值大于0时,表示该页面已经被分配且内核正在使用,暂时不会被释放。内核中提供加/减_refcount的接口函数,读者应该使用这些接口函数来使用_refcount引用计数。
get_page():_refcount加1;put_page():_refcount减1。若_refcount减1后等于0,那么会释放该页面。
get_page()函数调用page_ref_inc()来增加引用计数,最后使用atomic_inc()函数原子地增加引用计数。
put_page()首先使用put_gage_testzero()函数来使_refcount减1并且判断其是否为0。如果_refcount减1之后等于0,就会调用_put_page()来释放这个页面。
B _mapcount表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。每个用户进程都拥有各自独立的虚拟空间(256TB)和一份独立的页表,所以可能出现多个用户进程地址空间同时映射到一个物理页面的情况,RMAP系统就是利用这个特征来实现的。_mapcount主要用于RMAP系统中。
  1. 若_mapcount等于-1,表示没有PTE映射到页面。
  2. 若_mapcount等于0,表示只有父进程映射到页面。匿名页面刚分配时,_mapcount初始化为0。例如,当do_anonymous_page()产生的匿名页面通过page_add_new_anon_map()添加到rmap系统中时,会设置_mapcount为0,这表明匿名页面当前只有父进程的PTE映射到页面。
  • 若_mapcount大于0,表示除了父进程外还有其他进程映射到这个页面。同样以创建子进程时共享父进程地址空间为例,设置父进程的PTE内容到子进程中并增加该页面的_mapcount。
  • 2.匿名页面和高速缓存页面有什么区别?
匿名页面的产生:从内核的角度来看,在如下情况会产生匿名页面。
  1. 用户空间通过malloc()/mmap()接口函数来分配内存,在内核空间中发生缺页中断时,do_anonymous_page()会产生匿名页面。
  2. 发生写时复制。当缺页中断出现写保护错误时,新分配的页面是匿名页面,下面又分两种情况。
    a 调用do_wp_page().
    分配只读的特殊映射的页面,如映射到零页面的页面。
    分配非单身匿名页面(有多个映射的匿名页面,即page->_mapcount>0)。
    分配只读的私有映射的内容缓存页面。
    分配KSM页面。
    b 调用do_cow_page()共享的匿名映射(Shared Anonymous Mapping,SHMM)页面。
    上述这些情况在发生写时复制时会新分配匿名页面。
  3. do_swap_page(),从交换分区读回数据时会分配匿名页面。
  4. 迁移页面。以do_anonymous_page()分配一个匿名页面为例,匿名页面刚分配时的状态如下。
page->_refcount = 1。
page->_mapcount = 0。
设置PG_swapbacked标志位。
加入LRU_ACTIVE_ANON链表中,并设置PG_lru标志位。
page->mapping指向VMA中的anon_vma数据结构。
匿名页面在缺页中断中分配完成之后,就建立了进程虚拟地址空间和物理页面的映射关系,用户进程访问虚拟地址即访问匿名页面的内容。
高速缓存页面
假设现在系统内存紧张,需要回收一些页面来释放内存,匿名页面刚分配时会加入活跃LRU链表(LRU_ACTIVE_ANON)的头部,在活跃LRU链表移动一段时间后,该匿名页面到达活跃LRU链表的尾部,shrink_active_list()函数把该页面加入不活跃LRU链表(LRU_INACTIVE_ANON)。Linux内核为页面迁移提供了一个系统调用migrate_pages,它可以迁移一个进程的所有页面到指定内存节点上。该系统调用最早是为了在UMA系统中提供一种迁移进程到任意内存节点的能力。现在内核除了为NUMA系统提供页面迁移能力外,其他的一些模块也可以利用页面迁移功能做一些事情。如内存规整和内存热插拔等。页面迁移的设计初衷是在UMA系统中提高内存访问性能,把一些页面从一个内存节点迁移到另外一个内存节点。它还有一个应用场景(内存规整)。这些迁移的页面都是LRU链表上的页面。但是,最近几年Linux内核引入了一些新的特性,如zsmalloc和virtio-balloon页面。以virtio-balloon页面为例,它也有页面迁移的需求,之前的做法是在virtio-balloon驱动中进行迁移操作和相应的逻辑。如果其他的驱动也想做类似的页面迁移,那么它们就不能复用与virtio-balloon驱动相关的代码,必须重新写一套代码,这样会造成很多代码的重复与冗余。为了解决这个问题,内存管理的页面迁移机制提供相应的接口来支持这些非LRU页面的迁移。因此,页面迁移机制支持两大类内存页面。
传统LRU页面,如匿名页面和文件映射页面。
非LRU页面,如zsmalloc或者virtio-balloon页面。
  • 3. page数据结构中有一个锁,我们称为页锁,请问trylock_page()lock_page()有什么区别?
Page数据结构中的成员flags定义了一个标志PG_locked,内核通常利用PG_locked来设置一个页锁。lock_page()函数用于申请页锁,如果页锁被其他进程占用了,那么它会睡眠等待。从lock_page()函数的声明和实现代码来看,lock_page()函数首先会调用trylock_page()函数,然后调用__lock_page()函数。trylock_page()和lock_page()这两个函数看起来很相似,但有很大的区别。trylock_page()定义在include/linux/pagemap.h文件中,它使用test_and_set_bit_lock()尝试为page的flags设置PG_locked标志位,并且返回原来标志位的值。如果page的PG_locked位已经置位了,那么当前进程调用trylock_page()时返回false,说明有其他进程已经锁住了page。因此,若trylock_page()返回false,表示获取锁失败;若返回true,表示获取锁成功。
trylock_page()尝试给页面加锁。若trylock_page()返回false,表示别的进程已持有了这个页面的锁;否则,表示当前进程已经成功获取锁。
  1. 如果尝试获取页面不成功,当前不是强制迁移(force=0)或迁移模式为MIGRATE_ASYNC,则会直接忽略这个页面,因为这种情况下没有必要睡眠等待页面释放锁。
  2. 如果当前进程设置了PF_MEMALLOC标志位,表示当前进程可能处于直接内存压缩的内核路径上,通过睡眠等待页锁是不安全的,所以直接忽略该页面。例如,在文件预读中,预读的所有页面都会加锁并被添加到LRU链表中,等到预读完成后,这些页面会标记PG_uptodate并释放锁,这个过程中块设备层会把多个页面合并到一个BIO设备中。如果在分配第2个或者第3个页面时发生内存短缺,内核会运行到直接内存压缩的内核路径上,导致一个页面加锁之后又等待这个锁,产生死锁,因此直接内存压缩的内核路径会标记PF_MEMALLOC。PF_MEMALLOC标志位一般在直接内存压缩、直接内存回收以及kswapd中设置,这些场景下可能会有少量的内存分配行为,因此若设置PF_MEMALLOC标志位,表示允许它们使用系统预留的内存,即不用考虑zone水位问题,可以参见__perform_reclaim()、__alloc_pages_direct_compact()和kswapd()等函数。除了上述情况外,其余情况下只能调用lock_page()函数来等待页锁被释放。
  • 4. 请画出page数据结构中flags成员的布局示意图。
  • num pageflags {
  • PG_locked, /* Page is locked. Don't touch. */ //表示页面已经上锁;如果该比特位置位,说明已经被锁,内存管理其他模块不能访问这个页面,防止竞争
    PG_error, //表示页面操作过程中发生错误时会设置该位;
    PG_referenced, //同PG_active一起,用于控制页面的活跃程度,在kswapd页面回收中使用;
    PG_uptodate, //表示页面的数据已经从块设备成功读取到内存页面;
    PG_dirty, //表示页面内容发生改变,这个页面为脏的,即页面内容被改写,还没同步到外部存储器
    PG_lru, //表示页面加入了LRU链表中,内核使用LRU链表来管理活跃和不活跃页面;
    PG_active,
    PG_workingset,
    PG_waiters, /* Page has waiters, check its waitqueue. Must be bit #7 and in the same byte as "PG_locked" */
    PG_slab, //页面用于slab分配器
    PG_owner_priv_1, /* Owner use. If pagecache, fs may use*/
    PG_arch_1,
    PG_reserved,
    PG_private, /* If pagecache, has fs-private data */
    PG_private_2, /* If pagecache, has fs aux data */
    PG_writeback, /* Page is under writeback */ //表示页面的内容正在向块设备进行会写
    PG_compound,
    PG_swapcache,
    PG_mappedtodisk, /* Has blocks allocated on-disk */
    PG_reclaim, /* To be reclaimed asap */ //表示这个页面马上要被回收
    PG_swapbacked, /* Page is backed by RAM/swap */ //表示页面具有swap缓存功能,通过匿名页面才可以写回swap分区
    PG_unevictable, /* Page is "unevictable" */ //表示这个页面不能回收
    #ifdef CONFIG_MMU
    PG_mlocked, /* Page is vma mlocked */ //表示页面对应的vma处于mlocked状态;
    #endif
    __NR_PAGEFLAGS,
    };
  • 5.  请列举page数据结构中_refcount_mapcount计数的使用案例。
_refcount通常在内核中用于跟踪页面的使用情况,常见的用法归纳总结如下:
  1. 初始状态下,空闲页面的_refcount是0
  2. 分配页面时,_refcount会变成1。页面分配接口函数alloc_pages()在成功分配页面后,_refcount应该为0,这里使用VM_BUG_ON_PAGE()来判断,然后设置这些页面的_refcount为1。
  3. 加入LRU链表时,页面会被kswapd内核线程使用,因此_refcount会加1。以malloc为用户程序分配内存为例,发生缺页中断后,do_anonymous_page()函数成功分配出来一个页面,在设置硬件PTE之前,调用lru_cache_add()函数把这个匿名页面添加到LRU链表中。在这个过程中,使用page_cache_get()宏来增加_refcount。当页面已经添加到LRU链表后,_refcount会减1,这样做的目的是防止页面在添加到LRU链表过程中被释放。
  4. 被映射到其他用户进程的PTE时,_refcount会加1。如在创建子进程时共享父进程的地址空间,设置父进程的PTE内容到子进程中并增加该页面的_refcount,详见do_fork()->copy_process()->copy_mm()->dup_mmap()->copy_pte_range()->copy_one_pte()函数。
  5. 在copy_one_pte()函数中,通过vm_normal_page()找到父进程的PTE对应的页面,然后增加这个页面的_refcount。
  6. 页面的private成员指向私有数据。
    对于PG_swapable的页面,__add_to_swap_cache()函数会增加_refcount。
    对于PG_private的页面,主要在块设备的buffer_head中使用,如buffer_migrate_page()函数中会增加_refcount。
  7. 内核对页面进行操作等关键路径上也会使_refcount加1,如内核的follow_page()函数和get_user_pages()函数。以follow_page()函数为例,调用者通常需要设置FOLL_GET标志位来使其增加_refcount。如KSM中获取可合并的页面函数get_mergeable_page(),另一个例子是DIRECT_IO,详见write_protect_page()函数。
_mapcount表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。每个用户进程都拥有各自独立的虚拟空间(256TB)和一份独立的页表,所以可能出现多个用户进程地址空间同时映射到一个物理页面的情况,RMAP系统就是利用这个特性来实现的。_mapcount主要用于RMAP系统中。
若_mapcount等于-1,表示没有PTE映射到页面。
若_mapcount等于0,表示只有父进程映射到页面。匿名页面刚分配时,_mapcount初始化为0。例如,当do_anonymous_page()产生的匿名页面通过page_add_new_anon_rmap()添加到rmap系统中时,会设置_mapcount为0,这表明匿名页面当前只有父进程的PTE映射到页面。
若_mapcount大于0,表示除了父进程外还有其他进程映射到这个页面。同样以创建子进程时共享父进程地址空间为例,设置父进程的PTE内容到子进程中并增加该页面的_mapcount,详见do_fork()->copy_process()->copy_mm()->dup_mmap()->copy_pte_range()->copy_one_pte()函数。
阅读打卡第五站.docx (44.71 KB, 下载次数: 0)
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表