往期知识点记录:
- 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
- 轻内核M核源码分析系列一 数据结构-双向循环链表
- 轻内核M核源码分析系列二 数据结构-任务就绪队列
- 鸿蒙轻内核M核源码分析系列三 数据结构-任务排序链表
- 轻内核M核源码分析系列四 中断Hwi
- 轻内核M核源码分析系列五 时间管理
- 轻内核M核源码分析系列六 任务及任务调度(1)任务栈
- 轻内核M核源码分析系列六 任务及任务调度(2)任务模块
- 轻内核M核源码分析系列六 任务及任务调度(3)任务调度模块
- 轻内核M核源码分析系列七 动态内存Dynamic Memory
- 轻内核M核源码分析系列八 静态内存MemoryBox
- 轻内核M核源码分析系列九 互斥锁Mutex
- 轻内核M核源码分析系列十 软件定时器Swtmr
- 轻内核M核源码分析系列十一 (1)信号量Semaphore
- 轻内核M核源码分析系列十一 (2)信号量Semaphore
- 轻内核M核源码分析系列十二 事件Event
- 轻内核M核源码分析系列十三 消息队列Queue
- 轻内核M核源码分析系列十四 软件定时器Swtmr
- 轻内核M核源码分析系列十五 CPU使用率CPUP
- 轻内核M核源码分析系列十六 MPU内存保护单元
- 轻内核M核源码分析系列十七(1) 异常钩子函数类型介绍
- 轻内核M核源码分析系列十七(2) 异常钩子函数的注册操作
- 轻内核M核源码分析系列十七(3) 异常信息ExcInfo
- 轻内核M核源码分析系列十八 Fault异常处理
- 轻内核M核源码分析系列十九 Musl LibC
- 轻内核M核源码分析系列二十 Newlib C
- 持续更新中……
LiteOS-M内核LibC实现有2种,可以根据需求进行二选一,分别是musl libC和newlibc。本文先学习下Musl LibC的实现代码。文中所涉及的源码,均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_m 获取。LiteOS-M内核提供了和内核相关的文件系统、内存申请释放接口,其他接口可以直接使用Musl提供的。我们分别来看下内核提供的接口部分。
1、Musl LibC文件系统
在使用Musl LibC并且使能支持POSIX FS API时,可以使用文件kal\libc\musl\fs.c
中定义的文件系统操作接口。这些是标准的POSIX接口,如果想了解其用法,可以参考 Section 2: system calls 。可以在网页上搜索,也可以直接把上述网址和函数名称进行拼接,如对于mount()
函数,可以直接访问 https://linux.die.net/man/2/mount 。opendir等部分函数需要在 Section 3: library functions 网页上查看。下文快速记录下各个函数的使用方法。
1.1 函数mount
函数mount会挂载source参数(通常是设备名称,也可以是目录)指定的文件系统到target参数指定的目录。文件系统类型LiteOS-M内核支持"fat"和"littlefs"两种类型。"littlefs"文件系统不需要挂载选项参数mountflags。对于fat文件类型,挂载选项参数定义在文件third_party\musl\porting\liteos_m\kernel\include\sys\mount.h中,如MS_RDONLY、MS_NOSUID、MS_REMOUNT等等。参数data由文件系统进行解析,fat文件类型不需要该参数;"littlefs"文件系统需要传入的data参数应该为 (struct lfs_config*)指针类型。
该函数会调用components\fs\vfs\los_fs.c
中的函数LOS_FsMount,后文会专门讲解FS VFS。
int mount(const char *source, const char *target,const char *filesystemtype, unsigned long mountflags,const void *data)
{return LOS_FsMount(source, target, filesystemtype, mountflags, data);
}
1.2 函数umount和umount2
函数umount, umount2用于unmount卸载文件系统。参数target指定要卸载的文件系统。函数umount2除了卸载,还可以指定flag参数来控制卸载行为。支持的参数定义在third_party\musl\porting\liteos_m\kernel\include\sys\mount.h,如MNT_FORCE、MNT_DETACH、MNT_EXPIRE和UMOUNT_NOFOLLOW。
int umount(const char *target)
{return LOS_FsUmount(target);
}int umount2(const char *target, int flag)
{return LOS_FsUmount2(target, flag);
}
1.3 函数open、close和unlink
函数open用于打开一个文件或设备,可能会先创建文件或设备。参数path指定文件或设备的路径,参数oflag需要使用下面的访问模式O_RDONLY, O_WRONLY, O_RDWR中的一个,这几个定义在文件third_party\musl\porting\liteos_m\kernel\include\fcntl.h。third_party\musl\porting\liteos_m\kernel\include\bits\fcntl.h。另外,还有些其他文件创建标签或文件状态标签可以通过逻辑与进行指定。文件创建标签有O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TRUNC和O_TTY_INIT。其余的为文件状态标签,这些标签定义文件中third_party\musl\porting\liteos_m\kernel\include\bits\fcntl.h中。可以访问https://linux.die.net/man/2/open了解这些标签的详细用法。
函数open返回值为文件描述符file descriptor,会被其他函数如read, write, lseek, fcntl等使用。函数close用于关闭一个文件描述符,使fd不再引用任何文件,可被再次重用。函数unlink用于删除path路径指定的文件。
int open(const char *path, int oflag, ...)
{va_list vaList;va_start(vaList, oflag);int ret;ret = LOS_Open(path, oflag, vaList);va_end(vaList);return ret;
}int close(int fd)
{return LOS_Close(fd);
}int unlink(const char *path)
{return LOS_Unlink(path);
}
1.4 函数read和write
函数read尝试从fd中读取nbyte字节的数据到buf开始的缓存里,读取成功时返回读取的字节数目。函数write把buf处开始的nbyte字节数据写入fd引用的文件里,写入成功时返回实际写入的字节数目。
ssize_t read(int fd, void *buf, size_t nbyte)
{return LOS_Read(fd, buf, nbyte);
}ssize_t write(int fd, const void *buf, size_t nbyte)
{return LOS_Write(fd, buf, nbyte);
}
1.5 函数lseek
函数lseek用于重新定位文件读写的偏移位置。参数whence取值为SEEK_SET、SEEK_CUR或SEEK_END,定义在文件third_party\musl\porting\liteos_m\kernel\include\fcntl.h。
- SEEK_SET
偏移设置在offset字节处。 - SEEK_CUR
偏移设置在当前位置加上offset字节处。 - SEEK_END
偏移设置在文件大小加上offset字节处。
函数执行成功时,返回值为从文件开头的偏移字节数值。
off_t lseek(int fd, off_t offset, int whence)
{return LOS_Lseek(fd, offset, whence);
}}
1.6 函数fstat、stat和statfs
函数fstat和stat用于获取文件的状态state,参数参数分别是文件描述符和文件路径。参数中的struct stat
结构体定义在文件third_party\musl\porting\liteos_m\kernel\include\bits\stat.h中。
函数statfs返回文件系统统计statistics数据,结构体struct statfs定义在文件third_party\musl\porting\liteos_m\kernel\include\bits\statfs.h中。
int fstat(int fd, struct stat *buf)
{return LOS_Fstat(fd, buf);
}int stat(const char *path, struct stat *buf)
{return LOS_Stat(path, buf);
}
int statfs(const char *path, struct statfs *buf)
{return LOS_Statfs(path, buf);
}
1.7 函数mkdir、opendir、readir、closedir和rmdrir
函数mkdir用于创建一个目录,目录名称由参数path指定。参数mode指定目录权限。创建成功返回0,否则返回-1。
函数opendir用于打开一个目录流a directory stream,目录名称由参数dirName指定,返回一个执行目录刘的指针。发生错误时,返回NULL,并设置errno。返回值类型DIR是struct __dirstream的别名,定义在文件中third_party\musl\porting\liteos_m\kernel\include\dirent.h。可以访问https://linux.die.net/man/3/opendir了解更多关于该函数的信息。
函数readdir用于读取一个目录,返回一个struct dirent结构体指针,代表目录流DIR *dir中的下一个目录条目directory entry。到达目录流尾部或错误时,返回NULL。结构体定义在文件third_party\musl\porting\liteos_m\kernel\include\bits\dirent.h中。 可以访问https://linux.die.net/man/3/readdir了解更多关于该函数的信息。
函数closedir用于关闭一个目录。函数rmdir用于删除一个目录,只有空目录才会被删除。
int mkdir(const char *path, mode_t mode)
{return LOS_Mkdir(path, mode);
}DIR *opendir(const char *dirName)
{return LOS_Opendir(dirName);
}struct dirent *readdir(DIR *dir)
{return LOS_Readdir(dir);
}int closedir(DIR *dir)
{return LOS_Closedir(dir);
}int rmdir(const char *path)
{return LOS_Unlink(path);
}
1.8 函数fsync
函数mkdir用于同步内存中所有已修改的文件数据到储存设备。可以访问https://linux.die.net/man/3/fsync了解更多关于该函数的信息。
int fsync(int fd)
{return LOS_Fsync(fd);
}
1.9 函数rename
函数rename用于重命名一个文件。可以访问https://linux.die.net/man/3/rename了解更多关于该函数的信息。
int rename(const char *oldName, const char *newName)
{return LOS_Rename(oldName, newName);
}
1.10 函数ftruncate
函数ftruncate用于截断一个文件到指定的长度。可以访问https://linux.die.net/man/3/ftruncate了解更多关于该函数的信息。
int ftruncate(int fd, off_t length)
{return LOS_Ftruncate(fd, length);
}
2、Musl LibC内存分配释放
LiteOS-M内核提供了内存分配释放函数。这些是标准的POSIX接口,如果想了解其用法,可以参考 Section 3: library functions 。可以在网页上搜索,也可以直接把上述网址和函数名称进行拼接,如对于malloc()
函数,可以直接访问 https://linux.die.net/man/3/malloc 。opendir等部分函数需要在网页上查看。下文快速记录下各个函数的使用方法。
1.1 函数malloc、free和memalign
函数malloc和free分别调用内核内存模块的接口来实现内存申请和释放。函数memalign可以以指定的内存对齐大小来申请内存。
void free(void *ptr)
{if (ptr == NULL) {return;}LOS_MemFree(OS_SYS_MEM_ADDR, ptr);
}void *malloc(size_t size)
{if (size == 0) {return NULL;}return LOS_MemAlloc(OS_SYS_MEM_ADDR, size);
}
void *memalign(size_t boundary, size_t size)
{if (size == 0) {return NULL;}return LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, boundary);
}
1.2 函数malloc、free和memalign
函数calloc在内存的动态存储区中分配nitems个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
函数zalloc和malloc的区别是,申请成功后,对申请的内存区域置0。函数realloc用于重新申请一块内存区域。
void *calloc(size_t nitems, size_t size)
{size_t real_size;void *ptr = NULL;if (nitems == 0 || size == 0) {return NULL;}real_size = (size_t)(nitems * size);ptr = LOS_MemAlloc(OS_SYS_MEM_ADDR, real_size);if (ptr != NULL) {(void)memset_s(ptr, real_size, 0, real_size);}return ptr;
}
void *zalloc(size_t size)
{void *ptr = NULL;if (size == 0) {return NULL;}ptr = LOS_MemAlloc(OS_SYS_MEM_ADDR, size);if (ptr != NULL) {(void)memset_s(ptr, size, 0, size);}return ptr;
}void *realloc(void *ptr, size_t size)
{if (ptr == NULL) {return malloc(size);}if (size == 0) {free(ptr);return NULL;}return LOS_MemRealloc(OS_SYS_MEM_ADDR, ptr, size);
}
小结
本文学习了LiteOS-M内核Musl LibC的实现,特别是文件系统和内存分配释放部分。时间仓促和能力关系,如有失误,欢迎指正。
经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?
为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview
OpenHarmony 开发环境搭建
《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……
OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview