0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

细说vDSO机制原理

Linux爱好者 来源:hsqstephenzhang.github.io 2023-06-14 12:33 次阅读

1. 什么是 vDSO

众所周知,操作系统为我们管理硬件资源,并以系统调用的方式对用户进程提供 API,但是syscall很慢,涉及陷入内核以及上下文切换。对于少量频繁调用的系统调用(比如获取当期系统时间)来说,是否可以某种安全的方式开放到用户空间,让用户直接访问而不需要经过syscall呢?

vDSO就是用来解决这个问题的。

vDSO全称为virtual dynamic shared object,dynamic shared object 这个名词大家应该有所耳闻,就是 Linux 下的动态库的全称,而 virtual 表明,这个动态库是通过某种手段虚拟出来的,并不真正存在于 Linux 文件系统中。

要验证这点也很简单,只需要通过 ldd 命令,查看一些可执行文件所依赖的动态库即可,

$ldd/bin/ls
linux-vdso.so.1(0x00007ffe4e4ce000)
libcap.so.2=>/usr/lib/libcap.so.2(0x00007f7bf818e000)
libc.so.6=>/usr/lib/libc.so.6(0x00007f7bf7fc2000)
/lib64/ld-linux-x86-64.so.2=>/usr/lib64/ld-linux-x86-64.so.2(0x00007f7bf81e8000)

可以明显看出,在ls 这个可执行文件依赖的动态库列表中,除了 linux-vdso.so.1 都有明确的路径,同时还可以通过 proc 文件系统中进程的内存映射(memory map)情况来映射这一点:

$cat/proc/1/maps
....
7fd37e90f000-7fd37e911000rw-p0002f000103:0213244335/usr/lib/ld-2.33.so
7ffc2f7ce000-7ffc2f7ef000rw-p0000000000:000[stack]
7ffc2f7f7000-7ffc2f7fb000r--p0000000000:000[vvar]
7ffc2f7fb000-7ffc2f7fd000r-xp0000000000:000[vdso]
ffffffffff600000-ffffffffff601000--xp0000000000:000[vsyscall]

可以看出,vDSO 确实是以共享库的形式存在于每一个进程当中的。

通过 vDSO,进程访问一些系统提供的 API,就可以直接在自己的地址空间访问,而不需要进行用户-内核态的状态切换了

2. vDSO 实现原理

linux-vdso.so.1既然不是一个实实在在的文件,那其中的内容就应该直接保存在内存中,Linux 使用vdso_image来表示

2.1 vDSO image

在arch/x86/entyr/vdso/vdso-image-64.c文件中,定义了下面的vdso_image:

staticunsignedcharraw_data[8192]__ro_after_init__aligned(PAGE_SIZE)={
0x7F,0x45,0x4C,0x46,0x02,0x01,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x3E,0x00,
...
};

conststructvdso_imagevdso_image_64={
.data=raw_data,
.size=8192,
.alt=3013,
.alt_len=91,
.sym_vvar_start=-16384,
.sym_vvar_page=-16384,
.sym_pvclock_page=-12288,
.sym_hvclock_page=-8192,
.sym_timens_page=-4096,
};

vdso_image.raw_data对应的就是 vDSO 提供的所有系统调用的二进制指令,一共有 8192 字节,相当于下面的结构:

staticstructpage*pages[2];

vdso_iamge_64自然需要保存到全局变量中才能发挥作用,这就涉及接下来要提到的 vDSO 初始化。

2.2 vDSO 初始化

vDSO 通过init_vdso()函数来初始化,通过条件编译对 32/64 bit 的 image 进行选择。同时也需要通过subsys_initcall(init_vdso)将init_vdso()放到initcall列表中。

init_vdso_image()这里不过多介绍,主要是用来优化指令,毕竟 vdso_image 中提供的二进制指令是手动放在一个数组中的,还有相当大的优化空间

staticint__initinit_vdso(void)
{
BUILD_BUG_ON(VDSO_CLOCKMODE_MAX>=32);

init_vdso_image(&vdso_image_64);

#ifdefCONFIG_X86_X32_ABI
init_vdso_image(&vdso_image_x32);
#endif

return0;
}
subsys_initcall(init_vdso);

2.3 vDSO 和 可执行程序

如果你对 Linux 可执行程序的 加载-执行机制有所研究,就知道对于 elf 格式的可执行程序而言,最终调用了load_elf_binary()这个回调函数,在这个函数中,会根据 elf 文件头中的描述,设置好新进程的各个段,并将 elf 文件中的内容拷贝到相应位置。

为什么好端端的,要提到可执行程序加载呢?这是因为,在系统初始化完成之后,vdso_image已经设置完毕,只需要在每次加载二进制可执行程序的时候,分配一块内存空间,将vdso_image加载到该位置即可。

这就是arch_setup_additional_pages()函数所要完成的任务了:

intarch_setup_additional_pages(structlinux_binprm*bprm,intuses_interp)
{
if(!vdso64_enabled)
return0;
returnmap_vdso_randomized(&vdso_image_64);
}

map_vdso_randomized()会通过stack protect机制,选择一个随机的加载地址,并调用map_vdso完成 mapping 工作,该函数内容较多,这里不赘述。

最终,vDSO 会向用户提供四个系统调用:

__vdso_clock_gettime()
__vdso_getcpu()
__vdso_gettimeofday()
__vdso_time()

你还别不信,可以自行验证一下:

使用命令cat /proc/1/maps找到[vdso]对应的内存位置。

通过 dd 命令将内存的影像 dump 到文件中,如:dd if=/proc/1/mem of=/tmp/linux-vdso.so skip=140728627781632 ibs=1 count=4096,其中 skip 的值为 vdso 的内存起始地址,count 为这块内存的大小。

使用objdump命令查看linux-vdso.so中所有符号objdump -T /tmp/linux-vdso.so,最终结果如下。

linux-vdso.so:fileformatelf64-x86-64

DYNAMICSYMBOLTABLE:
0000000000000740wDF.text000000000000015dLINUX_2.6clock_gettime
0000000000000600gDF.text0000000000000127LINUX_2.6__vdso_gettimeofday
00000000000008a0wDF.text0000000000000044LINUX_2.6clock_getres
00000000000008a0gDF.text0000000000000044LINUX_2.6__vdso_clock_getres
0000000000000600wDF.text0000000000000127LINUX_2.6gettimeofday
0000000000000730gDF.text0000000000000010LINUX_2.6__vdso_time
0000000000000730wDF.text0000000000000010LINUX_2.6time
0000000000000740gDF.text000000000000015dLINUX_2.6__vdso_clock_gettime
0000000000000000gDO*ABS*0000000000000000LINUX_2.6LINUX_2.6
00000000000008f0gDF.text0000000000000025LINUX_2.6__vdso_getcpu
00000000000008f0wDF.text0000000000000025LINUX_2.6getcpu

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11290

    浏览量

    209290
  • 操作系统
    +关注

    关注

    37

    文章

    6799

    浏览量

    123276
  • API
    API
    +关注

    关注

    2

    文章

    1496

    浏览量

    61948
  • 命令
    +关注

    关注

    5

    文章

    683

    浏览量

    22011

原文标题:细说|vDSO机制原理

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    求3525电路详细说

    求3525电路详细说明,越详细越好,谢谢!
    发表于 04-18 08:21

    spi总线协议详细说

    本帖最后由 eehome 于 2013-1-5 09:52 编辑 spi总线协议详细说
    发表于 08-18 21:28

    图表细说电子元器件

    本帖最后由 eehome 于 2013-1-5 09:57 编辑 84404844058440684407图表细说电子元器件
    发表于 09-06 16:30

    hex文件格式详细说

    hex文件格式详细说
    发表于 11-13 12:36

    hex文件格式详细说

    hex文件格式详细说
    发表于 11-13 12:37

    stc下载烧录详细说

    stc下载烧录详细说
    发表于 01-05 16:28

    细说Android 4.0NDK编程源码

    本帖最后由 richthoffen 于 2019-7-19 16:39 编辑 细说Android 4.0NDK编程源码
    发表于 07-19 08:39

    Linux内核详细说

    Linux_内核详细说
    发表于 03-28 09:46 35次下载

    linux内存管理机制浅析

    本内容介绍了arm linux内存管理机制,详细说明了linux内核内存管理,linux虚拟内存管理,arm linux内存管理等方面的知识
    发表于 12-19 14:09 73次下载
    linux内存管理<b class='flag-5'>机制</b>浅析

    图表细说电路回路法识图_图表细说系列丛书—胡斌编著

    电子发烧友网站提供《图表细说电路回路法识图_图表细说系列丛书—胡斌编著.txt》资料免费下载
    发表于 03-30 16:31 0次下载

    SPI接口详细说

    SPI接口详细说
    发表于 12-23 02:11 8次下载

    使用AT89S51单片机制作红外遥控器的资料和源代码详细说

    本文档的主要内容详细介绍的是使用AT89S51单片机制作红外遥控器的资料和源代码详细说明。
    的头像 发表于 05-30 09:26 6181次阅读

    使用单片机制作密码锁的电路图和程序与流程图的详细说

    本文档的主要内容详细介绍的是使用单片机制作密码锁的电路图和程序与流程图的详细说明。
    发表于 09-09 16:08 47次下载
    使用单片<b class='flag-5'>机制</b>作密码锁的电路图和程序与流程图的详<b class='flag-5'>细说</b>明

    Memory芯片的测试资料详细说

    本文档的主要内容详细介绍的是Memory芯片的测试资料详细说明包括了:Memory芯片的重要性,Memory类型和结构特点, Memory失效机制, Memory测试标识缩写, Memory故障模型
    发表于 11-30 08:00 0次下载
    Memory芯片的测试资料详<b class='flag-5'>细说</b>明

    细说Linux Out Of Memory机制

    有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
    的头像 发表于 02-12 09:57 928次阅读