一起游 手游攻略 手游评测 宋保华:浅谈Linux页面迁移(Page Migration)完整版

宋保华:浅谈Linux页面迁移(Page Migration)完整版

时间:2024-09-09 16:40:19 来源:互联网 浏览:0

我想有两种场景你会关注这个Page迁移问题:一是在Linux中编写实时程序,特别是在Linux打了RT补丁之后。您希望您的申请有一定的延迟。希望您的Page在运行时变换位置所造成的延迟;另一种场景是在用户空间做DMA的场景,特别是SVA(SharedVirtual Addressing),设备和CPU共享页表,设备共享进程的虚拟地址空间,在这种场景下,如果你的DMA页正在运行周围,必然会导致设备的DMA暂停,造成设备传输性能的严重抖动。在这种情况下,设备的IOMMU和CPU的MMU将共享页表:

1. CoW导致的页面迁移

1.1 fork

典型的CoW(写时复制)与fork() 相关。当父子兄弟进程共享部分页面,并且这些页面本身应该具有独占属性时,此类页面将被标记为只读。当进程执行写操作时,会发生页面错误,然后内核为其申请新页面。例如下面的代码中,写入10到20的进程在写入过程中会得到一个新的内存页面,数据原来的虚拟地址会指向新的物理地址,从而引起页面迁移。

类似的场景涉及由页面私有映射引起的CoW:

运行该程序并通过smem 进行观察。可以清楚地看到,父进程执行一次新的写入后(程序打印“cow is did in Parent process”后),父子进程不再共享相关页面,其USS/PSS显着增加。大的:

fork衍生出来的copy-on-write场景比较基础,这里不再赘述。

1.2 KSM

其他CoW 场景包括KSM(内核同一页面合并)。 KSM会扫描多个进程的内存。如果它发现某个页面的内容完全相同,就会将其合并为一个页面,并将其标记为写保护。然后在这个页面上执行CoW,谁写它就得到一个新的副本。例如,当你使用qemu启动虚拟机时,使用mem-merge=on可以提示多个VM共享很多页面,有利于“超卖”。

sudo /x86_64-softmmu/qemu-system-x86_64 \-enable-kvm -m 1G \-machine mem-merge=on 但是,这本身也会导致虚拟机存在一些安全漏洞,并且可能受到旁路攻击。

例如将以下代码编译成a.out并启动两个a.out进程

./a.out./a.out代码:

我们看到这两个a.out的内存消耗如下:

但是,如果我们把中间的if 0改为if 1,这意味着mmap()的1MB内存可能需要合并,那么内存消耗情况就会发生明显的变化:

内存消耗大大减少。

我们可以看一下pageshare的情况:

合并发生在进程内和进程之间。

当然,如果有人在页面合并后再次写入合并后的页面,就会造成copy-on-write,比如下面代码中的那句p[0]=100。

透明大页面听起来很棒,因为它们不需要您在应用程序中显式指定MAP_HUGETLB,但实际使用场景可能并不那么棒。

使用透明大页面的最根本方法是将启用和碎片整理设置为始终:

始终回显/sys/kernel/mm/transparent_hugepage/enabledecho 始终/sys/kernel/mm/transparent_hugepage/defragenabled。写作总是意味着尽可能在所有区域使用透明大页面。 Defrag写入总是意味着内核会在积极申请内存时,进行内存回收(RECLAIM)和正则化(COMPACTION)以获得THP(Transparent Huge Page)。

我们稍微修改一下前面的示例代码,mmap16MB内存,并删除MAP_HUGETLB:

运行该程序并获取其pmap 状态:

我们发现从00007f46b0744000开始,有一个16MB的anon内存区域,这显然对应着我们代码中的mmap(1610241024)区域。

我们进一步敲定/proc/15371/smaps,得到该区域的内存分布情况:

显然,该区域是THPeligible,并获得一个透明的巨型页面。内核文档filesystems/proc.rst 对THPeligible 的描述如下:

'THPeligible' 指示映射是否有资格分配THP 页- 如果为真则为1,否则为0。它仅显示当前状态。

透明大页的生成显然会涉及到前面的内存COMPACTION过程。在实际用户场景中,透明大页可能会因为内存RECLAIM和COMPACTION而降低性能。例如,某些VMA区域寿命较短,申请后很快被释放,或者某些使用大内存的进程寿命较短。执行正则化需要很长时间,运行时释放这部分内存显然不值得。和《权力的游戏》里的夜王类似,我花了那么多季整理记忆来准备夜王的透明巨页,结果夜王一上来就被瞬间秒杀。你觉得我花这么多时间追剧是冤枉的吗?

因此,透明大页在实际项目中引入了一个半透明因素,即内核只能将透明大页分配到用户通过madvise() 提示需要大页的区间。提示时使用的参数为MADV_HUGEPAGE:

因此,默认情况下,很多系统都会将enabled和defrag都设置为madvise:

echo madvise /sys/kernel/mm/transparent_hugepage/enabledecho madvise /sys/kernel/mm/transparent_hugepage/defrag 或者干脆关闭透明大页功能:

echo never /sys/kernel/mm/transparent_hugepage/enabledecho never /sys/kernel/mm/transparent_hugepage/defrag 如果我们只对madvise 区域使用透明大页,用户的代码可以这样写:

既然我已经写了这样的代码,为什么还要透明呢?因此,为了确定性,我宁愿追求保留的、非交换的大页面。

3. NUMA Balancing引起的页面迁移

在典型的NUMA 系统中,有多个NODE。很可能每个NODE都有CPU和内存,并且NODE通过某种总线互连。下面的NUMA 系统有4 个节点。每个NODE有24个CPU和1个内存。节点通过红线互连:

在这样的系统中,CPU访问本地NODE节点的内存通常会比较快,而跨NODE访问内存则要慢很多(红色总线慢)。因此,Linux的NUMA自动平衡机制会尝试将内存迁移到正在访问它的CPU节点所在的NODE上。下图中,绿色内存经常被CPU24访问,但它位于NODE0的内存中:

那么Linux内核可能会将绿色内存迁移到CPU24所在的本地内存:

这样CPU24访问的时候就会快很多。

显然NUMA_BALANCE也依赖于MIGRATION机制:

我们来写一个多线程程序。该程序中有28 个线程(一个主线程、26 个执行无限循环的虚拟线程和一个写入内存的线程):

开这么多线程的目的只是为了尽可能不让write_thread_start对应的线程被分配到主线程所在的NUMA节点。

该程序的主线程最初写入所请求的64MB 内存。 30秒后,write_done=1用于指示write_thread_start()线程可以开始写入。 write_thread_start() 也会写入64MB。如果主线程和write_thread_start() 线程不在一个NODE 节点中,则可能会发生内存迁移。

这是我们在前2秒获得的进程的numastat。可以看到,几乎所有的64MB内存都在NODE3上:

但30秒后,当我们再次查看其NUMA状态时,发生了巨大的变化:

64MB 内存位于NODE1。由此我们可以推断,write_thread_start()线程应该运行在NODE1上,这导致了这次迁移的发生。

当然,我们也可以通过numactl--cpunodebind=2之类的命令来避免这个问题,比如:

# numactl --cpunodebind=2 ./a.outNUMA Balancing NUMA Balancing 的原理是定期取消映射进程的部分内存(例如每次256MB),并将扫描到的部分的PTE 设置为“no”页表访问权限”,以便以后访问时强制发生页错误,然后检测页错误是发生在本地NODE还是远程NODE,以了解CPU和内存是否距离较远。这表明即使没有发生实际的迁移、NUMA平衡会导致进程的内存访问出现Page failure。

4. Page migration究竟是怎么做的?

内存正则化和NUMA平衡引起的页面迁移的过程,简单来说就是将一个页面从A位置移动到B位置。俗话说“官一开口,差事断腿” ”。这个过程真的一点都不简单。在实际工程场景中,由于共享内存等原因,一个页面可能会被多个进程共享,并映射到多个进程的VMA上。因此,这个迁移过程必定伴随着多个进程的虚实映射的变化(迁移前后虚拟地址保持不变)以及相关address_space的基数树从指向A变为指向B。另外,新页面对应的一些标志应该与旧页面相同。

如果时间停止,例如,一旦我们开始将页面A 迁移到页面B 的过程,进程1、2、3 和4 将全部静止不动,内核的其余部分也将静止不动。这个过程相对比较简单。简单的。真实情况是,当我们将A迁移到B时,进程1、2、3、4仍然可能访问正在迁移的页面对应的虚拟地址。当迁移进行时,“树欲静而风不止”,用户进程和内核的其他子系统不会因为你“想安静”而给你“安静”。

迁移步骤非常复杂。我们专注于要点,放弃细节。我们主要以干净页的迁移为分析目标,暂且不考虑脏页的写回问题。在整个迁移过程中,除了进程1-4可以访问该页面的内容外,内核内存管理子系统中的其他代码也可能访问该页面对应的元数据。应避免内核内存管理的其他子系统的干扰。比如我正在迁移pageA,但是你的内存管理子系统还在LRU中扫描A,比如将pageA从inactiveLRU移到activeLRU。新皇帝明天就要进京了,老皇帝今晚还在修宫。世上还有如此无私的皇帝吗?因此,在迁移过程中,我们首先要使用__isolate_lru_page()将页面与LRU隔离,以免明天皇帝失效。因为,当你修改旧页面的元数据时,旧页面正在消亡。这不是太乱了吗?还有一种情况。明天甲帝将皇位让给乙帝,你今晚却杀了甲帝。例如pageA被回收并释放。明天天皇交接仪式如何进行?

__isolate_lru_page()核心代码如下,防止Page A被释放,也避免了LRU扫描动作:

if (likely(get_page_unless_zero(page))) { /* * 注意不要清除PageLRU,直到我们* 确定页面没有在其他地方被释放- * 页面释放代码依赖于它。 */ClearPageLRU(页); ret=0;它增加了page的_refcount,使得pageA在迁移到B的过程中不会被内存回收子系统释放。此外,ClearPageLRU()会清除该page的PG_lru标志,使PageLRU(page)为假,从而防止许多LRU 代码路径类似于以下内容:

宋保华:浅谈Linux页面迁移(Page Migration)完整版

static void __activate_page(struct page *page, struct lruvec *lruvec, void *arg){ if (PageLRU(page) !PageActive(page) !PageUnevictable(page)) { int lru=page_lru_base_type(page); } int nr_pages=hpage_nr_pages( 页); del_page_from_lru_list(页面, lruvec, lru); SetPageActive(页面); lru +=LRU_ACTIVE; add_page_to_lru_list(页面,lruvec,lru); … }} 隔离的准备工作完成了,我们开始迁移,然后我们就到了令人惊奇的部分——痛苦的内核函数:

int migrate_pages(struct list_head *from, new_page_t get_new_page, free_page_t put_new_page, unsigned long private, enum migrate_mode mode, int Reason);如果你看一下内核

https://www.kernel.org/doc/html/latest/vm/page_migration.html

文档,相信你看到这个地方一定会一头雾水,总共有20个步骤:

步骤太多,梳理起来比较困难,所以我们重点抓核心矛盾:迁移必须无缝,流程因为你的迁移而无法正常运行。在迁移过程中,进程仍然可能访问正在迁移的页面!当页面A迁移到页面B时,原进程1-4访问对应的虚拟地址,这种访问不存在非法行为。因此,我们必须确保它仍然可以访问。当然,这种访问可能会被延迟。大致可以分为3个阶段:

迁移初期,进程的页表项仍然指向页面A,映射到页面A的用户进程仍然可以毫无阻碍地访问A;

在迁移的中期,他们无法再访问A,因为A已取消映射,但此时实际上无法访问B,因为B尚未映射。一时间,皇位空缺。但是进程1-4访问这个虚拟地址并没有什么非法的!我们甚至不需要进程1-4 就知道我们正在迁移。因此,在这段挂起期间,当进程1-4访问虚拟地址时,我们应该能够感知到这次访问的发生,并让它们等待一段时间,以便新皇帝登基后可以继续访问;

迁移的最后阶段,B页被映射,新皇帝登基完成,前任皇帝被废弃。我们应该在“中期阶段”唤醒进程1-4来访问虚拟地址,并允许进程1-4访问它。页B。

第二阶段,根据迁移的页面来查找进程,并查找VMA来操作它们的PTE。使用反向映射技术RMAP。 PTE修改后,如果用户进程继续访问相关页面对应的虚拟地址,就会出现页面错误。发生页面错误后,进程会阻塞并等待发生页面错误的页面的锁定。这从缺页处理函数__migration_entry_wait()核心函数的注释和代码流程可以看出:

put_and_wait_on_page_locked()会将进程放入等待队列,等待该页的PG_locked标记被清除,即等待阶段3完成,从而实现无缝切换。

5. 如何规避页迁移

5.1 mlock可以吗?

mlock()的主要功能是防止交换。我们可以使用mlock() 来锁定匿名页面或具有文件背景的页面。如果匿名页被mlocked,则对应的内存不会被写入交换分区;如果文件后台页面被mlocked,相关的pagecache将不会被回收。有文件背景的页面的mlock有点难以理解,所以我们用下面的代码来演示有文件背景的mmap区域被mlock了:

由于我们mlocked,这4096字节必须驻留在内存中!但是,mlock()不能保证这4096字节对应的物理页不会被迁移。它的主要作用是防止页面被交换。从mlock() 手册页中可以清楚地看出这一点:mlock()、mlock2() 和mlockall() 将调用进程的部分或全部虚拟地址空间锁定到RAM 中,从而防止该内存被分页到交换区域。

为了实际证明页mlocked()仍然可以迁移,我们对前面演示NUMA Balance页迁移的程序稍作改动,对malloc :的64MB内存进行了mlock

再次运行这个程序,效果如下:

我们可以清楚地看到,随着write_thread_start()的执行,Node0上的64MB内存被迁移到NODE1上。

在Linux中,当执行mlock()操作时,相应的VMA将被设置VM_LOCKED标志。当然,用户mlock()的区域大小不一定完全等于mlock()对应区域的VMA大小,所以在mlock()的过程中,VMA会被分割或者合并(原因是同一VMA 中的标志应该相同)。我们都基于眼见为实的原则,用代码说话。让我们在上一个文件的后台页面上对mlock() 之前和之后的进程进行VMAdump。我们稍微改变一下代码,mmap 1M 但只有mlock 4K:

我们转储该进程在mlock 前后的VMA。在mlock之前,我们看到一个1024K的VMA区域,背景为/home/baohua/file:

mlock()之后我们再次观察,我们看到这个1024k被分割成了1020k+4k:

由于最初的4K 有单独的VMA 功能,因此1024 被分为两个VMA,4+1020。

由mlock()锁定的区域中的页面也将被标记为PG_mlocked(注意不是PG_locked)。相关页面将被放入unevictable_list链表中,页面将成为不可回收页面并从LRU中剥离。与不可清除相反的页面是可回收的。此类页面将进入inactive或activeLRU。内核的交换机制会扫描相关页面和LRU来决定回收最不活跃的页面。

根据内核文档

https://www.kernel.org/doc/Documentation/vm/unevictable-lru.txt,

Linux 支持mlocked 页面和其他不可删除页面的迁移。这涉及到简单地将PG_mlocked 和PG_unevictable 状态从旧页面移动到新页面。

因此,mlock()页面可能仍然会被迁移,但迁移后,原来的PG_mlocked标记会保留在迁移的目标页面上。新页面仍然是LRU 剥离,并且仍然不会被交换机制扫描。

至于是否应该进行这种不可回收页的内存合并动作,可以通过/proc/sys/vm/compact_unevictable_allowed参数来控制。如果我们启用它,内存正则化代码还将扫描unevictableLRU 并对不可回收的页面执行页面迁移。

透明大页是一个神奇的存在。假设我们的透明大页的大小是2MB。如果我们mlock()其中的1MB,那么没有被mlock()的1MB就可以被回收。当内核进行内存回收时,2MB可以再次被分割成4KB的页面,从而保证没有被mlocked()的部分可以被回收。

5.2 GUP可以吗?

我们发现mlock()没有阻止迁移的功能。那么,如果我们想使用用户空间缓冲区进行DMA,常规的方式是什么呢? Linux内核可以使用GUP(get_user_pages的衍生物)来固定页面,以防止相关页面被交换代码迁移或释放。这实际上为DMA 用户空间缓冲区提供了某种形式的安全性。

我们之前说过,mlock()可以避免页面交换,但无法避免页面迁移和旧页面释放。因此,如果我们将用户空间的一块区域即mlock()交给外设进行DMA,例如DMA的方向是DMA_FROM_DEVICE,因为设备感知页面A(app将用户空间缓冲区传递给外设) driver通过参数传递,传递时buffer仍然对应page A),但实际上在DMA发生之前,内存可能已经迁移到B了,Page A甚至被释放给buddy或者被其他程序申请了。此时设备执行DMA,仍然对A进行写入,极有可能会导致莫名其妙的崩溃。

因此,对于用户空间缓冲区和直接DMA场景,我们必须保证:

当我们将缓冲区传递给app中的驱动程序时,假设此时缓冲区对应的是页面A,那么当我们做DMA时以及DMA进行时,页面A必须始终存在。此pageA 不能被释放或重新分配给第三方。具体是如何完成的?我们可以看一下V4L2驱动程序。如果用户空间传入DMA指针,那么V4L2驱动的内核态是如何处理的?相关代码位于drivers/media/v4l2-core/videobuf-dma-sg.c:

videobuf_dma_init_user_locked()根据用户空间缓冲区的起始地址和大小计算页面数量,然后调用pin_user_pages()将这些页面固定在内核中。

pin_user_pages() 函数位于mm/gup.c 中,是GUP 函数系列的一部分。这个函数的作用,顾名思义,就是固定用户的页面。 Pin 和mlock() 具有完全不同的概率。 mlock()为VMA添加VMA_LOCKED属性,为VMA映射的页面添加PG_mlocked属性。它要求页面不能被回收或交换,但从pageA到pageB的迁移仍然可能发生。pageA仍然可能被释放,但是这个释放没有关系,因为我们已经有了B。所以本质上,mlock(p, size)是对虚拟地址p~p+size-1常驻内存的保证,或者说是一个通过VM_LOCKED 属性保证常驻内存。

VMA区域有常驻内存的page的一种保障,至于常驻的是A还是B,其实都不违背mlock()的语义。 mlock强调长期有人值班,不能掉链子,而不在乎你换不换班。Pin是完全不同的语义,pin强调的是贞节牌坊式的坚守。pin是把buffer对应的page本身让它无法释放,因为pin的过程,会导致这个page的引用计数增加GUP_PIN_COUNTING_BIAS(值为1024,我理解是一种代码的hack,以区分常规的get_user_pages和pin_user_pages,常规的get只是+1,而pin的主要目的是为了用user space的buffer做DMA操作): Pin page并没有改变page所在VMA区域的语义,这是和mlock()之间很大的区别,mlock()会导致VMA区域得到VM_LOCKED,但是我们并没有一个VM_PINNED这样的东西。从用户场景上来看,内核驱动Pin住了USERPTR对应的memory后,进一步,V4L2的代码videobuf_dma_map()会把这个buffer转换为sg进行map_sg操作,之后就可以DMA了。所以pin最主要的作用是保证DMA动作不至于访问内存的时候踏空。 如果我们不再需要这些userbuffer来进行DMA,相关的page应该被unpin: Unpin显然会导致一次引用计数减去GUP_PIN_COUNTING_BIAS的动作,如果ref减去GUP_PIN_COUNTING_BIAS的结果成为0,证明我们是最后一个用户,应该调用__put_page()促使page被正确释放:

宋保华:浅谈Linux页面迁移(Page Migration)完整版

这里还有一个特别值得关注的地方,如果我们进行的是DMA_FROM_DEVICE,也就是设备到内存的DMA,则相关的page可能被设备而不是CPU写dirty了,我们把unpin_user_pages_dirty_lock()的最后一个参数设置为了TRUE,这样unpin_user_pages_dirty_lock()会把相关Page的dirty标记置上,否则Linux内核甚至都不知道设备写过这个page。 pin_user_pages()在内核态增加了page的refcount,从而让内存管理的其他子系统不会释放这个page(尽管它没有像mlock那样把page从LRU上面拿下来,但是swap的代码也不可能释放它),也成功地阻止了page migration在这一个page上面的发生,甚至我们想去hot remove被pin的page所在的内存,都不可能成功了。 对于我们关注的页面迁移问题,pagemigration的代码会检查page的refcount,refcount不合适的page(pin本身导致了refcount不符合expected_count),不会被迁移: 所以,被pin的userspace page总体是DMA安全的。这里还有2个比较tricky的问题: 就是用户的page,被内核pin住了,用户在unpin之前进行unmap呢?首先我们不欢迎这样的动作,对于pin的主要场景如RDMA、VFIO等,我们强调pin住的page是LONGTERM的,一般用户空间弄好buffer,应该一直复用这个buffer。详见内核文档:core-api/pin_user_pages.rst 也可以看include/linux/mm.h的一段注释: 实际上,用户空间应该控制这个buffer是indefinite的。如果万一pin住的页面用户层面真地在unpin之前就unmap了呢?pin的refcount其实也阻止了这个page被释放,只是这个page所在对应的虚拟地址由于被unmap了,所以不再对CPU可见了。 当然,还有一个madvise(p, size, MADV_DONTNEED),其行为更加诡异,曾经臭名昭著的DirtyCoW漏洞(CVE-2016-5195),就与它息息相关。它并不是unmap,但是暗示内核它对某段地址访问暂时完成了使命很长一段时间不想访问了,并且去掉了页表的映射。这样后期再次访问这个地址p~ p+size-1的时候,如果p底层对应的是一个共享映射,可以获得老page的值;如果底层对应的是一个MAP_PRIVATE的映射,则按需获得一个0填充的页面: MADV_DONTNEED显然也是与pin的语义是违背的,这属于不“under userspace control”的情况了。如果我们真地对私有映射的pin区域执行了MADV_DONTNEED呢?显然pin也阻止了相关page被释放,但是当CPU重新访问page对应的虚拟地址的时候,MADV_DONTNEED却要求“the caller will see zero-fill-on-demand pages upon subsequent page references”,所以你不能指望先用MADV_DONTNEED暗示了自己不要这个memory了,然后再反过来期待DMA后CPU还能获取到自己曾经抛弃的内存,因为“是你当初说分手,分手就分手,现在又要用真爱把我哄回来,爱情不是你想卖,想买就能卖,让我挣开,让我明白,放手你的爱”。 下面的代码映射100MB,但是只对其中的前2页执行MADV_DONTNEED: 程序运行打印的结果如下: 前2页变成了0,第3页还是100。 如果pin住的区域是一个私有映射的区域(mmap的时候,使用了MAP_PRIVATE),然后在此区域执行了CoW会怎样?在下面的代码中,我们有个假想的设备驱动/dev/kernel-driver-with-pin-support,它支持一个IOCTL来让用户pin userspace的page。此例中,pin的这部分内存是私有映射的,而父进程又fork了子进程,之后父进程尝试写自己pin过的区域,这时会引发写时拷贝(CoW),从而让p对应的底层page发生变化: 这种编程方式,实际上也违背了用户空间应该控制这个buffer是indefinite的原则,只是更加隐晦,所以要用userspace buffer做DMA的话,也应该尽可能避免这种编程方式,一般要对用户态buffer做DMA的进程,应该是叶子进程。如果一定要fork,可以考虑对pin住的区域执行madvise(p, size, MADV_DONTFORK),以避免CoW,具体的原理在madvise的man page上说地很清楚:

5.3 使用巨页?

我们如果用户空间是用非透明的巨页的话,实际上这种巨页是“预留式”的,因此可有效的避免内存的回收、swap和因为memory compaction、Automatic NUMA balancing等导致的page migration等。一般用户这么获得hugetlb内存: base_ptr_ = mmap(NULL, memory_size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);它实际上免去了对pin、mlock()等的需求。由于巨页本身可减小TLB miss以及walk page table的路径长度,想必可带来性能提升。 一般情况下,当我们想在系统预留n个巨页的时候,若系统有m个NUMA节点,Linux会倾向于每个NUMA节点预留n/m个巨页。如果采用内存本地化的NUMA策略,任务运行在节点A的话,内核会倾向于从节点A预留的巨页向目标任务分配,所以我们最好是对任务进行一下NUMA的绑定。

6. 最后的话

在对用户空间代码进行轻微控制的情况下,GUP是比较理想的、抗性能抖动的、安全的针对用户空间buffer进行DMA的途径。当然,某些场景下,用户空间程序具有很大的灵活多变性,比如经常要动态malloc、free,我们又要在这样的heap上面去做DMA,每次都去pin和unpin显然是不现实的。这个时候我们可以借用Linux实时编程技术里面常常采用的,让malloc/free在一个存在的堆池发生分配和释放的技术: 诀窍就在于先申请一个大堆,通过mallopt(M_TRIM_THRESHOLD, -1UL)避免大堆在free的时候还给内核。之后所有的malloc,free,都是在事先预留好的大堆进行,不再需要每次malloc/free区域单独pin,它带来了类似SVA的编程灵活性。 如果用户空间采用无GUP的SVA方案,我们可能需考虑屏蔽如下功能以防止性能抖动:

用户评论

绝版女子

宋宝华的作品《论Linux的页迁移》深入探讨了一种优化内存管理的技术。

    有11位网友表示赞同!

桃洛憬

这部游戏让玩家沉浸在一个复杂的操作系统设计和优化领域,非常吸引技术爱好者。

    有18位网友表示赞同!

念安я

从玩家的角度来看,这个主题似乎不那么诱人,但它的理论深度和实用价值是不可忽视的。

    有11位网友表示赞同!

像从了良

我在阅读过程中感觉像是在游戏中体验了一次深入学习Linux内核架构的独特旅程。

    有5位网友表示赞同!

温柔腔

这本书为我打开了一个全新的编程思维路径,即使对于非专业的用户也能激发兴趣。

    有12位网友表示赞同!

暮光薄凉

虽然题目中提到了“游戏”行业,但实际上探讨的是系统设计和逻辑思考的领域,并非游戏本身的娱乐内容。

    有14位网友表示赞同!

熟悉看不清

作为一个计算机科学专业的学生,《论Linux的页迁移》为我理解现代操作系统提供了深入见解。

    有20位网友表示赞同!

珠穆郎马疯@

这本书让我对内存管理有了全新的认知,不再是简单的代码操作,而是背后的系统哲学。

    有19位网友表示赞同!

执拗旧人

即使是对Linux一窍不通的程序员,在宋宝华教授的带领下也能领悟其深意。

    有8位网友表示赞同!

青衫负雪

《论Linux的页迁移》让我开始质疑和思考操作系统设计的基本原理,非常震撼。

    有7位网友表示赞同!

反正是我

这本书为我打开了编程新世界的大门,我看到了技术发展的广阔可能性。

    有19位网友表示赞同!

孤街浪途

通过阅读《论Linux的页迁移》,我发现了解系统底层工作原理的乐趣远远超出我的预期。

    有5位网友表示赞同!

青楼买醉

虽然名字提及的是“游戏”行业,但实际上,这是一部深刻探讨操作系统核心内容的佳作。

    有14位网友表示赞同!

标题:宋保华:浅谈Linux页面迁移(Page Migration)完整版
链接:https://yqqlyw.com/news/sypc/9635.html
版权:文章转载自网络,如有侵权,请联系删除!
资讯推荐
更多
绯红之境兑换码最新2021 礼包兑换码大全

绯红之境兑换码最新2021 礼包兑换码大全[多图],绯红之境兑换码怎么领取?绯红之境兑换码有哪些?绯红之境在今日

2024-09-09
妄想山海怎么加好友 加好友方法大全

妄想山海怎么加好友 加好友方法大全[多图],妄想山海添加好友功能在哪里?妄想山海添加好友的方法是什么?好友添

2024-09-09
三国群英传7霸王再临攻略 霸王再临攻略技巧开启方法

三国群英传7霸王再临攻略 霸王再临攻略技巧开启方法[多图],三国群英传7霸王再临怎么玩?三国群英传7霸王再临

2024-09-09
江南百景图又见桃花村钓鱼位置在哪?又见桃花村钓鱼攻略

江南百景图又见桃花村钓鱼位置在哪?又见桃花村钓鱼攻略[多图],江南百景图又见桃花村钓鱼怎么钓?又见桃花村钓

2024-09-09