更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vdso.c
浏览该文件的文档.
1/*!
2 * @file los_vdso.c
3 * @brief
4 * @link http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-bundles-share.html
5 http://lishiwen4.github.io/linux/vdso-and-syscall
6 https://vvl.me/2019/06/linux-syscall-and-vsyscall-vdso-in-x86/
7 * @endlink
8 * @verbatim
9 基本概念:
10 VDSO(Virtual Dynamic Shared Object,虚拟动态共享库)相对于普通的动态共享库,区别在于
11 其so文件不保存在文件系统中,存在于系统镜像中,由内核在运行时确定并提供给应用程序,故称为虚拟动态共享库。
12 OpenHarmony系统通过VDSO机制实现上层用户态程序可以快速读取内核相关数据的一种通道方法,
13 可用于实现部分系统调用的加速,也可用于实现非系统敏感数据(硬件配置、软件配置)的快速读取。
14
15 运行机制:
16 VDSO其核心思想就是内核看护一段内存,并将这段内存映射(只读)进用户态应用程序的地址空间,
17 应用程序通过链接vdso.so后,将某些系统调用替换为直接读取这段已映射的内存从而避免系统调用达到加速的效果。
18 VDSO总体可分为数据页与代码页两部分:
19 数据页提供内核映射给用户进程的内核时数据;
20 代码页提供屏蔽系统调用的主要逻辑;
21
22 如下图所示,当前VDSO机制有以下几个主要步骤:
23
24 ① 内核初始化时进行VDSO数据页的创建;
25
26 ② 内核初始化时进行VDSO代码页的创建;
27
28 ③ 根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;
29
30 ④ 用户进程创建时将代码页映射进用户空间;
31
32 ⑤ 用户程序在动态链接时对VDSO的符号进行绑定;
33
34 ⑥ 当用户程序进行特定系统调用时(例如clock_gettime(CLOCK_REALTIME_COARSE, &ts)),VDSO代码页会将其拦截;
35
36 ⑦ VDSO代码页将正常系统调用转为直接读取映射好的VDSO数据页;
37
38 ⑧ 从VDSO数据页中将数据传回VDSO代码页;
39
40 ⑨ 将从VDSO数据页获取到的数据作为结果返回给用户程序;
41 @endverbatim
42 * @image html https://gitee.com/weharmonyos/resources/raw/master/82/vdso.jpg
43 * @attention 当前VDSO机制支持LibC库clock_gettime接口的CLOCK_REALTIME_COARSE与CLOCK_MONOTONIC_COARSE功能,
44 clock_gettime接口的使用方法详见POSIX标准。用户调用C库接口clock_gettime(CLOCK_REALTIME_COARSE, &ts)
45 或者clock_gettime(CLOCK_MONOTONIC_COARSE, &ts)即可使用VDSO机制。
46 使用VDSO机制得到的时间精度会与系统tick中断的精度保持一致,适用于对时间没有高精度要求且短时间内会
47 高频触发clock_gettime或gettimeofday系统调用的场景,若有高精度要求,不建议采用VDSO机制。
48 * @version
49 * @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
50 * @date 2021-11-24
51 */
52/*
53 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
54 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
55 *
56 * Redistribution and use in source and binary forms, with or without modification,
57 * are permitted provided that the following conditions are met:
58 *
59 * 1. Redistributions of source code must retain the above copyright notice, this list of
60 * conditions and the following disclaimer.
61 *
62 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
63 * of conditions and the following disclaimer in the documentation and/or other materials
64 * provided with the distribution.
65 *
66 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
67 * to endorse or promote products derived from this software without specific prior written
68 * permission.
69 *
70 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
71 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
72 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
73 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
74 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
75 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
76 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
77 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
78 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
79 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
80 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
81 */
82
83#include "los_vdso_pri.h"
84#include "los_vdso_datapage.h"
85#include "los_init.h"
86#include "los_vm_map.h"
87#include "los_vm_lock.h"
88#include "los_vm_phys.h"
89#include "los_process_pri.h"
90
91
92LITE_VDSO_DATAPAGE VdsoDataPage g_vdsoDataPage __attribute__((__used__));///< 数据页提供内核映射给用户进程的内核时数据
93
94STATIC size_t g_vdsoSize; ///< 虚拟动态共享库大小
95/// vdso初始化
97{//so文件不保存在文件系统中,而是存在于系统镜像中,镜像中的位置在代码区和数据区中间
98 g_vdsoSize = &__vdso_text_end - &__vdso_data_start;//VDSO 会映射两块内存区域:代码段 [vdso],只读数据区 [vvar] 数据页
99 //先检查这是不是一个 ELF文件
100 if (memcmp((CHAR *)(&__vdso_text_start), ELF_HEAD, ELF_HEAD_LEN)) {//.incbin OHOS_VDSO_SO 将原封不动的一个二进制文件编译到当前文件中
101 PRINT_ERR("VDSO Init Failed!\n");
102 return LOS_NOK;
103 }
104 return LOS_OK;
105}
106
107LOS_MODULE_INIT(OsVdsoInit, LOS_INIT_LEVEL_KMOD_EXTENDED);//注册vdso模块
108
109/*!
110 * @brief OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区.
111 * \n 结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址.
112 *
113 * @param flag
114 * @param len 映射长度
115 * @param paddr 物理地址
116 * @param space 进程空间
117 * @param vaddr 虚拟地址
118 * @return
119 *
120 * @see
121 */
122STATIC INT32 OsVdsoMap(LosVmSpace *space, size_t len, PADDR_T paddr, VADDR_T vaddr, UINT32 flag)
123{
124 STATUS_T ret;
125
126 while (len > 0) {
127 ret = LOS_ArchMmuMap(&(space->archMmu), vaddr, paddr, 1, flag);//建立虚实映射关系
128 if (ret != 1) {
129 PRINT_ERR("VDSO Load Failed! : LOS_ArchMmuMap!\n");
130 return LOS_NOK;
131 }
132 paddr += PAGE_SIZE;
133 vaddr += PAGE_SIZE;
134 len -= PAGE_SIZE;
135 }
136 return LOS_OK;
137}
138
139/*!
140 * @brief OsVdsoLoad 为指定进程加载vdso
141 * 本质是将系统镜像中的vsdo部分映射到进程空间
142 * @param processCB
143 * @return
144 *
145 * @see
146 */
148{
149 INT32 ret = -1;
150 LosVmMapRegion *vdsoRegion = NULL;
151 UINT32 flag = VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_EXECUTE; //用户空间区,可读,可执行,但不可写
152 //用户区,可读可执行
153 if ((processCB == NULL) || (processCB->vmSpace == NULL)) {
154 return 0;
155 }
156
157 (VOID)LOS_MuxAcquire(&processCB->vmSpace->regionMux);
158 //由虚拟空间提供加载内存
159 vdsoRegion = LOS_RegionAlloc(processCB->vmSpace, 0, g_vdsoSize, flag, 0);//申请进程空间 g_vdsoSize 大小线性区,用于映射
160 if (vdsoRegion == NULL) {
161 PRINT_ERR("%s %d, region alloc failed in vdso load\n", __FUNCTION__, __LINE__);
162 goto LOCK_RELEASE;
163 }
164 vdsoRegion->regionFlags |= VM_MAP_REGION_FLAG_VDSO;//标识为 vdso区
165 //为vsdo做好映射,如此通过访问进程的虚拟地址就可以访问到真正的vsdo
166 ret = OsVdsoMap(processCB->vmSpace, g_vdsoSize, LOS_PaddrQuery((VOID *)(&__vdso_data_start)),
167 vdsoRegion->range.base, flag);//__vdso_data_start 为 vdso 的起始地址
168 if (ret != LOS_OK) {//映射失败就释放线性区
169 ret = LOS_RegionFree(processCB->vmSpace, vdsoRegion);
170 if (ret) {
171 PRINT_ERR("%s %d, free region failed, ret = %d\n", __FUNCTION__, __LINE__, ret);
172 }
173 ret = -1;
174 }
175
176LOCK_RELEASE:
177 (VOID)LOS_MuxRelease(&processCB->vmSpace->regionMux);
178 if (ret == LOS_OK) {
179 return (vdsoRegion->range.base + PAGE_SIZE);
180 }
181 return 0;
182}
183/// 对数据页加锁
184STATIC VOID LockVdsoDataPage(VdsoDataPage *vdsoDataPage)
185{
186 vdsoDataPage->lockCount = 1;
187 DMB;
188}
189/// 对数据页加锁
190STATIC VOID UnlockVdsoDataPage(VdsoDataPage *vdsoDataPage)
191{
192 DMB;
193 vdsoDataPage->lockCount = 0;
194}
195
196/*!
197 * @brief OsVdsoTimevalUpdate
198 * 更新时间,根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;
199 * @return
200 *
201 * @see OsTickHandler 函数
202 */
204{
205 VdsoDataPage *kVdsoDataPage = (VdsoDataPage *)(&__vdso_data_start);//获取vdso 数据区
206
207 LockVdsoDataPage(kVdsoDataPage);//锁住数据页
208 OsVdsoTimeGet(kVdsoDataPage); //更新数据页时间
209 UnlockVdsoDataPage(kVdsoDataPage);//解锁数据页
210}
STATUS_T LOS_ArchMmuMap(LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr, size_t count, UINT32 flags)
LOS_ArchMmuMap 映射进程空间虚拟地址区间与物理地址区间 所谓的map就是生成L1,L2页表项的过程
Definition: los_arch_mmu.c:891
unsigned long PADDR_T
Definition: los_typedef.h:207
signed int INT32
Definition: los_typedef.h:60
unsigned long vaddr_t
Definition: los_typedef.h:206
unsigned long VADDR_T
Definition: los_typedef.h:208
int STATUS_T
Definition: los_typedef.h:215
unsigned int UINT32
Definition: los_typedef.h:57
char CHAR
Definition: los_typedef.h:63
STATIC INT32 OsVdsoMap(LosVmSpace *space, size_t len, PADDR_T paddr, VADDR_T vaddr, UINT32 flag)
OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区. 结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址.
Definition: los_vdso.c:122
UINT32 OsVdsoInit(VOID)
vdso初始化
Definition: los_vdso.c:96
VOID OsVdsoTimevalUpdate(VOID)
OsVdsoTimevalUpdate 更新时间,根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;
Definition: los_vdso.c:203
vaddr_t OsVdsoLoad(const LosProcessCB *processCB)
OsVdsoLoad 为指定进程加载vdso 本质是将系统镜像中的vsdo部分映射到进程空间
Definition: los_vdso.c:147
LITE_VDSO_DATAPAGE VdsoDataPage g_vdsoDataPage __attribute__((__used__))
数据页提供内核映射给用户进程的内核时数据
STATIC size_t g_vdsoSize
Definition: los_vdso.c:94
LOS_MODULE_INIT(OsVdsoInit, LOS_INIT_LEVEL_KMOD_EXTENDED)
STATIC VOID LockVdsoDataPage(VdsoDataPage *vdsoDataPage)
对数据页加锁
Definition: los_vdso.c:184
STATIC VOID UnlockVdsoDataPage(VdsoDataPage *vdsoDataPage)
对数据页加锁
Definition: los_vdso.c:190
CHAR __vdso_data_start
数据区起始地址 __vdso_data_start < __vdso_text_start
CHAR __vdso_text_end
代码区结束地址
CHAR __vdso_text_start
代码区起始地址
STATIC INLINE STATUS_T LOS_MuxAcquire(LosMux *m)
Definition: los_vm_lock.h:48
STATIC INLINE STATUS_T LOS_MuxRelease(LosMux *m)
Definition: los_vm_lock.h:53
PADDR_T LOS_PaddrQuery(VOID *vaddr)
通过虚拟地址查询映射的物理地址
Definition: los_vm_map.c:550
STATUS_T LOS_RegionFree(LosVmSpace *space, LosVmMapRegion *region)
释放进程空间指定线性区
Definition: los_vm_map.c:694
LosVmMapRegion * LOS_RegionAlloc(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags, VM_OFFSET_T pgoff)
Definition: los_vm_map.c:581
int memcmp(const void *str1, const void *str2, size_t n)
Definition: memcmp.c:37
LosVmSpace * vmSpace
UINT64 lockCount
数据页被锁数量
VADDR_T base
Definition: los_vm_map.h:84
UINT32 regionFlags
Definition: los_vm_map.h:125
LosVmMapRange range
Definition: los_vm_map.h:123
虚拟空间,每个进程都有一个属于自己的虚拟内存地址空间
Definition: los_vm_map.h:146
LosMux regionMux
Definition: los_vm_map.h:149
LosArchMmu archMmu
Definition: los_vm_map.h:157
VOID OsVdsoTimeGet(VdsoDataPage *vdsoDataPage)
将最新的时间刷进数据页
Definition: time.c:1200