当前位置: 首页 > 科技观察

Harmonyos内核源码分析(编译总结)-鸿蒙的所有汇编代码都在这里

时间:2023-03-12 18:22:38 科技观察

更多内容请访问:https://harmonyos.51cto.com与华为官方共同打造的鸿蒙技术社区编译其实很可爱。大多数IT从业者一生都不必接触组件。听起来像是来自远古的远方呼唤。总感觉很远却又能听到声音。汇编真的是01110011,汇编指令基本和机器指令一一对应。●所谓内核就是为硬件提供驱动以及驱动之后对资源的有序管理。这里所说的资源是CPU(单核/多核)、内存、磁盘和I/O设备。层层封装,步步隐匿,到了应用层,不知道有没有汉化,不管魏晋。好就是好,但有句话说得好,其实没有好时候,只是有人替你负重。你真的不想吗?你知道别人是如何为你背负重担的吗?●语言层次越高越接近人的思维方式,语言层次越低越接近逻辑与非门的层次波动。汇编是逆着硬件飞的,想研究内核就绕不开汇编。我觉得神秘来自于无知,恐惧来自于不亲近。●其实,当你深入分析内核源码的时候,你会发现汇编其实是很可爱很容易的,比起c/c++/Java太容易了,实在是傻傻的简单。鸿蒙内核源码分析系列至少有五篇文章涉及编译,请自行阅读,但还是远远不够。我需要写十五篇文章才能吃透。现在才刚刚开始。本文将先梳理一下鸿蒙内核的所有汇编文件和通用描述文件的功能,然后一一剥离。如果你不剥离这些汇编,你就不会停止。汇编目录下鸿蒙的所有汇编文件如下:直接点击查看注释源码,有的网站会去掉链接,没有办法,可以直接去各大网站搜索《鸿蒙内核源码分析》"并找到源代码注释。●\arch\arm\arm\src◆startup启动相关reset_vector_mp.S多核CPU启动代码,大文件◆reset_vector_up.S单核CPU启动代码,大文件◆armv7a◆cache.S缓存相关的两个函数◆los_dispatch.S异常分发处理,大文件。◆los_hw_exc.S硬件异常相关,大File.S拷贝内核空间数据到用户空间hw_user_get.S拷贝用户空间数据src到内核空间dst//errno_t_arm_get_user(void*dst,constvoid*src,size_tdstTypeLen,size_tsrcTypeLen)FUNCTION(_arm_get_user)stmdbsp!,{r0,r1,r2,r3,lr}@四个参数入栈,保存LRcmpr2,#0@r2和0比较beq.Lget_user_return@equal跳转到Lget_user_return直接返回cmpr2,r3@r2和r3比较bne.Lget_user_err@不相等,说明函数返回错误cmpr2,#1@r2compareswith1bhi.Lget_user_half@if(dstTypeLen>1)跳转到Lget_user_half.Lget_user_byte:@copydatabybyte0:ldrbtr3,[r1],#0@r3=*r11:strbr3,[r0],#0@*r0=r3b.Lget_user_return.Lget_user_half:cmpr2,#2@r2和2比较bhi.Lget_user_word@if(dstTypeLen>2)Lget_user_word2:ldrhtr3,[r1],#0@done复制lastbyte3:strhr3,[r0],#0@完成最后一个字节的拷贝b.Lget_user_return.Lget_user_word:cmpr2,#4@r2与4比较bhi.Lget_user_err@if(dstTypeLen>4)跳转到Lget_user_err4:ldrtr3,[r1],#05:strr3,[r0],#0.Lget_user_return:@returnanchorldmiasp!,{r0,r1,r2,r3,lr}@保存的内容出栈,恢复各个寄存器值movr0,0@r0保存返回值到0bxlr@跳回到调用函数继续执行,_arm_get_user到此结束!.lget_user_err:ldmiasp!,{r0,r1,r2,r3,lr}@保存的内容出栈,恢复各寄存器movr0的值,#-14@r0保存返回值为-14bxlr@跳转回调用函数继续执行,_arm_get_user到此结束!.pushsection__exc_table,"a".long0c,.Lget_user_err.long1c,.Lget_user_err.long2c,.Lget_user_err.long3c,.Lget_user_err.long4c,.Lget_user_err.long5c,.Lget_user_err.popsectionhw_user_put.S复制内核空间数据src到用户空间dst//errno_t_arm_put_user(void*dst,constvoid*src,size_tdstTypeLen,size_tsrcTypeLen)FUNCTION(_arm_put_user)stmdbsp!,{r0,r1,r2,r3,lr}cmpr2,#0beq.Lget_user_returncmpr2,r3bne.Lget_user_errcmpr2,#1bhilf_half:.Lgetldrbr3,[r1],#01:strbtr3,[r0],#0b.Lget_user_return.Lget_user_half:cmpr2,#2bhi.Lget_user_word2:ldrhr3,[r1],#03:strhtr3,[r0],#0b。Lget_user_return.Lget_user_word:cmpr2,#4bhi.Lget_user_err4:ldrr3,[r1],#05:strtr3,[r0],#0.Lget_user_return:ldmiasp!,{r0,r1,r2,r3,lr}movr0,0bxlr.Lget_user_err:ldmiasp!,{r0,r1,r2,r3,lr}movr0,#-14bxlr.pushsection__exc_table,"a".long0c,.Lget_user_err.long1c,.Lget_user_err.long2c,.Lget_user_err.long3c,.Lget_user_err.long4c,.lget_user_err.long5c,.Lget_user_err.popsection解释编码完全一样,没有区别。这就好比左边有美女,左边有丑女陪你,都是1+1=2,算术加法的逻辑是一样的,没有变化。但它能给你同样的感觉吗?美丑的含义是上层赋予的。美丑在这里并不重要。都变成r0,r1,r2,r3了,就像我东哥一样,眼睛瞎了,分不清为什么用户空间和内核空间的数据要拷贝?这是一个经典的问题。看了网上的一些回答,没有错:内核不能信任任何用户空间指针,必须验证用户空间指针指向的数据。如果只做验证而不做复制,那么在后续的操作中,就会受到其他线程/线程随时可能修改用户空间数据的威胁。所以必须复印一份。内存系列里反复说过,每个用户进程都有自己独立的用户空间,但是这个用户空间是通过MMU映射出来的。因此在高频任务切换的过程中,原有的用户空间地址内容很容易被覆盖。举个例子:?用户A在万丛大酒店21号房有个美女,说要献给内核大师。哥们,如果kernel不直接把美女带回家,只是记录美女在万丛酒店21号房间,kernel马上去还好,万一因为其他事情耽误了怎么办??在延误的时间里,调度算法将万丛酒店的21号房间给了客户B。当然,酒店管理人员会在客户B使用之前将美女换成其他地方(这样当客户A返回酒店时,原来的东西怎么办?)当21号房间腾出来的时候,B不会知道原来的房间是A用的,里面有美女,更不会知道美女是献给内核的老板。因为B的业务需要,东石很可能会进到21号房。因为只记录了地址,直接去万丛酒店21号房抓人,被抓的是东施。这样就不会乱了。?所以需要复制,把美女带回家先找个地方锁起来就好了。reset_vector_mp.S和reset_vector_up.S鸿蒙开机代码根据CPU是多核还是单核分为两个独立的文件。●非对称多处理(Asymmetricmultiprocessing,AMP)每个CPU核心运行一个独立的操作系统或同一操作系统的独立实例(instantiation)。●对称多处理(SMP)操作系统的一个实例可以同时管理所有CPU内核,应用程序不绑定到某个内核。●混合多处理(Boundmultiprocessing,BMP)操作系统的一个实例可以同时管理所有的CPU内核,但每个应用程序都锁定在一个指定的内核上。up(unitprocessing)的意思,单CPU,虽然没有mp那么复杂,但是文件也很大,500行汇编,一段看不完,需要单独写一篇文章讲reset_vector这里只列出up情况下的启动代码reset_vector:@鸿蒙单核cpu启动代码/*dosomeearlycpusetup:i/dcachedisable,mmudisabled*/mrcp15,0,r0,c1,c0,0bicr0,#(1<<12)bicr0,#(1<<2|1<<0)mcrp15,0,r0,c1,c0,0/*r11:deltaofphysicaladdressandvirtualaddress*/adrr11,pa_va_offsetldrr0,[r11]subr11,r11,r0/*ifweneedtorelocatetoproperlocationornot*/adrr4,__exception_handlers/*r4:baseofloadaddress*/ldrr5,=SYS_MEM_BASE/*r5:baseofphysicaladdress*/subsr12,r4,r5/*r12:deltaofloadaddressandphysicaladdress*/beqreloc_img_to_bottom_done/*ifweloadimageatthebottomofphysicaladdress*//*weneedtorelocateimageatthebottomofphysicaladdress*/ldrrhandress=__lercateimageatthebottomofphysicaladdress7,除外r7:baseoflinkedaddress(orvmaddress)*/ldrr6,=__bss_start/*r6:endoflinkedaddress(orvmaddress)*/subr6,r7/*r6:deltaoflinkedaddress(orvmaddress)*/addr6,r4/*r6:endofloadaddress*/los_dispatch.S和los_hw_exc.S异常模式处理入口和统一调度现实,之前也提到过,很复杂,更多1000多行,后面会详细介绍实现过程。jmp.S两个简单longjmpsetjmp函数的实现,注释部分请到鸿蒙内核源码注释分析查看FUNCTION(longjmp)ldmfdr0,{r4-r12}addr0,#(4*9)ldrr13,[r0]addr0,#4ldrr14,[r0]cmpr1,#0moveqr1,#1movr0,r1movpc,lrFUNCTION(setjmp)stmear0,{r4-r12}addr0,#(4*9)strr13,[r0]addr0,#4strr14,[r0]movr0,#0movpc,lrlos_hw_runstop.S。globalOsSRSaveRegister.globalOsSRRestoreRegister这两个函数的组装现实有点复杂,后面会分别说明。cache.S这是缓存部分两个函数的实现。这里没有注释。试着理解这两个函数的实现。注解部分请到鸿蒙内核源码注解分析查看。macroDCACHE_LINE_SIZE,reg,tmpmrcp15,0,\tmp,c0,c0,1lsr\tmp,\tmp,#16and\tmp,\tmp,#0xfmov\reg,#4mov\reg,\reg,lsl\tmp.endmFUNCTION(arm_inv_cache_range)push{r2,r3}DCACHE_LINE_SIZEr2,r3subr3,r2,#1tstr0,r3bicr0,r0,r3mcrnep15,0,r0,c7,c14,1tstr1,r3bicr1,r1,r3mcrnep15,0,r1,c7,c14,11:mcrp15,0,r0,c7,c6,1addr0,r0,r2cmpr0,r1blo1bdsbpop{r2,r3}movpc,lrFUNCTION(arm_clean_cache_range)push{r2,r3}DCACHE_LINE_SIZer2,r3subr3,r2,#1bicr0,r0,r31:mcrp15,0,r0,c7,c10,1addr0,r0,r2cmpr0,r1blo1bdsbpop{r2,r3}movpc,lr参与贡献●访问评论仓库地址●fork本仓库>>新建一个Feat_xxx分支>>提交代码注释>>新建创建PullRequest●创建新Issue更多信息请访问:与华为官方共建的Harmonyos技术社区https://harmonyos.51cto.com