一文细说vDSO机制原理

news2025/1/11 0:23:19

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-7fd37e911000 rw-p 0002f000 103:02 13244335                  /usr/lib/ld-2.33.so
7ffc2f7ce000-7ffc2f7ef000 rw-p 00000000 00:00 0                          [stack]
7ffc2f7f7000-7ffc2f7fb000 r--p 00000000 00:00 0                          [vvar]
7ffc2f7fb000-7ffc2f7fd000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [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:

static unsigned char raw_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, 
    ...
};

const struct vdso_image vdso_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 字节,相当于下面的结构:

static struct page *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 中提供的二进制指令是手动放在一个数组中的,还有相当大的优化空间

static int __init init_vdso(void)
{
    BUILD_BUG_ON(VDSO_CLOCKMODE_MAX >= 32);

    init_vdso_image(&vdso_image_64);

#ifdef CONFIG_X86_X32_ABI
    init_vdso_image(&vdso_image_x32);
#endif

    return 0;
}
subsys_initcall(init_vdso);

 资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈

2.3 vDSO 和 可执行程序

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

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

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

int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
    if (!vdso64_enabled)
        return 0;
    return map_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:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000740  w   DF .text  000000000000015d  LINUX_2.6   clock_gettime
0000000000000600 g    DF .text  0000000000000127  LINUX_2.6   __vdso_gettimeofday
00000000000008a0  w   DF .text  0000000000000044  LINUX_2.6   clock_getres
00000000000008a0 g    DF .text  0000000000000044  LINUX_2.6   __vdso_clock_getres
0000000000000600  w   DF .text  0000000000000127  LINUX_2.6   gettimeofday
0000000000000730 g    DF .text  0000000000000010  LINUX_2.6   __vdso_time
0000000000000730  w   DF .text  0000000000000010  LINUX_2.6   time
0000000000000740 g    DF .text  000000000000015d  LINUX_2.6   __vdso_clock_gettime
0000000000000000 g    DO *ABS*  0000000000000000  LINUX_2.6   LINUX_2.6
00000000000008f0 g    DF .text  0000000000000025  LINUX_2.6   __vdso_getcpu
00000000000008f0  w   DF .text  0000000000000025  LINUX_2.6   getcpu

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CDN 回源与CDN 多级缓存原理

一、什么是回源 回源是指浏览器在发送请求报文时,响应该请求报文的是源站点的服务器,而不是各节点上的缓存服务器(比如Nginx开启缓存),那么这个过程相对于通过各节点上的缓存服务器来响应的话就称作为回源。回源的请求…

Mac 原神电脑版下载安装使用教程,MacBook 上也可以玩原神了

最近发现了一个很棒的工具,他可以让你的 Mac 苹果电脑运行原神,而且画质和流畅度都是在线的,今天分享给大家 软件名字叫 playCover ,根据作者的介绍这款软件最初就是国外的一位博主想在 Mac 上玩原神特意开发的一款软件&#xff…

Faster R-CNN网络架构详解和TensorFlow Hub实现(附源码)

文章目录 一、RPN网络1. RPN网络简介2. backbone网络简介 二、Faster R-CNN网络架构1. Faster R-CNN网络简介2. 基于TensorFlow Hub实现Faster R-CNN 前言:Faster R-CNN的简介见 上一篇文章 一、RPN网络 1. RPN网络简介 RPN网络全称Region Proposal Network&#…

【星戈瑞】BODIPY-530/550氟化硼二吡咯荧光染料

BODIPY是一种荧光染料,其分子结构稳定、荧光强度高、荧光寿命长、光谱范围广,因此在许多领域都有应用。在生物医学领域,BODIPY作为荧光探针,可用于细胞成像、生物分子探测、药物筛选等方面。例如,一些研究者将BODIPY修…

如何在华为OD机试中获得满分?Java实现【计算某字符出现次数】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

今天面了个字节拿23k出来的小伙,让我见识到了什么才是天花板...

2023年堪称大学生就业最难的一年,应届毕业生人数是1158万,再次刷新记录。 但是我观察到一个数据,那就是已经就业的毕业生中,计算机通信等行业最受毕业生欢迎! 计算机IT行业薪资高,平均薪资是文科其他岗位的…

Java之旅(二)

Java安装 检查您是否在 Windows PC 上安装了 Java,请在开始栏中搜索 Java 或在命令提示符 (cmd.exe) 中键入以下内容:java -version 在 Windows 上安装 Java: 转到“系统属性”(可以在控制面板 > 系统和安全 > 系统 >…

android aidl及binder基础知识总结

1、什么是binder binder是android framework提供的,用于跨进程方法调用的机制,具有安全高效等特点。 我们知道,在 Android 系统中,每个应用程序都运行在一个独立的进程中,各个进程之间需要进行数据交换和调用&#x…

元宇宙应用领域-医疗

元宇宙(Metaverse)是一个虚拟空间,用户可以通过数字技术和设备在其中生活和工作。元宇宙由一系列相关的技术和应用组成,包括区块链、虚拟现实、增强现实、人工智能、网络安全、大数据和云计算等。 元宇宙是一种新型的虚拟空间&am…

【加解密】bcryptjs | CryptoJS | JSEncrypt | node-rsa 加密| 解密 | RSA | ASE | MD5

加解密 1、 bcryptjs 解密 - 只可加密,比对密码,不可解密 下载 npm i bcryptjs 作用:字符串加密,已加密的字符串不可破解,只可比对。优点:加密的字符不可解密缺点:已加密的字符不可解密&#…

【TI毫米波雷达笔记】IWR6843AOPEVM开箱功能测试

【TI毫米波雷达笔记】IWR6843AOPEVM开箱功能测试 我用的是IWR6843AOPEVM-G 相关资源可以在ti官网下载 要用的软件是 TI官方上位机 mmWave_Demo_Visualizer 可以用网页版 也可以用软件包 建议先上网页版看看版本支不支持对应的板子 网页版: dev.ti.com/gallery/…

CMake之安装

目录 公共选项安装目标安装文件安装目录安装导出 公共选项 install有多个签名,这些签名公用的选项有以下: DESTINATION:指定文件要安装的目录,可以是相对路径或绝对路径。 相对路径:会使用 CMAKE_INSTALL_PREFIX 作为…

rttread-nano 使用记录:rt_kprintf函数格式化打印无法左对齐

rttread-nano 使用记录:rt_kprintf函数格式化打印无法左对齐 今天用rt_kprintf函数打印输出一个表格,为了表格好看每一列我都使用格式化参数-负号符号设置为了左对齐,但是发现无法打印,也无法打印浮点数,换成微库的pri…

通过注册表显示和隐藏“我的电脑”、“回收站”等图标

注册表路径: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel 其中{208D2C60-3AEA-1069-A2D7-08002B30309D}是我的电脑,值的类型为REG_DWORD,改为0后隐藏,1代表显示。 其…

Try ‘apt --fix-broken install’ with no packages解决办法

在Ubuntu中用apt-get安装软件,系统报出Unmet dependencies错误。 Unmet dependencies. Try ‘apt --fix-broken install’ with no packages (or specify a solution) 解决方案如下: sudo apt --fix-broken install sudo apt-get update sudo apt-get u…

建筑专业应届生零基础想学习数据分析,职业发展前景如何?能学会吗?

建筑应届生零基础想学习数据分析,职业发展前景如何?能学会吗? 当然是能学会的,但如果想要有发展前景,不仅是会数据分析而已,更需要实战能力,能够结合不同的业务进行分析,掌握各种常见…

openFast中的陆上风电机组5MW_Land_DLL_WTurb参数详解

文章目录 一、openFAST是什么?二、参数截图三、参数详解 一、openFAST是什么? openFAST是一种开放源代码的工具,为风能工程师提供了用于模拟和设计风力涡轮机的框架。它可以计算风力涡轮机在各种环境条件下的性能,并提供对风力涡…

HTML、CSS、 JavaScript介绍(二)

CSS 指层叠样式表 (Cascading Style Sheets),CSS定义如何显示 HTML 元素。HTML 标签原本被设计为用于定义文档内容,样式表定义如何显示 HTML 元素,就像 HTML 中的字体标签和颜色属性所起的作用那样。样式通常保存在外部的 .css 文件中。我们只…

「GPT实战」GPT接入直播间实现虚拟人互动

摘要 ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用。结合两者的优势和特点,可以探索出更多的应用场景和商业模式。例如,在元宇宙中使用ChatGPT进行自然语言交互,可以为用户提供更加智能化、个性化的服务和支持;在Ch…

清除浮动的方法

目录 清除浮动 2.1 清除浮动的方法 — ① 直接设置父元素高度 2.2 清除浮动的方法 — ② 额外标签法 2.3 清除浮动的方法 — ③ 单伪元素清除法 2.4 清除浮动的方法 — ④ 双伪元素清除法 2.5 清除浮动的方法 — ⑤ 给父元素设置overflow : hidden 清除浮动 ➢ 含义&#…