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

鸿蒙内核源码分析(系统调用)-图形系统调用

时间:2023-03-21 19:15:32 科技观察

更多信息请访问:与华为官方共建的Harmonyos技术社区https://harmonyos.51cto.com在阅读本文之前,本文将系统调用解释清楚。本文通过一张图片和七段代码详细介绍了系统调用的全过程。代码一直戳到最后,直到汇编层不能再继续下去。先看图,这里的mode可以理解为空间,因为mode不一样。堆栈空间不同。流程解读●在应用层main中使用系统调用mq_open(posix标准接口)●mq_open封装在库中,这里直接看库中的代码。●调用mq_open中的syscall并将参数传递给寄存器R7、R0~R6●SVC0完成从用户模式到内核模式(SVC)的切换●_osExceptSwiHdl运行在svc模式。●PC寄存器直接指向_osExceptSwiHdl取指令。●_osExceptSwiHdl为汇编代码,保存用户优先模式地址(R0~R12寄存器),调用OsArmA32SyscallHandle完成系统调用。OsArmA32SyscallHandle通过系统调用号(保存在R7寄存器中)查询对应的注册函数SYS_mq_open。SYS_mq_open就是这个系统调用的实现函数。完成后返回OsArmA32SyscallHandleOsArmA32SyscallHandle返回_osExceptSwiHdl_osExceptSwiHdl恢复用户态站点(R0~R12寄存器)从内核态(SVC)切换到用户态,PC寄存器也切换回用户态。流程中七段跟踪代码,一一分析1.Applicationmainintmain(void){charmqname[NAMESIZE],msgrv1[BUFFER],msgrv2[BUFFER];constchar*msgptr1="testmessage1";constchar*msgptr2="testmessage2withdifferentlength";mqd_tmqdes;intprio1=1,prio2=2;structtimespects;structmq_attrattr;intunresolved=0,failure=0;sprintf(mqname,"/"FUNCTION"_"TEST"_%d",getpid());attr.mq_msgsize=BUFFER;attr.mq_maxmsg=BUFFER;mqdes=mq_open(mqname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR,&attr);if(mqdes==(mqd_t)-1){perror(ERROR_PREFIX"mq_open");unresolved=1;}if(mq_send(mqdes,msgptr1,strlen(msgptr1),prio1)!=0){perror(ERROR_PREFIX"mq_send");unresolved=1;}printf("TestPASSED\n");returnPTS_PASS;}2.mq_open发起系统调用mqd_tmq_open(constchar*name,intflags,...){mode_tmode=0;structmq_attr*attr=0;if(*name=='/')name++;if(flags&O_CREAT){va_listap;va_start(ap,flags);mode=va_arg(ap,mode_t);attr=va_arg(ap,structmq_attr*);va_end(ap);}returnsyscall(SYS_mq_open,name,flags,mode,attr);}解释SYS_mq_open是一个真正的系统调用函数,对应一个系统调用号__NR_mq_open,通过宏SYSCALL_HAND_DEF.staticUINTPTRg_syscallHandle[SYS_CALL_NUM]=将SysMqOpen注册到g_syscallHandle{0};//系统调用入口函数注册staticUINT8g_syscallNArgs[(SYS_CALL_NUM+1)/NARG_PER_BYTE]={0};//保存系统调用对应的参数个数#defineSYSCALL_HAND_DEF(id,fun,rType,nArg)\if((id)>NARG_BITS):(nArgs&NARG_MASK);//获取参数个数if((handle==0)||(nArgs>ARG_NUM_7)){//系统调用必须有参数,参数不能大于8PRINT_ERR("UnsupportsyscallID:%dnArgs:%d\n",cmd,nArgs);regs[REG_R0]=-ENOSYS;returnregs;}//regs[0-6]记录系统调用的参数,这就是为什么系统调用号保存在R7寄存器switch(nArgs){//参数个数caseARG_NUM_0:caseARG_NUM_1:ret=(*(SyscallFun1)handle)(regs[REG_R0]);//执行系统调用,类似SysUnlink(pathname);休息;caseARG_NUM_2://@note_thinking怎么一个系统调用有两个参数,这里确实是三个参数,任务栈会发生什么?caseARG_NUM_3:ret=(*(SyscallFun3)handle)(regs[REG_R0],regs[REG_R1],regs[REG_R2]);//类似SysExecve(fileName,argv,envp);break;caseARG_NUM_4:caseARG_NUM_5:ret=(*(SyscallFun5)handle)(regs[REG_R0],regs[REG_R1],regs[REG_R2],regs[REG_R3],regs[REG_R4]);break;default://ret=(*(SyscallFun7)handle)(regs[REG_R0],regs[REG_R1],regs[REG_R2],regs[REG_R3],regs[REG_R4],regs[REG_R5],regs[REG_R6]);}regs[REG_R0]=ret;//R0保存系统调用返回ValueOsSaveSignalContext(regs);//保存用户堆栈场景/*返回current_regs的最后一个值。这支持从异常返回时进行上下文切换。*该功能仅与SYS_context_switch系统调用一起使用。*/returnregs;//返回寄存器的值}解释●参数为R0~Rn对应的regs●R7保存系统调用号,R0~R3保存SysMqOpen的四个参数●g_syscallHandle[cmd]可以查询SYSCALL_HAND_DEF(__NR_mq_open,SysMqOpen,mqd_t,ARG_NUM_4)对应注册时的SysMqOpen函数●*(SyscallFun5)handle此时为SysMqOpen●注意初始main函数中SysMqOpen的参数为mqdes=mq_open(mqname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR,&属性);这样就完成了真正系统调用的过程(kMqName,mqName,PATH_MAX);if(retValue<0){returnretValue;}ret=mq_open(kMqName,openFlag,mode,attr);//一个消息队列可以有多个进程来if(ret==-1){return(mqd_t)-get_errno();}returnret;}解释●这里main函数的mq_open和mq_open其实是两个函数体实现。一个是给应用层调用的,一个是给内核层使用的,只是同名而已。●SysMqOpen返回到OsArmA32SyscallHandleregs[REG_R0]=ret;●OsArmA32SyscallHandle返回到_osExceptSwiHdl●_osExceptSwiHdl后面的代码用于恢复用户态场景和SPSR、PC等寄存器。以上就是鸿蒙系统调用的全过程。关于各模式下寄存器(R0~R15)的使用,寄存器章节有详细说明,请自行查看。更多信息请访问:和华为官方Harmonyos技术社区共建https://harmonyos.51cto.com