江南才子 发表于 2021-6-28 16:22:49

鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 百篇博客分析HarmonyOS源码 | v56.02

  将 HarmonyOS | 鸿蒙 研究到底 < 国内 | 国外 >

  百篇博客系列篇.本篇为:

[*]v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的?| 51 .c .h .o
  可执行文件和共享目标文件(动态连接库)是程序的静态存储形式.要执行一个程序,系统要先把相应的可执行文件和动态连接库装载到进程空间中,这样形成一个可运行的进程的内存空间布局,也可以称它为"进程映像".
  本篇结合源码介绍鸿蒙加载和运行shell进程的整个过程,因本篇涉及代码较多,所以删减了一些不相干的代码. 鸿蒙加载和运行ELF的函数为 LOS_DoExecveFile
LOS_DoExecveFile
  根文件系统已经提供shell,fileName为 "/bin/shell"
//运行用户态进程 ELF格式,运行在内核态
INT32 LOS_DoExecveFile(const CHAR *fileName, CHAR * const *argv, CHAR * const *envp)
{
    ELFLoadInfo loadInfo = { 0 };
    CHAR kfileName = { 0 };//此时已陷入内核态,所以局部变量都在内核空间
    INT32 ret;
    loadInfo.newSpace = OsCreateUserVmSapce();//创建用户虚拟空间
    if (loadInfo.newSpace == NULL) {
      PRINT_ERR("%s %d, failed to allocate new vm space\n", __FUNCTION__, __LINE__);
      return -ENOMEM;
    }
    loadInfo.argv = argv;//参数数组
    loadInfo.envp = envp;//环境数组
    ret = OsLoadELFFile(&loadInfo);//加载ELF文件
    if (ret != LOS_OK) {
      return ret;
    }
//对当前进程旧虚拟空间和文件进行回收
    ret = OsExecRecycleAndInit(OsCurrProcessGet(), loadInfo.fileName, loadInfo.oldSpace, loadInfo.oldFiles);
    if (ret != LOS_OK) {
      (VOID)LOS_VmSpaceFree(loadInfo.oldSpace);//释放虚拟空间
      goto OUT;
    }
    ret = OsExecve(&loadInfo);//运行ELF内容
    if (ret != LOS_OK) {
      goto OUT;
    }
    return loadInfo.stackTop;
OUT:
    (VOID)LOS_Exit(OS_PRO_EXIT_OK);
    return ret;
}
  解读

[*]创建了一个新的用户进程空间,每个应用进程都有自己独立的进程空间,也称虚拟空间.这个空间和内核空间是隔离的,用户空间的虚拟地址范围为 0x00000000 ~ 0x3FFFFFFF,内核空间是0x3FFFFFFF ~ 0xFFFFFFFF
[*]加载ELF文件,注意 SysExecve -> LOS_DoExecveFile,而SysExecve是个系统调用,所以 LOS_DoExecveFile是运行在内核空间.加载过程由内核完成,包括申请的动态内存都是由内核空间提供.
[*]加载成功后,当前进程会被腾龙换鸟,把原有内脏挖空后留给新的shell使用,原用进程空间和文件都会被保存下来.
[*]运行shell,代码段,数据段装载完成后,设置好运行栈,运行就变得很简单,将用户栈保存到内核栈中,程序就会切到shell入口地址 0x1000 执行,正式开始了 shell 之旅
如何加载?
  ELF一体两面,面对不同的场景扮演不同的角色,这是理解ELF的关键,链接器只关注1(ELF头信息),3(区),4(区头表) 的内容,加载器只关注1(ELF头信息),2(段头表),3(段)的内容,本篇说加载过程,所以不会出现区(sections)这个概念. 先看shell 1,2,3(段)的内容,这些内容看过

[*]v53.xx 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银| 51 .c .h .o
[*]v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main| 51 .c .h .o
  的不会陌生,对照着代码去看很容易理解.
root@5e3abe332c5a:/home/harmony/out/hispark_aries/ipcamera_hispark_aries/bin# readelf -h shell
ELF Header:
Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class:                           ELF32
Data:                              2's complement, little endian
Version:                           1 (current)
OS/ABI:                            UNIX - System V
ABI Version:                     0
Type:                              DYN (Shared object file)
Machine:                           ARM
Version:                           0x1
Entry point address:               0x1000
Start of program headers:          52 (bytes into file)
Start of section headers:          25268 (bytes into file)
Flags:                           0x5000200, Version5 EABI, soft-float ABI
Size of this header:               52 (bytes)
Size of program headers:         32 (bytes)
Number of program headers:         11
Size of section headers:         40 (bytes)
Number of section headers:         27
Section header string table index: 26
root@5e3abe332c5a:/home/harmony/out/hispark_aries/ipcamera_hispark_aries/bin# readelf -l shell

Elf file type is DYN (Shared object file)
Entry point 0x1000
There are 11 program headers, starting at offset 52

Program Headers:
Type         Offset   VirtAddr   PhysAddr   FileSiz MemSizFlg Align
PHDR         0x000034 0x00000034 0x00000034 0x00160 0x00160 R   0x4   
INTERP         0x000194 0x00000194 0x00000194 0x00016 0x00016 R   0x1   
      
LOAD         0x000000 0x00000000 0x00000000 0x00e64 0x00e64 R   0x1000
LOAD         0x001000 0x00001000 0x00001000 0x03690 0x03690 R E 0x1000
LOAD         0x005000 0x00005000 0x00005000 0x001b8 0x001b8 RW0x1000
LOAD         0x006000 0x00006000 0x00006000 0x00034 0x00060 RW0x1000
DYNAMIC      0x005008 0x00005008 0x00005008 0x000c8 0x000c8 RW0x4   
GNU_RELRO      0x005000 0x00005000 0x00005000 0x001b8 0x01000 R   0x1   
GNU_EH_FRAME   0x000e54 0x00000e54 0x00000e54 0x0000c 0x0000c R   0x4   
GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW0   
EXIDX          0x000928 0x00000928 0x00000928 0x00010 0x00010 R   0x4   

Section to Segment mapping:
Segment Sections...
   00
   01   .interp
   02   .interp .dynsym .gnu.hash .hash .dynstr .rel.dyn .ARM.exidx .rel.plt .rodata .eh_frame_hdr .eh_frame
   03   .text .init .fini .plt
   04   .init_array .fini_array .dynamic .got .got.plt
   05   .data .bss
   06   .dynamic
   07   .init_array .fini_array .dynamic .got .got.plt .bss.rel.ro
   08   .eh_frame_hdr
   09
   10   .ARM.exidx
  解读

[*]INTERP 段,说明ELF需要加载另一个动态链接库 /lib/ld-musl-arm.so.1.
[*]GNU_STACK 段,指的就是栈,没有它内核无法构建栈,而且必须是RW
[*]LOAD 段,指加载段,即.bss,.data,.text都属于加载段,加载它们到指定位置就是加载器的工作,而ELF本身已经提供了指令/数据的相对位置.加载器只需提供一个加载开始地址就能计算出指令/数据在虚拟空间中的最终地址.
ELFLoadInfo
  理解ELFLoadInfo是理解鸿蒙加载ELF运行的关键.代码都已经注释.
typedef struct {//加载ELF信息结构体
    ELFInfo      execInfo;//可执行文件信息
    ELFInfo      interpInfo;//解析器文件信息 lib/libc.so
    const CHAR   *fileName;//文件名称
    CHAR         *execName;//程序名称
    INT32      argc;//参数个数
    INT32      envc;//环境变量个数
    CHAR *const*argv;//参数数组
    CHAR *const*envp;//环境变量数组
    UINTPTR      stackTop;//栈底位置,递减满栈下,stackTop是高地址位
    UINTPTR      stackTopMax;//栈最大上限
    UINTPTR      stackBase;//栈顶位置
    UINTPTR      stackParamBase;//栈参数空间,放置启动ELF时的外部参数,大小为 USER_PARAM_BYTE_MAX 4K
    UINT32       stackSize;//栈大小
    INT32      stackProt;//LD_PT_GNU_STACK栈的权限 ,例如(RW)
    UINTPTR      loadAddr;//加载地址
    UINTPTR      elfEntry;//装载点地址 即: _start 函数地址
    UINTPTR      topOfMem;//虚拟空间顶部位置,loadInfo->topOfMem = loadInfo->stackTopMax - sizeof(UINTPTR);
    UINTPTR      oldFiles;//旧空间的文件映像
    LosVmSpace   *newSpace;//新虚拟空间
    LosVmSpace   *oldSpace;//旧虚拟空间
#ifdef LOSCFG_ASLR
    INT32      randomDevFD;
#endif
} ELFLoadInfo;
  解读

[*]一个程序要运行需要两个必不可少的硬性条件.
   
[*]      
[*]指令在哪里,由 elfEntry,它是.text的开始位置,直接在 elf头中可以读到.   
   
[*]      
[*]拿到指令后在哪里运行,即栈在哪里,ELFLoadInfo有7个变量在描述栈信息.足以说明栈的重要性.栈的构建对应的是ELF的GNU_STACK段,权限必须是(R + W)   
   
   
[*]interpInfo对应的是ELF的INTERP段,不是所有的ELF都会有INTERP段,如下: INTERP         0x000194 0x00000194 0x00000194 0x00016 0x00016 R   0x1   
   
这个段的意思就是需要加载动态链接库,/lib/ld-musl-arm.so.1 是 libc.so的一个软链,具体位置在根文件系统 /rootfs/lib/libc.so 位置.
[*]argv,envc命令行参数和环境变量内核会专门开辟4K空间,保存在栈底位置,一起保存的还有ELF的辅助向量表auxVector.
[*]loadAddr 通过LOS_MMap将各 LOAD段并做好的虚拟地址和物理地址的映射关系保存在了映射区.
   
[*]从代码看对.bss区做了匿名映射,见于OsSetBss(),不清楚为何内核要区别对待.bss区.   
[*]其余各区做了文件映射.   

加载过程(OsLoadELFFile)
  源码位置: ..\kernel\extended\dynload\src\los_load_elf.c
//加载ELF格式文件
INT32 OsLoadELFFile(ELFLoadInfo *loadInfo)
{
    INT32 ret;
    OsLoadInit(loadInfo);//初始化加载信息
    ret = OsReadEhdr(loadInfo->fileName, &loadInfo->execInfo, TRUE);//读ELF头信息
    if (ret != LOS_OK) {
      goto OUT;
    }
    ret = OsReadPhdrs(&loadInfo->execInfo, TRUE);//读ELF程序头信息,构建进程映像所需信息.
    if (ret != LOS_OK) {
      goto OUT;
    }
    ret = OsReadInterpInfo(loadInfo);//读取段 INTERP 解析器信息
    if (ret != LOS_OK) {
      goto OUT;
    }
    ret = OsSetArgParams(loadInfo, loadInfo->argv, loadInfo->envp);//设置外部参数内容
    if (ret != LOS_OK) {
      goto OUT;
    }
    OsFlushAspace(loadInfo);//擦除空间
    ret = OsLoadELFSegment(loadInfo);//加载段信息
    if (ret != LOS_OK) {//加载失败时
      OsCurrProcessGet()->vmSpace = loadInfo->oldSpace;//切回原有虚拟空间
      LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu);//切回原有MMU
      goto OUT;
    }
    OsDeInitLoadInfo(loadInfo);//ELF和.so 加载完成后释放内存
    return LOS_OK;

OUT:
    OsDeInitFiles(loadInfo);
    (VOID)LOS_VmSpaceFree(loadInfo->newSpace);
    (VOID)OsDeInitLoadInfo(loadInfo);
    return ret;
}
  解读

[*]OsReadPhdrs读取程序头(段头),共11个段头.
[*]OsReadInterpInfo读取动态链接库 lib/libc.so段头信息.
[*]OsSetArgParams将外部参数(命令行和环境变量)保存在栈底位置
[*]OsFlushAspace切换进程空间,新进程空间重置堆区,映射区,MMU切换.映射区一旦变化意味着MMU的L1,L2表的变化.
[*]OsLoadELFSegment加载ELF .bss,.data,.text区,这些区统一叫 LOAD段,建立新的虚拟地址和物理地址映射关系 LOAD         0x000000 0x00000000 0x00000000 0x00e64 0x00e64 R   0x1000
LOAD         0x001000 0x00001000 0x00001000 0x03690 0x03690 R E 0x1000
LOAD         0x005000 0x00005000 0x00005000 0x001b8 0x001b8 RW0x1000
LOAD         0x006000 0x00006000 0x00006000 0x00034 0x00060 RW0x1000
四个加载段的内容对应以下各区,这些区都会加载到用户空间指定位置.
02   .interp .dynsym .gnu.hash .hash .dynstr .rel.dyn .ARM.exidx .rel.plt .rodata .eh_frame_hdr .eh_frame
03   .text .init .fini .plt
04   .init_array .fini_array .dynamic .got .got.plt
05   .data .bss

[*]经过以上操作, shell在虚拟内存中真实样子如下:
          内存映像    虚拟地址范围    大小    备注            stack 向下生长    USER_ASPACE_TOP_MAX ~ USER_MAP_SIZE + USER_MAP_BASE                  mmap 向上生长    USER_MAP_SIZE + USER_MAP_BASE ~ USER_MAP_BASE    USER_MAP_SIZE    USER_MAP_BASE = (USER_ASPACE_TOP_MAX >> 1)          heap 向上生长    USER_MAP_BASE ~ USER_HEAP_BASE      USER_HEAP_BASE = USER_ASPACE_TOP_MAX >> 2          .data .bss    0x06060 ~ 0x006000    0x00060            .init_array .fini_array .dynamic .got .got.plt    0x051b8 ~ 0x005000    0x001b8            .text .init .fini .plt    0x04690 ~ 0x001000    0x03690            .interp .dynsym .gnu.hash .hash .dynstr .rel.dyn .ARM.exidx .rel.plt .rodata .eh_frame_hdr .eh_frame    0x00e64 ~ 0x000000    0x00e64  但注意:其中不包含 /lib/libc.so的信息,动态链接部分会单独一篇去说明.

[*]用户地址空间在 mmap处 一切为二, 堆区独占1/4, 所有区(.bbs,.text,..)共占1/4,映射区和栈区共占1/2,二者相立而行,向中间靠拢.
如何运行?
  由 ..\kernel\extended\dynload\src\los_exec_elf.c 提供,很简单.
//运行ELF
STATIC INT32 OsExecve(const ELFLoadInfo *loadInfo)
{
    if ((loadInfo == NULL) || (loadInfo->elfEntry == 0)) {
      return LOS_NOK;
    }
//任务运行的两个硬性要求:1.提供入口指令 2.运行栈空间.
    return OsExecStart((TSK_ENTRY_FUNC)(loadInfo->elfEntry), (UINTPTR)loadInfo->stackTop,
                     loadInfo->stackBase, loadInfo->stackSize);
}

//执行用户态任务, entry为入口函数 ,其中 创建好task,task上下文 等待调度真正执行, sp:栈指针 mapBase:栈底 mapSize:栈大小
LITE_OS_SEC_TEXT UINT32 OsExecStart(const TSK_ENTRY_FUNC entry, UINTPTR sp, UINTPTR mapBase, UINT32 mapSize)
{
    UINT32 intSave;

    if (entry == NULL) {
      return LOS_NOK;
    }

    if ((sp == 0) || (LOS_Align(sp, LOSCFG_STACK_POINT_ALIGN_SIZE) != sp)) {//对齐
      return LOS_NOK;
    }
//注意 sp此时指向栈底,栈底地址要大于栈顶
    if ((mapBase == 0) || (mapSize == 0) || (sp <= mapBase) || (sp > (mapBase + mapSize))) {//参数检查
      return LOS_NOK;
    }

    LosTaskCB *taskCB = OsCurrTaskGet();//获取当前任务
    SCHEDULER_LOCK(intSave);//拿自旋锁

    taskCB->userMapBase = mapBase;//用户态栈顶位置
    taskCB->userMapSize = mapSize;//用户态栈
    taskCB->taskEntry = (TSK_ENTRY_FUNC)entry;//任务的入口函数
//初始化内核态栈
    TaskContext *taskContext = (TaskContext *)OsTaskStackInit(taskCB->taskID, taskCB->stackSize,
                                                            (VOID *)taskCB->topOfStack, FALSE);
    OsUserTaskStackInit(taskContext, (UINTPTR)taskCB->taskEntry, sp);//初始化用户栈,将内核栈中上下文的 context->R = sp ,context->sp = sp
    //这样做的目的是将用户栈SP保存到内核栈中,
    SCHEDULER_UNLOCK(intSave);//解锁
    return LOS_OK;
}
  解读

[*]运行shell出奇的简单,设置好执行指令的入口地址(PC)寄出器和栈指针(SP)就可以了,这些内容在系列篇中已经反复说过,请自行翻看.
[*]因shell为用户态进程,所以会有内核态和用户态两个栈,初始化内核栈 OsTaskStackInit 和用户栈 OsUserTaskStackInit过程在线程概念篇中也已有描述.
百篇博客.往期回顾
  在加注过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 :P
  与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。

[*]  v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的?| 51 .c .h .o

[*]  v55.xx 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人| 51 .c .h .o

[*]  v54.xx 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程| 51 .c .h .o

[*]  v53.xx 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银| 51 .c .h .o

[*]  v52.xx 鸿蒙内核源码分析(静态站点篇) | 五一哪也没去就干了这事| 51 .c .h .o

[*]  v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main| 51 .c .h .o

[*]  v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙看这篇或许真的够了| 51 .c .h.o

[*]  v49.xx 鸿蒙内核源码分析(信号消费篇) | 谁让CPU连续四次换栈运行| 51 .c .h.o

[*]  v48.xx 鸿蒙内核源码分析(信号生产篇) | 年过半百,依然活力十足| 51 .c .h.o

[*]  v47.xx 鸿蒙内核源码分析(进程回收篇) | 临终前如何向老祖宗托孤| 51 .c .h.o

[*]  v46.xx 鸿蒙内核源码分析(特殊进程篇) | 龙生龙凤生凤老鼠生儿会打洞| 51 .c .h.o

[*]  v45.xx 鸿蒙内核源码分析(Fork篇) | 一次调用,两次返回| 51 .c .h.o

[*]  v44.xx 鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断| 51 .c .h.o

[*]  v43.xx 鸿蒙内核源码分析(中断概念篇) | 海公公的日常工作| 51 .c .h.o

[*]  v42.xx 鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射 | 51 .c .h.o

[*]  v41.xx 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务| 51 .c .h.o

[*]  v40.xx 鸿蒙内核源码分析(汇编汇总篇) | 汇编可爱如邻家女孩| 51 .c .h.o

[*]  v39.xx 鸿蒙内核源码分析(异常接管篇) | 社会很单纯,复杂的是人| 51 .c .h.o

[*]  v38.xx 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器| 51 .c .h.o

[*]  v37.xx 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅| 51 .c .h.o

[*]  v36.xx 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆| 51 .c .h.o

[*]  v35.xx 鸿蒙内核源码分析(时间管理篇) | 谁是内核基本时间单位| 51 .c .h.o

[*]  v34.xx 鸿蒙内核源码分析(原子操作篇) | 谁在为原子操作保驾护航| 51 .c .h.o

[*]  v33.xx 鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据| 51 .c .h.o

[*]  v32.xx 鸿蒙内核源码分析(CPU篇) | 整个内核就是一个死循环| 51 .c .h.o

[*]  v31.xx 鸿蒙内核源码分析(定时器篇) | 哪个任务的优先级最高| 51 .c .h.o

[*]  v30.xx 鸿蒙内核源码分析(事件控制篇) | 任务间多对多的同步方案| 51 .c .h.o

[*]  v29.xx 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步| 51 .c .h.o

[*]  v28.xx 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽| 51 .c .h.o

[*]  v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁| 51 .c .h.o

[*]  v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞节牌坊| 51 .c .h.o

[*]  v25.xx 鸿蒙内核源码分析(并发并行篇) | 听过无数遍的两个概念| 51 .c .h.o

[*]  v24.xx 鸿蒙内核源码分析(进程概念篇) | 进程在管理哪些资源| 51 .c .h.o

[*]  v23.xx 鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数| 51 .c .h.o

[*]  v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班| 51 .c .h.o

[*]  v21.xx 鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU| 51 .c .h.o

[*]  v20.xx 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地由谁提供| 51 .c .h.o

[*]  v19.xx 鸿蒙内核源码分析(位图管理篇) | 谁能一分钱分两半花| 51 .c .h.o

[*]  v18.xx 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义| 51 .c .h.o

[*]  v17.xx 鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存| 51 .c .h.o

[*]  v16.xx 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么| 51 .c .h.o

[*]  v15.xx 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里| 51 .c .h.o

[*]  v14.xx 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础| 51 .c .h.o

[*]  v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功| 51 .c .h.o

[*]  v12.xx 鸿蒙内核源码分析(内存管理篇) | 虚拟内存全景图是怎样的| 51 .c .h.o

[*]  v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分配方式| 51 .c .h.o

[*]  v10.xx 鸿蒙内核源码分析(内存主奴篇) | 皇上和奴才如何相处| 51 .c .h.o

[*]  v09.xx 鸿蒙内核源码分析(调度故事篇) | 用故事说内核调度过程| 51 .c .h.o

[*]  v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析| 51 .c .h.o

[*]  v07.xx 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的| 51 .c .h.o

[*]  v06.xx 鸿蒙内核源码分析(调度队列篇) | 内核有多少个调度队列| 51 .c .h.o

[*]  v05.xx 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的| 51 .c .h.o

[*]  v04.xx 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元| 51 .c .h.o

[*]  v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁的贡献最大| 51 .c .h.o

[*]  v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源| 51 .c .h.o

[*]  v01.xx 鸿蒙内核源码分析(双向链表篇) | 谁是内核最重要结构体| 51 .c .h.o

关于 51 .c .h .o
  看系列篇文章会常看到 51 .c .h .o,希望这对大家阅读不会造成影响. 分别对应以下四个站点的首个字符,感谢这些站点一直以来对系列篇的支持和推荐,尤其是 oschina gitee ,很喜欢它的界面风格,简洁大方,让人感觉到开源的伟大!

[*]51cto
[*]csdn
[*]harmony
[*]oschina
  而巧合的是.c .h .o是C语言的头/源/目标文件,这就很有意思了,冥冥之中似有天数,将这四个宝贝以这种方式融合在一起. 51 .c .h .o , 我要CHO ,嗯嗯,hin 顺口 : )
百万汉字注解.百篇博客分析
  百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee | github | csdn | coding >
  百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto | csdn | harmony| osc >
关注不迷路.代码即人生

  热爱是所有的理由和答案 - turing
  原创不易,欢迎转载,但麻烦请注明出处.

页: [1]
查看完整版本: 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 百篇博客分析HarmonyOS源码 | v56.02