更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vdso.c 文件参考

http://lishiwen4.github.io/linux/vdso-and-syscall https://vvl.me/2019/06/linux-syscall-and-vsyscall-vdso-in-x86/ 更多...

浏览源代码.

函数

LITE_VDSO_DATAPAGE VdsoDataPage g_vdsoDataPage __attribute__ ((__used__))
 数据页提供内核映射给用户进程的内核时数据 更多...
 
UINT32 OsVdsoInit (VOID)
 vdso初始化 更多...
 
 LOS_MODULE_INIT (OsVdsoInit, LOS_INIT_LEVEL_KMOD_EXTENDED)
 
STATIC INT32 OsVdsoMap (LosVmSpace *space, size_t len, PADDR_T paddr, VADDR_T vaddr, UINT32 flag)
 OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区.
结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址. 更多...
 
vaddr_t OsVdsoLoad (const LosProcessCB *processCB)
 OsVdsoLoad 为指定进程加载vdso
本质是将系统镜像中的vsdo部分映射到进程空间 更多...
 
STATIC VOID LockVdsoDataPage (VdsoDataPage *vdsoDataPage)
 对数据页加锁 更多...
 
STATIC VOID UnlockVdsoDataPage (VdsoDataPage *vdsoDataPage)
 对数据页加锁 更多...
 
VOID OsVdsoTimevalUpdate (VOID)
 OsVdsoTimevalUpdate
更新时间,根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页; 更多...
 

变量

STATIC size_t g_vdsoSize
 

详细描述

http://lishiwen4.github.io/linux/vdso-and-syscall https://vvl.me/2019/06/linux-syscall-and-vsyscall-vdso-in-x86/

 基本概念:
     VDSO(Virtual Dynamic Shared Object,虚拟动态共享库)相对于普通的动态共享库,区别在于
     其so文件不保存在文件系统中,存在于系统镜像中,由内核在运行时确定并提供给应用程序,故称为虚拟动态共享库。
     OpenHarmony系统通过VDSO机制实现上层用户态程序可以快速读取内核相关数据的一种通道方法,
     可用于实现部分系统调用的加速,也可用于实现非系统敏感数据(硬件配置、软件配置)的快速读取。

     运行机制:
     VDSO其核心思想就是内核看护一段内存,并将这段内存映射(只读)进用户态应用程序的地址空间,
     应用程序通过链接vdso.so后,将某些系统调用替换为直接读取这段已映射的内存从而避免系统调用达到加速的效果。
     VDSO总体可分为数据页与代码页两部分:
      数据页提供内核映射给用户进程的内核时数据;
      代码页提供屏蔽系统调用的主要逻辑;

     如下图所示,当前VDSO机制有以下几个主要步骤:

    ① 内核初始化时进行VDSO数据页的创建;

    ② 内核初始化时进行VDSO代码页的创建;

    ③ 根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;

    ④ 用户进程创建时将代码页映射进用户空间;

    ⑤ 用户程序在动态链接时对VDSO的符号进行绑定;

    ⑥ 当用户程序进行特定系统调用时(例如clock_gettime(CLOCK_REALTIME_COARSE, &ts)),VDSO代码页会将其拦截;

    ⑦ VDSO代码页将正常系统调用转为直接读取映射好的VDSO数据页;

    ⑧ 从VDSO数据页中将数据传回VDSO代码页;

    ⑨ 将从VDSO数据页获取到的数据作为结果返回给用户程序;
注意
当前VDSO机制支持LibC库clock_gettime接口的CLOCK_REALTIME_COARSE与CLOCK_MONOTONIC_COARSE功能, clock_gettime接口的使用方法详见POSIX标准。用户调用C库接口clock_gettime(CLOCK_REALTIME_COARSE, &ts) 或者clock_gettime(CLOCK_MONOTONIC_COARSE, &ts)即可使用VDSO机制。 使用VDSO机制得到的时间精度会与系统tick中断的精度保持一致,适用于对时间没有高精度要求且短时间内会 高频触发clock_gettime或gettimeofday系统调用的场景,若有高精度要求,不建议采用VDSO机制。
版本
作者
weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
日期
2021-11-24

在文件 los_vdso.c 中定义.

函数说明

◆ __attribute__()

LITE_VDSO_DATAPAGE VdsoDataPage g_vdsoDataPage __attribute__ ( (__used__)  )

数据页提供内核映射给用户进程的内核时数据

◆ LockVdsoDataPage()

STATIC VOID LockVdsoDataPage ( VdsoDataPage vdsoDataPage)

对数据页加锁

在文件 los_vdso.c184 行定义.

185{
186 vdsoDataPage->lockCount = 1;
187 DMB;
188}
UINT64 lockCount
数据页被锁数量
这是这个函数的调用关系图:

◆ LOS_MODULE_INIT()

LOS_MODULE_INIT ( OsVdsoInit  ,
LOS_INIT_LEVEL_KMOD_EXTENDED   
)

◆ OsVdsoInit()

UINT32 OsVdsoInit ( VOID  )

vdso初始化

在文件 los_vdso.c96 行定义.

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}
char CHAR
Definition: los_typedef.h:63
STATIC size_t g_vdsoSize
Definition: los_vdso.c:94
CHAR __vdso_data_start
数据区起始地址 __vdso_data_start < __vdso_text_start
CHAR __vdso_text_end
代码区结束地址
CHAR __vdso_text_start
代码区起始地址
int memcmp(const void *str1, const void *str2, size_t n)
Definition: memcmp.c:37
函数调用图:

◆ OsVdsoLoad()

vaddr_t OsVdsoLoad ( const LosProcessCB processCB)

OsVdsoLoad 为指定进程加载vdso
本质是将系统镜像中的vsdo部分映射到进程空间

参数
processCB
返回
参见

在文件 los_vdso.c147 行定义.

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}
signed int INT32
Definition: los_typedef.h:60
unsigned int UINT32
Definition: los_typedef.h:57
STATIC INT32 OsVdsoMap(LosVmSpace *space, size_t len, PADDR_T paddr, VADDR_T vaddr, UINT32 flag)
OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区. 结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址.
Definition: los_vdso.c:122
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
LosVmSpace * vmSpace
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
LosMux regionMux
Definition: los_vm_map.h:149
函数调用图:
这是这个函数的调用关系图:

◆ OsVdsoMap()

STATIC INT32 OsVdsoMap ( LosVmSpace space,
size_t  len,
PADDR_T  paddr,
VADDR_T  vaddr,
UINT32  flag 
)

OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区.
结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址.

参数
flag
len映射长度
paddr物理地址
space进程空间
vaddr虚拟地址
返回
参见

在文件 los_vdso.c122 行定义.

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}
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
int STATUS_T
Definition: los_typedef.h:215
LosArchMmu archMmu
Definition: los_vm_map.h:157
函数调用图:
这是这个函数的调用关系图:

◆ OsVdsoTimevalUpdate()

VOID OsVdsoTimevalUpdate ( VOID  )

OsVdsoTimevalUpdate
更新时间,根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;

返回
参见
OsTickHandler 函数

在文件 los_vdso.c203 行定义.

204{
205 VdsoDataPage *kVdsoDataPage = (VdsoDataPage *)(&__vdso_data_start);//获取vdso 数据区
206
207 LockVdsoDataPage(kVdsoDataPage);//锁住数据页
208 OsVdsoTimeGet(kVdsoDataPage); //更新数据页时间
209 UnlockVdsoDataPage(kVdsoDataPage);//解锁数据页
210}
STATIC VOID LockVdsoDataPage(VdsoDataPage *vdsoDataPage)
对数据页加锁
Definition: los_vdso.c:184
STATIC VOID UnlockVdsoDataPage(VdsoDataPage *vdsoDataPage)
对数据页加锁
Definition: los_vdso.c:190
VOID OsVdsoTimeGet(VdsoDataPage *vdsoDataPage)
将最新的时间刷进数据页
Definition: time.c:1200
函数调用图:
这是这个函数的调用关系图:

◆ UnlockVdsoDataPage()

STATIC VOID UnlockVdsoDataPage ( VdsoDataPage vdsoDataPage)

对数据页加锁

在文件 los_vdso.c190 行定义.

191{
192 DMB;
193 vdsoDataPage->lockCount = 0;
194}
这是这个函数的调用关系图:

变量说明

◆ g_vdsoSize

STATIC size_t g_vdsoSize

虚拟动态共享库大小

在文件 los_vdso.c94 行定义.