提交 74c739d8 编写于 作者: 鸿蒙内核源码分析's avatar 鸿蒙内核源码分析

对链接脚本注解

    百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
    鸿蒙研究站 | http://weharmonyos.com (国内)
              | https://weharmony.github.io (国外)
    oschina | https://my.oschina.net/weharmony
    博客园 | https://www.cnblogs.com/weharmony/
    知乎 | https://www.zhihu.com/people/weharmonyos
    csdn | https://blog.csdn.net/kuangyufei
    51cto | https://harmonyos.51cto.com/column/34
    掘金 | https://juejin.cn/user/756888642000808
    公众号 | 鸿蒙研究站 (weharmonyos)
上级 30f7789e
......@@ -34,6 +34,18 @@
标志寄存器为1时,继续循环,重新进行原子操作。
有多个任务对同一个内存数据进行加减或交换等操作时,使用原子操作保证结果的可预知性。
volatile关键字在用C语言编写嵌入式软件里面用得很多,不使用volatile关键字的代码比使用volatile关键字的代码效率要高一些,
但就无法保证数据的一致性。volatile的本意是告诉编译器,此变量的值是易变的,每次读写该变量的值时务必从该变量的内存地址中读取或写入,
不能为了效率使用对一个“临时”变量的读写来代替对该变量的直接读写。编译器看到了volatile关键字,就一定会生成内存访问指令,
每次读写该变量就一定会执行内存访问指令直接读写该变量。若是没有volatile关键字,编译器为了效率,
只会在循环开始前使用读内存指令将该变量读到寄存器中,之后在循环内都是用寄存器访问指令来操作这个“临时”变量,
在循环结束后再使用内存写指令将这个寄存器中的“临时”变量写回内存。在这个过程中,如果内存中的这个变量被别的因素
(其他线程、中断函数、信号处理函数、DMA控制器、其他硬件设备)所改变了,就产生数据不一致的问题。另外,
寄存器访问指令的速度要比内存访问指令的速度快,这里说的内存也包括缓存,也就是说内存访问指令实际上也有可能访问的是缓存里的数据,
但即便如此,还是不如访问寄存器快的。缓存对于编译器也是透明的,编译器使用内存读写指令时只会认为是在读写内存,
内存和缓存间的数据同步由CPU保证。
@endverbatim
* @attention 原子操作中,操作数及其结果不能超过函数所支持位数的最大值。目前原子操作接口只支持整型数据。
* @version
......
......@@ -349,8 +349,8 @@ cpu_start: /* CPU 启动 */
bl mmu_setup /* 安装MMU */
#endif
bl HalSecondaryCpuStart
b . //注意 b . 就会跳转到当前地址,相当于死循环
bl HalSecondaryCpuStart //跳到次核CPU执行
b . //注意 b . 指在当前地址运行,相当于死循环
secondary_cpu_init: /* 次级CPU初始化 */
#ifdef LOSCFG_TEE_ENABLE
......
......@@ -57,7 +57,7 @@ typedef struct ArchMmuInitMapping {
const char *name;///< 名称
} LosArchMmuInitMapping;
extern LosArchMmuInitMapping g_archMmuInitMapping[];
extern LosArchMmuInitMapping g_archMmuInitMapping[];//映射关系表
extern UINTPTR g_vmBootMemBase;
extern BOOL g_kHeapInited;
......
/*!
* @file los_vdso.c
* @brief
* @link http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-bundles-share.html
http://lishiwen4.github.io/linux/vdso-and-syscall
https://vvl.me/2019/06/linux-syscall-and-vsyscall-vdso-in-x86/
* @endlink
* @verbatim
基本概念:
VDSO(Virtual Dynamic Shared Object,虚拟动态共享库)相对于普通的动态共享库,区别在于
其so文件不保存在文件系统中,存在于系统镜像中,由内核在运行时确定并提供给应用程序,故称为虚拟动态共享库。
OpenHarmony系统通过VDSO机制实现上层用户态程序可以快速读取内核相关数据的一种通道方法,
可用于实现部分系统调用的加速,也可用于实现非系统敏感数据(硬件配置、软件配置)的快速读取。
运行机制:
VDSO其核心思想就是内核看护一段内存,并将这段内存映射(只读)进用户态应用程序的地址空间,
应用程序通过链接vdso.so后,将某些系统调用替换为直接读取这段已映射的内存从而避免系统调用达到加速的效果。
VDSO总体可分为数据页与代码页两部分:
数据页提供内核映射给用户进程的内核时数据;
代码页提供屏蔽系统调用的主要逻辑;
如下图所示,当前VDSO机制有以下几个主要步骤:
/*!
* @file los_vdso.c
* @brief
* @link http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-small-bundles-share.html
http://lishiwen4.github.io/linux/vdso-and-syscall
https://vvl.me/2019/06/linux-syscall-and-vsyscall-vdso-in-x86/
* @endlink
* @verbatim
基本概念:
VDSO(Virtual Dynamic Shared Object,虚拟动态共享库)相对于普通的动态共享库,区别在于
其so文件不保存在文件系统中,存在于系统镜像中,由内核在运行时确定并提供给应用程序,故称为虚拟动态共享库。
OpenHarmony系统通过VDSO机制实现上层用户态程序可以快速读取内核相关数据的一种通道方法,
可用于实现部分系统调用的加速,也可用于实现非系统敏感数据(硬件配置、软件配置)的快速读取。
运行机制:
VDSO其核心思想就是内核看护一段内存,并将这段内存映射(只读)进用户态应用程序的地址空间,
应用程序通过链接vdso.so后,将某些系统调用替换为直接读取这段已映射的内存从而避免系统调用达到加速的效果。
VDSO总体可分为数据页与代码页两部分:
数据页提供内核映射给用户进程的内核时数据;
代码页提供屏蔽系统调用的主要逻辑;
如下图所示,当前VDSO机制有以下几个主要步骤:
① 内核初始化时进行VDSO数据页的创建;
......@@ -37,174 +37,174 @@
⑧ 从VDSO数据页中将数据传回VDSO代码页;
⑨ 将从VDSO数据页获取到的数据作为结果返回给用户程序;
@endverbatim
* @image html https://gitee.com/weharmonyos/resources/raw/master/82/vdso.jpg
* @attention 当前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机制。
* @version
* @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
* @date 2021-11-24
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vdso_pri.h"
#include "los_vdso_datapage.h"
#include "los_init.h"
#include "los_vm_map.h"
#include "los_vm_lock.h"
#include "los_vm_phys.h"
#include "los_process_pri.h"
LITE_VDSO_DATAPAGE VdsoDataPage g_vdsoDataPage __attribute__((__used__));///< 数据页提供内核映射给用户进程的内核时数据
STATIC size_t g_vdsoSize; ///< 虚拟动态共享库大小
/// vdso初始化
UINT32 OsVdsoInit(VOID)
{//so文件不保存在文件系统中,而是存在于系统镜像中,镜像中的位置在代码区和数据区中间
g_vdsoSize = &__vdso_text_end - &__vdso_data_start;//VDSO 会映射两块内存区域:代码段 [vdso],只读数据区 [vvar] 数据页
//先检查这是不是一个 ELF文件
if (memcmp((CHAR *)(&__vdso_text_start), ELF_HEAD, ELF_HEAD_LEN)) {//.incbin OHOS_VDSO_SO 将原封不动的一个二进制文件编译到当前文件中
PRINT_ERR("VDSO Init Failed!\n");
return LOS_NOK;
}
return LOS_OK;
}
LOS_MODULE_INIT(OsVdsoInit, LOS_INIT_LEVEL_KMOD_EXTENDED);//注册vdso模块
/*!
* @brief OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区.
* \n 结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址.
*
* @param flag
* @param len 映射长度
* @param paddr 物理地址
* @param space 进程空间
* @param vaddr 虚拟地址
* @return
*
* @see
*/
STATIC INT32 OsVdsoMap(LosVmSpace *space, size_t len, PADDR_T paddr, VADDR_T vaddr, UINT32 flag)
{
STATUS_T ret;
while (len > 0) {
ret = LOS_ArchMmuMap(&(space->archMmu), vaddr, paddr, 1, flag);//建立虚实映射关系
if (ret != 1) {
PRINT_ERR("VDSO Load Failed! : LOS_ArchMmuMap!\n");
return LOS_NOK;
}
paddr += PAGE_SIZE;
vaddr += PAGE_SIZE;
len -= PAGE_SIZE;
}
return LOS_OK;
}
/*!
* @brief OsVdsoLoad 为指定进程加载vdso
* 本质是将系统镜像中的vsdo部分映射到进程空间
* @param processCB
* @return
*
* @see
*/
vaddr_t OsVdsoLoad(const LosProcessCB *processCB)
{
INT32 ret = -1;
LosVmMapRegion *vdsoRegion = NULL;
UINT32 flag = VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_EXECUTE; //用户空间区,可读,可执行,但不可写
//用户区,可读可执行
if ((processCB == NULL) || (processCB->vmSpace == NULL)) {
return 0;
}
(VOID)LOS_MuxAcquire(&processCB->vmSpace->regionMux);
//由虚拟空间提供加载内存
vdsoRegion = LOS_RegionAlloc(processCB->vmSpace, 0, g_vdsoSize, flag, 0);//申请进程空间 g_vdsoSize 大小线性区,用于映射
if (vdsoRegion == NULL) {
PRINT_ERR("%s %d, region alloc failed in vdso load\n", __FUNCTION__, __LINE__);
goto LOCK_RELEASE;
}
vdsoRegion->regionFlags |= VM_MAP_REGION_FLAG_VDSO;//标识为 vdso区
//为vsdo做好映射,如此通过访问进程的虚拟地址就可以访问到真正的vsdo
ret = OsVdsoMap(processCB->vmSpace, g_vdsoSize, LOS_PaddrQuery((VOID *)(&__vdso_data_start)),
vdsoRegion->range.base, flag);//__vdso_data_start 为 vdso 的起始地址
if (ret != LOS_OK) {//映射失败就释放线性区
ret = LOS_RegionFree(processCB->vmSpace, vdsoRegion);
if (ret) {
PRINT_ERR("%s %d, free region failed, ret = %d\n", __FUNCTION__, __LINE__, ret);
}
ret = -1;
}
LOCK_RELEASE:
(VOID)LOS_MuxRelease(&processCB->vmSpace->regionMux);
if (ret == LOS_OK) {
return (vdsoRegion->range.base + PAGE_SIZE);
}
return 0;
}
/// 对数据页加锁
STATIC VOID LockVdsoDataPage(VdsoDataPage *vdsoDataPage)
{
vdsoDataPage->lockCount = 1;
DMB;
}
/// 对数据页加锁
STATIC VOID UnlockVdsoDataPage(VdsoDataPage *vdsoDataPage)
{
DMB;
vdsoDataPage->lockCount = 0;
}
/*!
* @brief OsVdsoTimevalUpdate
* 更新时间,根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;
* @return
*
* @see OsTickHandler 函数
*/
VOID OsVdsoTimevalUpdate(VOID)
{
VdsoDataPage *kVdsoDataPage = (VdsoDataPage *)(&__vdso_data_start);//获取vdso 数据区
LockVdsoDataPage(kVdsoDataPage);//锁住数据页
OsVdsoTimeGet(kVdsoDataPage); //更新数据页时间
UnlockVdsoDataPage(kVdsoDataPage);//解锁数据页
}
⑨ 将从VDSO数据页获取到的数据作为结果返回给用户程序;
@endverbatim
* @image html https://gitee.com/weharmonyos/resources/raw/master/82/vdso.jpg
* @attention 当前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机制。
* @version
* @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
* @date 2021-11-24
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vdso_pri.h"
#include "los_vdso_datapage.h"
#include "los_init.h"
#include "los_vm_map.h"
#include "los_vm_lock.h"
#include "los_vm_phys.h"
#include "los_process_pri.h"
LITE_VDSO_DATAPAGE VdsoDataPage g_vdsoDataPage __attribute__((__used__));///< 数据页提供内核映射给用户进程的内核时数据
STATIC size_t g_vdsoSize; ///< 虚拟动态共享库大小
/// vdso初始化
UINT32 OsVdsoInit(VOID)
{//so文件不保存在文件系统中,而是存在于系统镜像中,镜像中的位置在代码区和数据区中间
g_vdsoSize = &__vdso_text_end - &__vdso_data_start;//VDSO 会映射两块内存区域:代码段 [vdso],只读数据区 [vvar] 数据页
//先检查这是不是一个 ELF文件
if (memcmp((CHAR *)(&__vdso_text_start), ELF_HEAD, ELF_HEAD_LEN)) {//.incbin OHOS_VDSO_SO 将原封不动的一个二进制文件编译到当前文件中
PRINT_ERR("VDSO Init Failed!\n");
return LOS_NOK;
}
return LOS_OK;
}
LOS_MODULE_INIT(OsVdsoInit, LOS_INIT_LEVEL_KMOD_EXTENDED);//注册vdso模块
/*!
* @brief OsVdsoMap 映射,这里先通过内核地址找到 vdso的物理地址,再将物理地址映射到进程的线性区.
* \n 结论是每个进程都可以拥有自己的 vdso区,映射到同一个块物理地址.
*
* @param flag
* @param len 映射长度
* @param paddr 物理地址
* @param space 进程空间
* @param vaddr 虚拟地址
* @return
*
* @see
*/
STATIC INT32 OsVdsoMap(LosVmSpace *space, size_t len, PADDR_T paddr, VADDR_T vaddr, UINT32 flag)
{
STATUS_T ret;
while (len > 0) {
ret = LOS_ArchMmuMap(&(space->archMmu), vaddr, paddr, 1, flag);//建立虚实映射关系
if (ret != 1) {
PRINT_ERR("VDSO Load Failed! : LOS_ArchMmuMap!\n");
return LOS_NOK;
}
paddr += PAGE_SIZE;
vaddr += PAGE_SIZE;
len -= PAGE_SIZE;
}
return LOS_OK;
}
/*!
* @brief OsVdsoLoad 为指定进程加载vdso
* 本质是将系统镜像中的vsdo部分映射到进程空间
* @param processCB
* @return
*
* @see
*/
vaddr_t OsVdsoLoad(const LosProcessCB *processCB)
{
INT32 ret = -1;
LosVmMapRegion *vdsoRegion = NULL;
UINT32 flag = VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_EXECUTE; //用户空间区,可读,可执行,但不可写
//用户区,可读可执行
if ((processCB == NULL) || (processCB->vmSpace == NULL)) {
return 0;
}
(VOID)LOS_MuxAcquire(&processCB->vmSpace->regionMux);
//由虚拟空间提供加载内存
vdsoRegion = LOS_RegionAlloc(processCB->vmSpace, 0, g_vdsoSize, flag, 0);//申请进程空间 g_vdsoSize 大小线性区,用于映射
if (vdsoRegion == NULL) {
PRINT_ERR("%s %d, region alloc failed in vdso load\n", __FUNCTION__, __LINE__);
goto LOCK_RELEASE;
}
vdsoRegion->regionFlags |= VM_MAP_REGION_FLAG_VDSO;//标识为 vdso区
//为vsdo做好映射,如此通过访问进程的虚拟地址就可以访问到真正的vsdo
ret = OsVdsoMap(processCB->vmSpace, g_vdsoSize, LOS_PaddrQuery((VOID *)(&__vdso_data_start)),
vdsoRegion->range.base, flag);//__vdso_data_start 为 vdso 的起始地址
if (ret != LOS_OK) {//映射失败就释放线性区
ret = LOS_RegionFree(processCB->vmSpace, vdsoRegion);
if (ret) {
PRINT_ERR("%s %d, free region failed, ret = %d\n", __FUNCTION__, __LINE__, ret);
}
ret = -1;
}
LOCK_RELEASE:
(VOID)LOS_MuxRelease(&processCB->vmSpace->regionMux);
if (ret == LOS_OK) {
return (vdsoRegion->range.base + PAGE_SIZE);
}
return 0;
}
/// 对数据页加锁
STATIC VOID LockVdsoDataPage(VdsoDataPage *vdsoDataPage)
{
vdsoDataPage->lockCount = 1;
DMB;
}
/// 对数据页加锁
STATIC VOID UnlockVdsoDataPage(VdsoDataPage *vdsoDataPage)
{
DMB;
vdsoDataPage->lockCount = 0;
}
/*!
* @brief OsVdsoTimevalUpdate
* 更新时间,根据系统时钟中断不断将内核一些数据刷新进VDSO的数据页;
* @return
*
* @see OsTickHandler 函数
*/
VOID OsVdsoTimevalUpdate(VOID)
{
VdsoDataPage *kVdsoDataPage = (VdsoDataPage *)(&__vdso_data_start);//获取vdso 数据区
LockVdsoDataPage(kVdsoDataPage);//锁住数据页
OsVdsoTimeGet(kVdsoDataPage); //更新数据页时间
UnlockVdsoDataPage(kVdsoDataPage);//解锁数据页
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册