Linux时间子系统2: clock_gettime的VDSO机制分析

news2024/11/24 1:02:24

        在之前分析clock_gettime的文章中接触到了VDSO,本篇文章是对VDSO的学习总结,借鉴了很多前人的经验。

   1. 什么是VDSO

        vDSO:virtual DSO(Dynamic Shared Object),虚拟动态共享库,内核向用户态提供了一个虚拟的动态共享库。在 Linux 众多的系统调用中,有一部分存在以下特点:

  • 系统调用本身很快,主要时间花费在 trap 过程
  • 无需高特权级别权限

        这部分系统调用如果能够直接在用户空间中执行,则能够对性能有较大的改善。gettimeofday 就是一个典型的例子,它仅仅只是读取内核中的时间信息,而且对于许多应用程序来说,读取系统时间是必要的同时也是频率很高的行为。

        例如在ARM64平台到处的接口如下:

   aarch64 functions
       The table below lists the symbols exported by the vDSO.

       symbol                   version
       ──────────────────────────────────────
       __kernel_rt_sigreturn    LINUX_2.6.39
       __kernel_gettimeofday    LINUX_2.6.39
       __kernel_clock_gettime   LINUX_2.6.39
       __kernel_clock_getres    LINUX_2.6.39

vdso在不同平台的命名略有不同, 如下:

user ABI   vDSO name
─────────────────────────────
aarch64    linux-vdso.so.1
arm        linux-vdso.so.1
ia64       linux-gate.so.1
mips       linux-vdso.so.1
ppc/32     linux-vdso32.so.1
ppc/64     linux-vdso64.so.1
riscv      linux-vdso.so.1
s390       linux-vdso32.so.1
s390x      linux-vdso64.so.1
sh         linux-gate.so.1
i386       linux-gate.so.1
x86-64     linux-vdso.so.1
x86/x32    linux-vdso.so.1

         因为vdso本身是内核提供的机制,被编译进内核,所以并没有具体的文件路径,以上名称是C库访问时需要用到。

        vdso和vsyscall的对比以及vdso引入linux kernel的时间可以参考

The VDSO on arm64

2. 使用VDSO

使用VDSO的方式有三种

  • 使用 C 标准库
  • 使用 dlopen 获取函数地址
  • 使用 getauxvel 获取函数地址

具体可以参考这篇文章:articles/20220717-riscv-syscall-part3-vdso-overview.md · 泰晓科技/RISCV-Linux - Gitee.com

3. VDSO实现原理

a. vdso的编译以及如何集成到内核

        可直接参考链接:泰晓科技 / RISCV-Linux

        这里附上文章中的图片:

b. vdso的几个问题

vdso的初始化同样在上面的文章中讲得很详细了,我们按照如下思路再捋一遍。

1) vdso.so不是给内核用的,但是被内核包含,用户态如何调用到vdso中的代码呢?

2) 内核如何更新数据,数据放在哪里让用户态可以获取到呢

3)用户态通过vdso.so中的代码如何访问到内核中的数据呢?

c. vdso中的代码如何共享给用户态

        vdso被包含进内核,而不是链接进内核,这是因为vdso.so中的代码段是给用户态进程使用的,那么很显然用户态进程需要映射代码段的地址到进程的地址空间。

       首先,在vdso.S(/arch/arm64/kernel/vdso)中,vdso_start,vdso_end定义了vdso代码段的起始地址和结束地址

	.globl vdso_start, vdso_end
	.section .rodata
	.balign PAGE_SIZE
vdso_start:
	.incbin "arch/arm64/kernel/vdso/vdso.so"
	.balign PAGE_SIZE
vdso_end:

	.previous

 vDSO 内核中代码部分地址初始化的时候,vdso_code_start和 vdso_code_end分别被赋值了 vdso_start和 vdso_end,在__vdso_init函数中,使用vdso_info[abi].cm->pages记录了代码段的物理页信息,如下:

	/* Grab the vDSO code pages. */
	pfn = sym_to_pfn(vdso_info[abi].vdso_code_start);

	for (i = 0; i < vdso_info[abi].vdso_pages; i++)
		vdso_pagelist[i] = pfn_to_page(pfn + i);

	vdso_info[abi].cm->pages = vdso_pagelist;

有了物理页信息,那么用户态进程访问代码段,只需要建立物理页与进程虚拟地址空间的映射即可,用户态进程execve解析elf文件时,在内核会调用arch_setup_additional_pages,__setup_additional_pages则会从vdso_info中取出代码段和数据段的page进行映射,从而用户进程就可以访问代码段和数据段的数据了。

	ret = _install_special_mapping(mm, vdso_base, VVAR_NR_PAGES * PAGE_SIZE,
				       VM_READ|VM_MAYREAD|VM_PFNMAP,
				       vdso_info[abi].dm);
	if (IS_ERR(ret))
		goto up_fail;

	if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) && system_supports_bti())
		gp_flags = VM_ARM64_BTI;

	vdso_base += VVAR_NR_PAGES * PAGE_SIZE;
	mm->context.vdso = (void *)vdso_base;
	ret = _install_special_mapping(mm, vdso_base, vdso_text_len,
				       VM_READ|VM_EXEC|gp_flags|
				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
				       vdso_info[abi].cm);

用户态映射后的示意图:

图片来自:杂谈:vdso原理 - 知乎

d. 内核如何更新vdso数据,以及用户态如何访问

有了上面访问代码段的机制,用户态访问数据的机制自然不用再说了,需要注意的是dm 的初始化在 vvar_fault 函数中实现。vvar_fault 是 dm 缺页中断的回调函数。那么内核态如何更新vsdo数据呢,主要通过update_vsyscall更新vdso_data变量

用户态调用vdso函数,以 gettimeofday为例分析 vDSO 函数调用流程,libc 调用 vsdo.so 中 __kernel_gettimeofday 函数, __kernel_gettimeofday 访问 vvar 数据。除了第一次访问会触发 Page Fault (实测开销大于syscall),整个过程不会陷入内核态。

gettimeofday->__kernel_gettimeofday=> special_mapping_fault
__kernel_gettimeofday->__arch_get_vdso_data=> special_mapping_fault->vvar_fault
    __arch_get_hw_counter //从硬件 timer 读取 cntvct_el0 寄存器得到距离上次更新vdso_data的时间差,加上 vdso_data 里的时间得到最终时间

参考资料:

The vDSO on arm64

泰晓科技 / RISCV-Linux

杂谈:vdso原理 - 知乎

        

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

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

相关文章

52.Python-web框架-Django - 多语言编译-fuzzy错误

目录 1.起因 2.原因 3.解决方法 3.1手动移除fuzzy标记 3.2重新生成po文件&#xff0c;并检查是否还存在fuzzy标记 3.3重新编译生成mo文件 1.起因 在Django的国际化和本地化过程中&#xff0c;当你发现某些字段仅显示msgid&#xff0c;而不显示msgstr时&#xff0c;可能是…

如何购买代码签名证书?

在Gworg网站上快速轻松地购买代码签名证书&#xff1a; 1.根据需要选择合适的代码签名证书&#xff1a; OV代码签名、EV代码签名 2.选择结算周期 代码签名证书可购买&#xff0c;有效期最长为3年。 注意&#xff1a;建议一次性3年&#xff0c;因为代码签名证书不能之前续费…

2024.6.12 作业 xyt

今日课堂练习&#xff1a;vector构造函数 #include <iostream> #include <vector> using namespace std;void printVector(vector<int> &v) {vector<int>::iterator iter;for(iterv.begin(); iter ! v.end(); iter){cout << *iter <<…

765432221

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

深入探究MySQL游标(Cursor)

前言 MySQL游标&#xff08;Cursor&#xff09;是MySQL中用于处理查询结果的一种机制。游标允许我们在查询结果集中逐行处理数据&#xff0c;而不是一次性获取所有数据。这对于处理大量数据非常有用&#xff0c;因为它可以减少内存消耗并提高性能。在MySQL中&#xff0c;游标主…

Day49 代码随想录打卡|二叉树篇---二叉搜索树中的搜索

题目&#xff08;leecode T700&#xff09;&#xff1a; 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返回 null 。 方法&#xff1a; …

excel文件被覆盖了怎么恢复?6个方法,文件误点了保存恢复数据(excel篇)

Excel文件误保存覆盖了之前的文档怎么办&#xff1f; 当Excel文件误保存覆盖了之前的文档时&#xff0c;我们可以尝试以下几种方法来恢复数据&#xff1a; 使用Windows的“还原到上一个版本”功能&#xff1a;如果启用了Windows系统的“系统保护”和“文件历史记录”功能&…

python数据分析--- ch6-7 python容器类型的数据及字符串

python数据分析---ch6-7 python容器类型的数据及字符串 1. Ch6--容器类型的数据1.1 序列1.1.1 序列的索引操作1.1.2 加和乘操作1.1.3 切片操作1.1.4 成员测试 1.2 列表1.2.1 创建列表1.2.2 追加元素1.2.3 插入元素1.2.4 替换元素1.2.5 删除元素1.2.6 列表排序&#xff08;1&…

King Media 8.2 中文版安装

King Media-Viral Magazine News Video是一个用于架设社交网站的php脚本&#xff0c;能让您创建一个视频、新闻和图像的新颖社交网站。 功能 支持&#xff1a;从Url、Youtube、Vimeo、Vine、Instagram、Metacafe、DailyMotion上传和分享图片通过Facebook、谷歌、雅虎、Github和…

【计算机网络】TCP报文详解

认识TCP报头 其实协议的形式都是一个结构化的数据&#xff0c;TCP协议也不例外。一起来看看TCP协议的报头是怎么样的。 以上就是TCP报头&#xff0c;实际上是一个结构化的数据&#xff0c;也就是一个结构体。例如&#xff1a; struct tcp_hdr {unsigned int stc_port : 16;un…

2024中国通信技术产业博览会:JUNO光缆与WaveLogic 6技术,海洋深处的数字脉搏

在数字化时代&#xff0c;通信技术是连接世界的桥梁。NTT DATA主导的JUNO海底光缆计划&#xff0c;通过部署Ciena的WaveLogic 6技术&#xff0c;标志着全球通信网络容量的一次重大飞跃。这一进步不仅加强了亚洲与北美之间的数据连接&#xff0c;更为即将到来的“2024中国军民两…

PMBOK® 第六版 制定项目管理计划

目录 读后感—PMBOK第六版 目录 项目管理计划往往被我们普遍认为是一项多余的工作&#xff0c;尤其是在一些项目处于仅依靠人治也能够勉强达成的临界点时。这是因为大多数项目在前期都充斥着诸多不确定性&#xff0c;到处都存在缺失的部分&#xff0c;这就注定了当下所做的计划…

SQL Server 安装后,服务器再改名,造成名称不一致,查询并修改数据库服务器真实名称

SELECT SERVERNAME -- 1.查询旧服务器名称 SELECT serverproperty(servername) AS new --2.查询新服务器名称 -- 3.更新服务器名称 IF SERVERPROPERTY(servername) <> 新服务器名称替换 BEGIN DECLARE server_name NVARCHAR(128) SET server_name 新服务器…

list集合自定义排序

一、基本类型排序 1.list中只有数字或字符串 //升序排序 List<T> ,T为数字或字符串 Collections.sort(list); //降序排序 Collections.sort(list,Collections.reverseOrder());2.list中为对象 基于jdk.18 import lombok.Data;Data public class User {private int i…

SwiftUI中的@ViewBuilder理解与使用

ViewBuilder是一个属性包装器&#xff0c;也是一个自定义的函数包装器&#xff0c;用于构建一个或多个视图。在 SwiftUI 中&#xff0c;很多地方都使用了 ViewBuilder&#xff0c;例如 VStack、HStack、ZStack 和 Group 等。它可以接受多个视图并返回一个单一的组合视图。 比如…

超详解——Python 元组详解——小白篇

目录 1. 元组简介 创建元组 2. 元组常用操作 访问元组元素 切片操作 合并和重复 成员操作符 内置函数 解包元组 元组方法 3. 默认集合类型 作为字典的键 作为函数参数 作为函数的返回值 存储多种类型的元素 4.元组的优缺点 优点 缺点 5.元组的使用场景 数据…

【iOS】UI学习——界面切换

界面切换 push和poppresent和dismiss示例程序 push和pop 在 Objective-C 中,pop 和 push 通常是与 UINavigationController 一起使用的方法,用于控制导航栈中视图控制器的跳转和回退。 push 和 pop 通常成对使用,用于实现导航栈的前进和后退功能。当用户进入新的视图控制器时…

Linux——自动化运维ansibe

一、自动化运维定义 自动化--- 自动化运维&#xff1a; 服务的自动化部署操作系统的日常运维&#xff1a;日志的备份、临时文件清理、服务器日常状态巡检、&#xff08;几乎包括了linux服务管理、linux 系统管理以及在docker 容器课程中涉及的所有内容&#xff09;服务架构的…

FPGA专项课程即将开课,颁发AMD官方证书

社区成立以来&#xff0c;一直致力于为广大工程师提供优质的技术培训和资源&#xff0c;得到了众多用户的喜爱与支持。为了满足用户需求&#xff0c;我们特别推出了“基于Vitis HLS的高层次综合及图像处理开发”课程。 本次课程旨在帮助企业工程师掌握前沿的FPGA技术&#xff…

谷歌工程师指责OpenAI阻碍AGI研究进展:推迟了5到10年

Google母公司Alphabet的一位软件工程师表示&#xff0c;OpenAI阻碍了人工通用智能&#xff08;AGI&#xff09;的发展5到10年。在最近的一次播客访谈中&#xff0c;Google软件工程师弗朗索瓦乔莱特&#xff08;Franois Chollet &#xff09;表达了他对AGI研究现状的担忧。这段对…