MAP_DENYWRITE:被Linux内核屏蔽的flag

简介: 一 背景谈到MAP_DENYWRITE,可能有些陌生。这个flag很少被用户态开发者关注,其中没有被关注的理由主要是“this flag is ignored by os”,简而言之,操作系统(Linux内核)将会忽略掉用户传入的MAP_DENYWRITE标志。回到MAP_DENYWRITE是什么?与MAP_ANONYMOUS、MAP_SHARED、MAP_PRIVATE等一样,是系统调用mmap

一 背景

谈到MAP_DENYWRITE,可能有些陌生。这个flag很少被用户态开发者关注,其中没有被关注的理由主要是“this flag is ignored by os”,简而言之,操作系统(Linux内核)将会忽略掉用户传入的MAP_DENYWRITE标志。回到MAP_DENYWRITE是什么?与MAP_ANONYMOUS、MAP_SHARED、MAP_PRIVATE等一样,是系统调用mmap()函数为映射目标设置映射方式的一种flag。顾名思义,MAP_DENYWRITE表示这段映射的虚拟地址区间不允许写操作,例如,我们通过open()以可写的方式获取文件句柄,将会返回“ETXTBSY: text file is busy”错误。

本文主要介绍MAP_DENYWRITE相关的内容,例如使用了MAP_DENYWRITE的可执行二进制文件忽略MAP_DENYWRITE的动态共享库。为了描述简单,后面直接使用EXEC和DSO(Dynamic Shared Object)分别表示可执行二进制文件和动态共享库。为了更加清晰的展示MAP_DENYWRITE作用,设计了实验1和实验2来显示用户态可见的差异。同时,借助实验1和实验2也为引出两个疑问:(a)为什么i_writecount会导致“ETXTBSY: text file is busy”;(b)为什么vim“写-存”DSO触发程序“Segment fault”;其答案分别可以在第三节和第四节获取。最后,第五节设计了最后一个实验对实验3进行补充。

二 MAP_DENYWRITE是什么

关于MAP_DENYWRITE是什么?这里先直接引用一下man2上的描述:

图1:MAP_DENYWRITE官方描述

看样子,Linux内核会忽略用户态传入的MAP_DENYWRITE标志(参考:https://man7.org/linux/man-pages/man2/mmap.2.html),主要是为了防止DDoS攻击。

早期的社区讨论

2001年这个问题就已经被讨论了,如下图:

图2:Linus关于解释Linux内核屏蔽MAP_DENYWRITE的邮件

图2中,Linus以mmap("/etc/passwd")为例,说明了内核为什么忽略MAP_DENYWRITE。另外,更明显的例子便是普通的log日志文件,如果有用户恶意使用MAP_DENYWRITE的方式map了该文件,那么对该日志文件存在写操作的所有进程都将会崩溃,这是一个严重的漏洞。

尽管Linux内核屏蔽的用户态传入的MAP_DENYWRITE,但是,还是有那么一个“固执”的程序至今为止仍坚持在使用mmap()映射DSO时使用MAP_DENYWRITE,即ld.so。既然DSO在映射过程中实际传入到内核的MAP_DENYWRITE被忽略了,那么为何不提交一个补丁在glibc中去掉这一块相关的代码?考古发现,其实也有人多年前提交过,我们可以找到当年的邮件讨论,下面是截取的其中一个Maintainer的观点:

图3:glibc社区对是否保留MAP_DENYWRITE的讨论

图中的意见基本表示glibc会保留在映射DSO时加入MAP_DENYWRITE标志,认为这种问题应该Linux内核去解决,详细可以参考链接1。

写到这里似乎这篇文章就可以全剧终了。

当然还有剧情,男女主角还没出现!“作者还能继续编”!尽管Linux内核忽略了从用户态传入的该参数,但是这个参数内核自己还是可以用:内核在加载初始化ELF可执行文件时,整个过程都在内核态完成,所以内核直接可以加上这个flag,而不会引入DDoS攻击。

本小节接下来将会设计两个与MAP_DENYWRITE有关联的实验,这两个实验可以帮助我们更清晰的解读MAP_DENYWRITE的故事:

  • 实验1:EXEC文件本身在执行过程中,操作系统将会在其代码段的映射区间印上MAP_DENYWRITE,那么如何可以看出来?同时正如前面的提到的,对其代码进行写,如何触发“ ETXTBSY: text file  is  busy ”错误。
  • 实验2:为了防止DDoS攻击,操作系统忽略掉了用户(例如ld.so)传入的MAP_DENYWRITE。那么对其进行写打开又有哪些现象?与EXEC又有哪些不同?

实验1:对EXEC的代码段进行测试

下面是一个进程的可执行文件本身的代码段在被映射后的截图:

图4:EXEC代码段smaps信息

在图2中dw是该进程的可执行文件本身,并且最后VmFlags一行可以包含dw的标志,表示内核在映射dw的代码段过程中,带上了MAP_DENYWRITE。下面一个很简单的用例来表示如何复现“ETXTBSY: text file is busy”。

图5:一个发生“ETXTBSY: text file is busy”错误的用例演示

图5中,演示了一个程序在执行过程中,对自己的EXEC文件dw_txtbsy以可写的方式进行打开,其中使用的测试用例如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
        int fd;

        fd = open("/mnt/nvme2/map_denywrite/dw_txtbsy", O_WRONLY);
        if (fd < 0)
                perror("dw_txtbsy:");

        return 0;
}

实验2:对DSO的代码段进行测试

与实验1相同,首先展示DSO的代码段在建立映射后的smaps信息,如下图所示。

图6:DSO代码段smap信息

图6选择几乎所有的程序都依赖的libc.so为例。在其VmFlags中并没有发现dw标志,所以从逻辑上,我们可以想到如果对其进行EXEC同样的实验,并不会发生“ETXTBSY: text file is busy”错误。这里给出测试的用例,感兴趣的小伙伴可以自行验证。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
        int fd;

        fd = open("/usr/lib64/libc-2.32.so", O_WRONLY);
        if (fd < 0)
                perror("dw_txtbsy:");

        return 0;
}

其实在这个实验中,我们更关系的是下面的实验:

实验设计:既然前面提到DSO可以以写的方式open,那么我们自己写一个简单的DSO文件,并让另外一个可执行文件中链接调用其中的函数,保持进程运行状态,同时通过vim对该DSO进行修改,看看会出现什么现象。

代码1:load.c

#include <stdio.h>

int foo(void);

int foo(void)
{
        printf("foo: 1\n");
        return 0;
}

代码2:dso_w.c

#include <stdio.h>
#include <unistd.h>

extern int foo(void);

int main(void)
{
        while(1) {
                foo();
                sleep(3);
        }

        return 0;
}

编译过程:

$ gcc -O2 -fPIC -c -o load.o load.c
$ gcc -shared -o libload.so load.o
$ gcc -O2 -o dso_w dso_w.c libload.so -Wl,-R,.
$ ./dso_w

下面的实验的截图:

图7:对正在使用的DSO进行修改发生Segmentation fault

图7中,最开始程序正常打印“foo: 1”,但是当我们直接使用vim对libload.so进行修改成“foo: 2”后,发生了“Segmentation fault”。当我们重新执行该程序时,我们的修改起效了。由此,本实验基本可以得出结论:对于DSO,虽然由于没有MAP_DENYWRITE,可以允许用户对其进行修改,但是通过vim“修改-保存”的操作,将会导致正常依赖该DSO的程序直接异常退出。

写到这里,DSO的第二个实验基本完成。

小结

前面两个实验主要为证明在存在或没有MAP_DENYWRITE时,程序行为有何差异。当然除以上实验外,与MAP_DENYWRITE相关的还有install、cp和mv等命令。对于已经被MAP_DENYWRITE映射的文件,可以使用install可以替换inode,但是无法使用cp或者mv。其中详细读者可以自行调研。

此外,前面的两个实验也为引出下面的问题:

  • 对EXEC代码段进行写时,发生“ ETXTBSY: text file  is  busy ”的底层内核逻辑是什么?
  • 对正常使用的DSO写时,为什么会发生“Segmentation fault”?以及背后的符号重定向逻辑细节是什么?

下面一节将尝试一一解释。

三 为什么i_writecount会导致“ETXTBSY: text file is busy”

紧跟上文,前面提到EXEC的代码段和数据段的映射过程基本都在内核态完成,因此加入MAP_DENYWRITE是可信任的。因此,本节基于此,想要理清楚对EXEC代码段进行写时,发生“ETXTBSY: text file is busy”的底层内核逻辑是什么?下面会首先给出导致ETXTBSY错误的根本变量:inode->i_writecount值,该值表示当前有多少写者正在占用该file。最后简单给出open()、mmap()和exec()与其相关联的Linux内核调用链,以供读者复现。

下面是内核中与i_writecount相关的四个函数:

static inline int get_write_access(struct inode *inode)
{
  return atomic_inc_unless_negative(&inode->i_writecount) ? 0 : -ETXTBSY;
}
static inline int deny_write_access(struct file *file)
{
  struct inode *inode = file_inode(file);
  return atomic_dec_unless_positive(&inode->i_writecount) ? 0 : -ETXTBSY;
}
static inline void put_write_access(struct inode * inode)
{
	atomic_dec(&inode->i_writecount);
}
static inline void allow_write_access(struct file *file)
{
	if (file)
		atomic_inc(&file_inode(file)->i_writecount);
}

其中put_write_access()函数会在建立映射的过程中,检测是否存在MAP_DENYWRITE,如果存在,则调用put_write_access()将i_writecount值减1,函数调用链路为:

exec()->load_elf_binary()->vm_mmap_pgoff()->do_mmap()->mmap_region()
      ->{deny | put }_write_access

而当程序通过open(file, O_WRONLY)的方式打开某文件时,内核为做一些列检查,其中包括调用get_write_access()获取写的权限,当该值为负值,get_write_access()将会返回-ETXTBSY,即用户看见的“ETXTBSY: text file is busy”,调用路径大致为:

open()->path_openat->do_o_path->vfs_open->do_dentry_open->get_write_access

如果用一张图总结上面的描述,大致可以画成:

图8:i_writecount与“ETXTBSY: text file is busy”框图

第一个问题到此基本结束。总结一句话:对EXEC而言,最终MAP_DENYWRITE落实到inode->i_writecount发挥作用。相对问题2,简单很多。

四 为什么vim“写-存”DSO触发程序“Segment fault”?

在开始一堆概念解释前,首先先给出标题的答案:在实验2中,我们通过vim修改二进制的方式,将“printf("foo: 1\n")”改为“printf("foo: 2\n")”,该操作将会触发“清空libload.so pagecache--重新读取到pagecache”等操作【】,该操作完成以后GOT和PLT中的数据没并用根据当前实际映射情况进行更新(正常情况下ld.so会在映射DSO完成后,调用_dl_runtime_resolve函数初始化库中成员函数的实际映射地址)。简而言之,便是再次执行foo函数时,由于DSO实际映射的数据段中GOT和PLT数据未被初始化,为异常地址,直接执行这些异常地址便会产生段错误。

注:

(1)使用vim修改DSO并保存到磁盘时,vim使用了open(O_WRONLY | O_TRUNC),其中O_TRUNC会截断整个文件,执行“truncate pagecache”操作,清空文件在内存中的pagecache和页表。当下次访问DSO中的成员函数时,重新触发缺页异常,初始化pagecache等。这属于vim行为,若使用open(O_WRONLY)不会影响在内存DSO的pagecache。

(2)正常情况下,DSO被映射后,其内存中的GOT和PLT数据与磁盘中DSO文件的GOT和PLT段数据是不同的。

GOT和PLT

在讲GOT和PLT的功能前,可以先思考这样一个问题:一个DSO会被不同的进程依赖,并且在不同进程地址空间中,所才的地址空间也不同,因此这种差异是如何解决的?并且让不同的进程可以和谐运行下去。

答案就是GOT,不同的进程对不同的DSO映射地址的保存都是私有的数据,这些私有的GOT数据就保存在各自DSO内存中的数据段中

链接器ld.so在执行重定向时会用到的部分, 先来看他们的定义。

(1).got

这是我们常说的GOT, 即Global Offset Table, 全局偏移表. 这是链接器在执行链接时,实际上要填充的部分, 保存了所有外部符号的地址信息。GOT表项还保留了3个公共表项, 每项32位(4字节), 保存在前三个位置, 分别是:

  • got[0]:本ELF动态段(.dynamic段)的装载地址;
  • got[1]:本ELF的link_map数据结构描述符地址;
  • got[2]:_dl_runtime_resolve函数的地址。 对于一个运行的进程,其本身的GOT以及依赖库的GOT皆是由ld.so来填充

(2).plt

这也是我们常说的PLT(Procedure Linkage Table),即进程链接表. 这个表里包含了一些代码,主要有两个作用:

  • 调用链接器来解析某个外部函数的地址, 并填充到.got.plt中, 然后跳转到该函数;
  • 直接在.got.plt中查找并跳转到对应外部函数(如果已经填充过);

(3).got.plt

.got.plt相当于.plt的GOT全局偏移表, 其内容有两种情况, 1)如果在之前查找过该符号,内容为外部函数的具体地址. 2)如果没查找过,则内容为跳转回.plt的代码, 并执行查找。至于为什么要这么绕, 后面会说明具体原因。

(4).plt.got

略。

这里就以实验2中dso_w.c和load.c为例,探究调用foo函数的过程中涉及的PLT和GOT访问。详细的流程如下图所示:

图9:函数foo重定向流程框图

图9中展示了第一次访问foo函数时发生跳转以及访问GOT和PLE表的四个重要过程。四个过程如图中标记,其简介大致为:

  • 过程1:主要是访问foo函数,此时会跳转到foo@plt,该地址处于PLT表中;
  • 过程2:在foo@plt中,需要访问.got.plt表,获取0x420000+32处存在值,即0x400520。并跳转到该位置;
  • 过程3:经过过程2的跳转,此时处于PLT表的最开始位置(0x400520),此处会访问0x41f00+4088处( 位于GOT表 )所存值,并跳转,即执行_dl_runtime_resolve函数;
  • 过程4,在该函数中,将会修改过程2中访问的0x400520为真实的函数地址。当下次访问foo函数时,在执行过程1以后就会执行DSO中真正的foo函数;

看完这四个过程,不禁让人疑惑_dl_runtime_resolve函数是在何时被写入到GOT表中以及它如何计算出foo函数实际的映射地址。关于第一个问题,前面有简单的提到(“对于一个运行的进程,其本身的GOT以及依赖库的GOT皆是由ld.so来填充”),对于本文比较关心的DSO而言,其GOT便是在ld.so将其mmap到进程的地址空间以后初始的。作者在此大胆推测:前面所写DSO说发生的段错误,应该是由于重新缺页读取的DSO后,其GOT和PLT各项为非法地址,例如过程2访问.got.plt表时读取的异常地址发生错误跳转后便触发段错误。

一段插曲:在实验验证前,原文此处为“大胆推测:前面所写DSO说发生的段错误,应该是由于重新缺页读取的DSO后,其GOT各项为非法地址,例如过程3执行_dl_runtime_resolve函数就发生的段错误。”。但是实验验证后发现此推测忽略了DSO的PLT也属于代码段,映射后地址也发生了变化。

基于该推测性结论,我们再大胆猜测是否存在另一个结论:如果DSO中的foo函数,没有再调用其他库函数,即不涉及访问GOT/PLT行为,那么对该DSO任意修改(看上去像所谓的DSO热升级),应该不会导致“Segmentation fault”。关于该结论的验证,留到本节最后“最后一个实验”进行验证。

实验3:问题解剖

经过上一节分析,本节开始验证前面的推测:“前面所写DSO说发生的段错误,应该是由于重新缺页读取的DSO后,其GOT和PLT各项为非法地址,例如过程2访问.got.plt表时读取的异常地址发生错误跳转后便触发段错误”。为验证该推测,任然选择前面实验2的代码:dso_w.c和load.c。本实验其实是实验2的后续,实验步骤完全相同,但是本实验中需要分析生成的core.pid文件,以及找到出现段错误的根本原因。

在实验前,首先需要对环境进行配置,确保可以生成core dump文件。

$ sysctl -w kernel.pattern="core.$p"

此外,实验过程中借助objdump -DTR dso_w命令获取GOT/PLT表地址信息,例如获取GOT的地址为0x41ffd8,如下所示。同时,利用gdb查看GOT表中内容:

gef➤  disassemble 0x41ffd8
Dump of assembler code for function _GLOBAL_OFFSET_TABLE_:
   0x000000000041ffd8:  .inst   0x0041fde8 ; undefined
   0x000000000041ffdc:  .inst   0x00000000 ; undefined
   0x000000000041ffe0:  .inst   0x00000000 ; undefined
   0x000000000041ffe4:  .inst   0x00000000 ; undefined
End of assembler dump.

注意,在上面的gdb中,我们查看的是非运行时的dso_w数据,因此看到的GOT表中各项基本为零值。

下面是定位原因的过程。

$ gdb dso_w core.262672

gef➤  bt # 第一步
#0  0x00000000000004a0 in ?? ()
#1  0x0000ffff8f48c5ec in foo () at load.c:9
#2  0x0000000000400670 in main () at dso_w.c:9
gef➤  disassemble 0x0000ffff8f48c5ec # 第二步
Dump of assembler code for function foo:
   0x0000ffff8f48c5d4 <+0>:     stp     x29, x30, [sp, #-32]!
   0x0000ffff8f48c5d8 <+4>:     mov     x29, sp
   0x0000ffff8f48c5dc <+8>:     str     wzr, [sp, #28]
   0x0000ffff8f48c5e0 <+12>:    adrp    x0, 0xffff8f48c000
   0x0000ffff8f48c5e4 <+16>:    add     x0, x0, #0x610
   0x0000ffff8f48c5e8 <+20>:    bl      0xffff8f48c4e0 <puts@plt>
   0x0000ffff8f48c5ec <+24>:    ldr     w0, [sp, #28]
   0x0000ffff8f48c5f0 <+28>:    ldp     x29, x30, [sp], #32
   0x0000ffff8f48c5f4 <+32>:    ret
End of assembler dump.
gef➤  disassemble 0xffff8f48c4e0 # 第三步
Dump of assembler code for function puts@plt:
   0x0000ffff8f48c4e0 <+0>:     adrp    x16, 0xffff8f4ac000 <__cxa_finalize@got.plt>
   0x0000ffff8f48c4e4 <+4>:     ldr     x17, [x16, #16]
   0x0000ffff8f48c4e8 <+8>:     add     x16, x16, #0x10
   0x0000ffff8f48c4ec <+12>:    br      x17
End of assembler dump.
gef➤  x /x 0xffff8f4ac010 # 最后一步
0xffff8f4ac010 <puts@got.plt>:  0x000004a0

上面标出了四个步骤:

  • 第一步:通过core dump文件,可以看到发生错误的原因是pc异常值:0x4a0;
  • 第二步:查看foo函数的汇编代码,确定跳转到0xffff8f48c4e0;
  • 第三步:查看0xffff8f48c4e0处的汇编代码,发现接下来将会从.got.plt中读取0xffff8f4ac010处的值;
  • 最后一步:查看0xffff8f4ac010地址上所存值为0x4a0,导致原因找到;

上面四步其实就是将图9中展示的几个过程在gdb中复现了一遍。最后一步读取的值“0x000004a0”直接指明了发生段错误就是因为.got.plt表各项没有被初始化,还是错误的地址。

五 最后一个实验

与前面实验2相似,如下为load.c源码,在这个实验中,通过二进制的方式将“int i = 0”这行代码修改为"int i = 1"的代码。根据前面的GOT和PLT分析,该操作不会导致程序段错误。

#include <stdio.h>

int foo(void);
int foo(void)
{
        int i = 0;
        return i;
}

本实验需要的步骤与实验2完全相同,仅仅是修改的数据不同。在这个实验中,首先在libload.so中定位到“int i = 0”的机器码,对机器码直接修改。在vim中修改时,通过借助xxd和xxd -r进行十六进制转换以及恢复。

如前面猜测的那样,foo函数不依赖DSO的GOT/PLT内容,随意修改成合法的指令照样运行。因此本实验并不会触发程序段错误。

六 总结

前面主要通过四个实验来介绍MAP_DENYWRITE,以及解释与MAP_DENYWRITE有关的两个“为什么”。其实本文最初目的是搞清楚“为什么vim“写-存”DSO时会触发程序Segment fault?”,其他内容主要铺垫和引出该问题。文中大量的概念并没有完全阐述清楚,例如GOT/PLT,其内容并不止于文中提到的部分,涉及到许多其他复杂的概念和过程。

本文所描述的问题主要是代码加速项目中对DSO使用大页实现过程中遗留的疑问,项目成员包括弃余,据德,钟江。

参考

  • 有人尝试去掉libc中的MAP_DENYWRITE,但是最终没有去掉: 点我
  • 内核社区的讨论: 点我
  • 知乎-so覆盖coredump带来的思考: 点我
  • ELF中PLT和GOT工作机制简介-可还原: 点我
  • 聊聊动态链接和dl_runtime_resolve: 点我
相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
2月前
|
监控 Linux 开发者
理解Linux操作系统内核中物理设备驱动(phy driver)的功能。
综合来看,物理设备驱动在Linux系统中的作用是至关重要的,它通过与硬件设备的紧密配合,为上层应用提供稳定可靠的通信基础设施。开发一款优秀的物理设备驱动需要开发者具备深厚的硬件知识、熟练的编程技能以及对Linux内核架构的深入理解,以确保驱动程序能在不同的硬件平台和网络条件下都能提供最优的性能。
128 0
|
5月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
237 67
|
3月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
102 0
|
5月前
|
存储 Linux
Linux内核中的current机制解析
总的来说,current机制是Linux内核中进程管理的基础,它通过获取当前进程的task_struct结构的地址,可以方便地获取和修改进程的信息。这个机制在内核中的使用非常广泛,对于理解Linux内核的工作原理有着重要的意义。
215 11
|
6月前
|
自然语言处理 监控 Linux
Linux 内核源码分析---proc 文件系统
`proc`文件系统是Linux内核中一个灵活而强大的工具,提供了一个与内核数据结构交互的接口。通过本文的分析,我们深入探讨了 `proc`文件系统的实现原理,包括其初始化、文件的创建与操作、动态内容生成等方面。通过对这些内容的理解,开发者可以更好地利用 `proc`文件系统来监控和调试内核,同时也为系统管理提供了便利的工具。
253 16
|
8月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
448 15
|
8月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
196 4
|
10月前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
196 26
|
10月前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
390 24
|
9月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。