更多内容请访问:鸿蒙技术社区与华为官方共建https://harmonyos.51cto.com有两个LiteOS-M内核LibC实现,你可以根据自己的需要选择两者之一,即musllibC和newlibc。本文首先学习NewlibC的实现代码,本文涉及的源码可以从开源网站https://gitee.com/openharmony/kernel_liteos_m获取。在使用MuslC库时,内核提供了一个posix接口(//kernel/liteos_m/kal/posix),实现了基于LOS_XXX适配的pthread、mqeue、fs、semaphore、time等模块。内核提供的posix接口和musl中的标准C库接口共同构成了LiteOS-M的LibC。编译时使用arm-none-eabi-gcc,但只使用其工具链的编译功能,并通过添加-nostdinc和-nostdlib强制使用我们自己修改的musl-C。社区和第三方厂商大多使用公版工具链arm-none-eabi-gcc加上私有定制优化进行编译。LiteOS-M内核还支持公版arm-none-eabi-gccC库编译运行内核。newlib是一个小型C库。对于涉及系统调用的posix接口部分,newlib提供了一些需要系统适配的hook函数,如_exit()、_open()、_close()、_gettimeofday()等,由操作系统适配这些hook,您可以使用公共版本的newlib工具链来编译和运行程序。1、NewlibC文件系统使用NewlibC且支持POSIXFSAPI时(可在kernel\liteos-m\目录下执行makemeuconfig弹出配置界面,路径为Compat-Chooselibcimplementation),如图在下图中。可以使用文件kal\libc\newlib\porting\src\fs.c中定义的文件系统操作接口。这些是标准的POSIX接口。如果想了解POSIX的用法,可以在linux平台输入man-a函数名,比如man-aopendir打开函数的手册。1.1函数mount、umount、umount2的用法与muslc部分一致。intmount(constchar*source,constchar*target,constchar*filesystemtype,unsignedlongmountflags,constvoid*data){returnLOS_FsMount(source,target,filesystemtype,mountflags,data);}intumount(constchar*target){returnLOS_FsUmount(target);}intumount2(constchar*target,intflag){returnLOS_FsUmount2(target,flag);}1.2文件操作接口下划线开头的函数实现是newlibc的hook函数实现。下面具体分析newlib的钩子函数调用过程。int_open(constchar*path,intoflag,...){va_listvaList;va_start(vaList,oflag);intret;ret=LOS_Open(path,oflag);va_end(vaList);returnret;}int_close(intfd){returnLOS_Close(fd);}ssize_t_read(intfd,void*buf,size_tnbyte){returnLOS_Read(fd,buf,nbyte);}ssize_t_write(intfd,constvoid*buf,size_tnbyte){returnLOS_Write(fd,buf,nbyte);}off_t_lseek(intfd,off_toffset,intwhence){returnLOS_Lseek(fd,offset,whence);}int_unlink(constchar*path){returnLOS_Unlink(path);}int_fstat(intfd,structstat*buf){returnLOS_Fstat(fd,buf);}int_stat(constchar*path,structstat*buf){returnLOS_Stat(path,buf);}intfsync(intfd){returnLOS_Fsync(fd);}intmkdir(constchar*path,mode_tmode){returnLOS_Mkdir(path,mode);}DIR*opendir(constchar*dirName){returnLOS_Opendir(dirName);}structdirent*readdir(DIR*dir){returnLOS_Readdir(dir);}intclosedir(DIR*dir){returnLOS_Closedir(dir);}intrmdir(constchar*path){returnLOS_Unlink(path);}intrename(constchar*oldName,constchar*newName){returnLOS_Rename(oldName,newName);}intstatfs(constchar*path,structstatfs*buf){returnLOS_Statfs(path,buf);}intftruncate(intfd,off_tlength){returnLOS_Ftruncate(fd,length);}当newlib没有开启支持POSIXFSAPI时,需要提供了这些钩子函数的空实现可以返回-1错误码size_tnbyte){return-1;}ssize_t_write(intfd,constvoid*buf,size_tnbyte){return-1;}off_t_lseek(intfd,off_toffset,intwhence){return-1;}int_unlink(constchar*path){return-1;}int_fstat(intfd,structstat*buf){return-1;}int_stat(constchar*path,structstat*buf){return-1;}2.newlibC内存分配释放newlibcmalloc适配参考TheRedHatnewlibCLibrary-malloc.实现malloc适配有两种方式:实现_sbrk_r函数。在这个方法中,内存分配函数使用了newlib中的那个。实现_malloc_r、_realloc_r、_free_r、_memalign_r、_malloc_usable_size_r等。在这种方法中,内存分配函数可以使用内核的。为了方便根据业务进行内存分配算法调优和问题定位,推荐后者。内核的内存函数定义在文件kal\libc\newlib\porting\src\malloc.c中。源码片段如下,代码实现比较简单,不再分析源码。......void__wrap__free_r(struct_reent*reent,void*aptr){if(aptr==NULL){return;}LOS_MemFree(OS_SYS_MEM_ADDR,aptr);}size_t__wrap__malloc_usable_size_r(struct_reent*reent,void*aptr){return0;}void*__wrap__malloc_r(struct_reent*reent,size_tnbytes){if(nbytes==0){returnNULL;}returnLOS_MemAlloc(OS_SYS_MEM_ADDR,nbytes);}void*__wrap__memalign_r(struct_reent*reent,size_talign,size_tnbytes)==s({returnNULL;}returnLOS_MemAllocAlign(OS_SYS_MEM_ADDR,nbytes,align);}...你可能注意到函数名是由__wrap_加上钩子函数名组成的,这是因为这些函数的符号已经存在于newlib中,所以需要使用gccwraplink选项用内核的实现替换这些函数符号,在设备开发板的配置文件中,例如//device/board/fnlink/v200zr/liteos_m/config.gni,为这些添加wraplink选项函数,示例如下:board_ld_flags+=["-Wl,--wrap=_malloc_r","-Wl,--wrap=_realloc_r","-Wl,--wrap=_free_r","-Wl,--wrap=_memalign_r","-Wl,--wrap=_malloc_usable_size_r",]3.newlib钩子函数介绍以open函数的钩子函数_open为例介绍钩子调用进程newlib的函数。open()函数在newlib-cygwin\newlib\libc\syscalls\sysopen.c中实现,会进一步调用函数_open_r,这是一个可重入函数ReentrantFunction,支持多线程运行。inopen(constchar*file,intflags,...){va_listap;intret;va_start(ap,flags);ret=_open_r(_REENT,file,flags,va_arg(ap,int));va_end(ap);returnret;}所有可重入函数都定义在文件夹newlib-cygwin\newlib\libc\reent中,函数_open_r定义在该文件夹下的文件newlib-cygwin\newlib\libc\reent\openr.c中。函数代码如下:int_open_r(struct_reent*ptr,constchar*file,intflags,intmode){intret;errno=0;if((ret=_open(file,flags,mode))==-1&&errno!=0)ptr->_errno=errno;returnret;}函数_open_r,如上代码所示,会进一步调用函数_open。该功能以arm硬件平台为例,在newlib-cygwin\libgloss\arm\syscalls.c文件中实现。newlib目录是与硬件平台无关的功能实现,libloss目录是底层驱动实现,以各个硬件平台为文件夹组织起来。在具体硬件平台目录下的syscalls.c文件中,实现了newlib需要的存根函数:/*Forwardprototypes.*/int_system(constchar*);int_rename(constchar*,constchar*);int_isatty(int);clock_t_times(structtms*);int_gettimeofday(structtimeval*,void*);int_unlink(constchar*);int_link(constchar*,constchar*);int_stat(constchar*,structstat*);int_fstat(int,structstat*);int_swistat(intfd,structstat*st);void*_sbrk(ptrdiff_t);pid_t_getpid(void);int_close(int);clock_t_clock(void);int_swiclose(int);int_open(constchar*,int,...);int_swiopen(constchar*,int);int_write(int,constvoid*,size_t);int_swiwrite(int,constvoid*,size_t);_off_t_lseek(int,_off_t,int);_off_t_swilseek(int,_off_t,int);int_read(int,void*,size_t);int_swireread(int,void*,size_t);voidinitialise_monitor_handles(void);上面提到的函数_open,源码如下。后续的分析不再继续,LiteOS-M内核会提供这些钩子函数的实现。int_open(constchar*path,intflags,...){return_swiopen(path,flags);}总结本文学习了LiteOS-M内核NewlibC的实现,特别是文件系统和内存分配释放,最后介绍了Newlib挂钩函数。更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区
