OpenHarmony设备开发 轻量系统内核(LiteOS-M)附录
版本:V3.2Beta
内核编码规范
此规范基于业界通用的编程规范整理而成,请内核的开发人员遵守这样的编程风格。
总体原则
总体原则:
- 清晰:代码应当易于理解、易于维护、易于重构,避免晦涩语法
- 简洁:命名简短,函数紧凑
- 高效:通过使用算法、编译器优化选项或硬件资源提高程序效率
- 美观:代码风格合理、一致
在大部分情况下,开发人员应当遵从以下规范,但也有一些例外场景。如修改第三方开源代码或大量使用开源代码接口下,应当与开源代码保持一致。请依据总体原则,灵活处理。
目录结构
建议按照功能模块划分子目录,子目录再定义头文件和源文件目录。
目录名和文件名如果没有特殊的需要,采用全小写的形式,可以使用下划线(“_”)分割。
命名
推荐使用驼峰风格,具体规则如下:
类型 | 命名风格 | 形式 |
函数、结构体类型、枚举类型、联合体类型、typedef的类型 | 大驼峰,或带有模块前缀的大驼峰 | AaaBbb XXX_AaaBbb |
局部变量,函数参数,宏参数,结构体中字段,联合体中成员 | 小驼峰 | aaaBBB |
全局变量 | 带“g_”前缀的小驼峰 | g_aaaBBB |
宏(不含函数式宏),枚举值,goto标签 | 全大写,下划线分割 | AAA_BBB |
函数式宏 | 全大写下划线分割,或大驼峰,或带有模块前缀的大驼峰 | AAA_BBB AaaBbb XXX_AaaBbb |
头文件防止重复的符号 | 以下划线“_”开头以H结尾,中间为文件名的全大写并以下划线分割 | _AAA_H |
内核对外API建议采用LOS_ModuleFunc的形式,如果有宾语则建议采用前置的方式,比如:
LOS_TaskCreate
LOS_MuxLock
kernel目录下内部模块间的接口使用OsModuleFunc的形式,比如:
OsTaskScan
OsMuxInit
注释
一般的,尽量通过清晰的软件架构,良好的符号命名来提高代码可读性;在需要的时候,辅以注释说明。
注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。
注释内容要简洁、明了、无歧义,信息全面且不冗余。
文件头部要进行注释,建议注释列出:版权说明、文件功能说明,作者、创建日期、注意事项等。
注释风格要统一,建议优先选择/* */的方式,注释符与注释内容之间要有1空格,单行、多行注释风格如下:
/* 单行注释 */
// 单行注释
/*
* 多行注释
* 第二行
*/
// 多行注释
// 另一行
针对代码的注释,应该置于对应代码的上方或右方。
代码上方的注释,与代码行间无空行,保持与代码一样的缩进。
代码右边的注释,与代码之间,至少留1空格。
建议将多条连续的右侧注释对齐,比如:
#define CONST_A 100 /* Const A */
#define CONST_B 2000 /* Const B */
格式
程序采用缩进风格编写,使用空格而不是制表符(’\t’)进行缩进,每级缩进为4个空格。
换行时,函数左大括号另起一行放行首,并独占一行;其他左大括号跟随语句放行末。右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的else/else if,或者逗号、分号。
一行只写一条语句。
比如:
struct MyType { // 跟随语句放行末,前置1空格
...
}; // 右大括号后面紧跟分号
int Foo(int a) { // 函数左大括号独占一行,放行首
if (a > 0) {
Foo(); // 一行只有一条语句
Bar();
} else { // 右大括号、"else"、以及后续的左大括号均在同一行
...
} // 右大括号独占一行
...
}
每行字符数不要超过 120 个,代码过长时应当换行,换行时将操作符留在行末,新行缩进一层或进行同类对齐,并将表示未结束的操作符或连接符号留在行末。
// 假设下面第一行已经不满足行宽要求
if (currentValue > MIN && // Good:换行后,布尔操作符放在行末
currentValue < MAX) { // Good: 与(&&)操作符的两个操作数同类对齐
DoSomething();
...
}
flashPara.flashEndAddr = flashPara.flashBaseAddr + // Good: 加号留在行末
flashPara.flashSize; // Good: 加法两个操作数对齐
// Good:函数参数放在一行
ReturnType result = FunctionName(paramName1, paramName2);
ReturnType result = FunctionName(paramName1,
paramName2,
paramName3); // Good:保持与上方参数对齐
ReturnType result = FunctionName(paramName1, paramName2,
paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进
ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行
paramName1, paramName2, paramName3); // 换行后,4 空格缩进
// Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解
int result = DealWithStructLikeParams(left.x, left.y, // 表示一组相关参数
right.x, right.y); // 表示另外一组相关参数
包括 if/for/while/do-while 语句应使用大括号,即复合语句。
while (condition) {} // Good:即使循环体是空,也应使用大括号
while (condition) {
continue; // Good:continue 表示空逻辑,使用大括号
}
case/default 语句相对 switch 缩进一层,风格如下:
switch (var) {
case 0: // Good: 缩进
DoSomething1(); // Good: 缩进
break;
case 1: { // Good: 带大括号格式
DoSomething2();
break;
}
default:
break;
}
指针类型"*"跟随变量或者函数名,例如:
int *p1; // OK
int* p2; // Bad:跟随类型
int*p3; // Bad:两边都没空格
int * p4; // Bad:两边都有空格
struct Foo *CreateFoo(void); // OK: "*"跟随函数名
特例:
char * const VERSION = "V100"; // OK: 当有 const 修饰符时,"*"两边都有空格
int Foo(const char * restrict p); // OK: 当有 restrict 修饰符时,"*"两边都有空格
sz = sizeof(int*); // OK:右侧没有变量,"*"跟随类型
宏
定义函数式宏前,应考虑能否用函数替代。对于可替代场景,建议用函数替代宏。对于有性能需求的场景,可以使用内联函数。
定义宏时,要使用完备的括号,例如:
#define SUM(a, b) ((a) + (b)) // 符合本规范要求.
#define SOME_CONST 100 // Good: 单独的数字无需括号
#define ANOTHER_CONST (-1) // Good: 负数需要使用括号
#define THE_CONST SOME_CONST // Good: 单独的标识符无需括号
以下情况需要注意:
- 宏参数参与 ‘#’, ‘##’ 操作时,不要加括号;
- 宏参数参与字符串拼接时,不要加括号;
- 宏参数作为独立部分,在赋值(包括+=, -=等)操作的某一边时,可以不加括号;
- 宏参数作为独立部分,在逗号表达式,函数或宏调用列表中,可以不加括号。
// x 不要加括号
#define MAKE_STR(x) #x
// obj 不要加括号
#define HELLO_STR(obj) "Hello, " obj
// a, b 需要括号;而 value 可以不加括号
#define UPDATE_VALUE(value, a, b) (value = (a) + (b))
// a 需要括号;而 b 可以不加括号
#define FOO(a, b) Bar((a) + 1, b)
包含多条语句的函数式宏的实现语句必须放在 do-while(0)中。
禁止把带副作用的表达式作为参数传递给函数式宏,比如自加操作(“a++”)。
函数式宏定义中慎用 return、goto、continue、break 等改变程序流程的语句。
禁止宏调用参数中出现预编译指令,如#include,#define和#ifdef,这样做会导致未定义的行为。
宏定义不以分号结尾。
头文件
头文件应当职责单一。
通常情况下,每个.c文件都应有一个相应的.h文件(并不一定同名),用于放置对外提供的函数声明、宏定义、类型定义等。如果不需要提供对外接口,可以没有对应的.h文件。
避免头文件循环依赖,如a.h 包含 b.h,b.h 包含 c.h,c.h 包含 a.h。
头文件应当自包含,即包含某个头文件,不需要引入其他头文件就可以编译。
头文件用#define、#ifndef、#endif保护,防止重复包含;不要使用 #pragma once。
禁止通过声明的方式引用外部函数接口、变量,只能通过包含头文件的方式使用其他模块或文件提供的接口。
建议按稳定度包含头文件,依次顺序为: 源码对应的头文件,C标准库,操作系统库,平台库,项目公共库,自己其他的依赖。
数据类型
基础数据类型建议使用los_compiler.h中定义的类型,比如无符号32位整数位定义为UINT32。
变量
避免大量栈分配,如较大的局部数组。
谨慎使用全局变量,尽量不用或少用全局变量。
变量应当初始化后再使用。
禁止将局部变量的地址返回到其作用域以外。
指向资源句柄或描述符的变量,在资源释放后立即赋予新值(如果变量的作用域马上结束可以不赋予新值)。指向资源句柄或描述符的变量包括指针、文件描述符、socket描述符以及其它指向资源的变量。
断言
断言必须使用宏定义,且只能在调试版本中生效。
断言应当看作设计约束,禁止用断言检测程序在运行期间可能导致的错误,可能发生的错误要用错误处理代码来处理。
禁止在断言内改变运行环境。
一个断言只用于检查一个错误。
函数
由一个进程向另一个进程发送的数据、由应用向内核发送的数据等应当进行合法性校验,校验包括但不限于:
- 校验数据长度
- 校验数据范围
- 校验数据类型和格式
- 校验输入只包含可接受的字符(“白名单”形式)
函数应避免使用全局变量、静态局部变量和直接的I/O操作,不可避免时,应当对读写操作进行封装。
标准库支持
CMSIS支持
基本概念
CMSIS是Cortex Microcontroller Software Interface Standard(Cortex微控制器软件接口标准)的缩写,是对于那些基于ARM Cortex处理器的微控制器独立于供应商的硬件抽象层。它包含多个组件层,其中之一是RTOS层,该层定义了一套通用及标准化的RTOS API接口,减少了应用开发者对特定RTOS的依赖,方便用户软件的移植重用。该套API有2个版本,分别为版本1(CMSIS-RTOS v1)和版本2(CMSIS-RTOS v2),OpenHarmony LiteOS-M仅提供其版本2的实现。
开发指导
接口说明
CMSIS-RTOS v2提供下面几种功能,接口详细信息可以查看API参考。
表1 内核信息与控制
接口名 | 接口描述 |
osKernelGetInfo | 获取RTOS内核信息。 |
osKernelGetState | 获取当前的RTOS内核状态。 |
osKernelGetSysTimerCount | 获取RTOS内核系统计时器计数。 |
osKernelGetSysTimerFreq | 获取RTOS内核系统计时器频率。 |
osKernelInitialize | 初始化RTOS内核。 |
osKernelLock | 锁定RTOS内核调度程序。 |
osKernelUnlock | 解锁RTOS内核调度程序。 |
osKernelRestoreLock | 恢复RTOS内核调度程序锁定状态。 |
osKernelResume | 恢复RTOS内核调度程序。(暂未实现) |
osKernelStart | 启动RTOS内核调度程序。 |
osKernelSuspend | 挂起RTOS内核调度程序。(暂未实现) |
osKernelGetTickCount | 获取RTOS内核滴答计数。 |
osKernelGetTickFreq | 获取RTOS内核滴答频率。 |
表2 线程管理
接口名 | 接口描述 |
osThreadDetach | 分离线程(线程终止时可以回收线程存储)。(暂未实现) |
osThreadEnumerate | 枚举活动线程。(暂未实现) |
osThreadExit | 终止当前正在运行的线程的执行。 |
osThreadGetCount | 获取活动线程的数量。 |
osThreadGetId | 返回当前正在运行的线程的线程ID。 |
osThreadGetName | 获取线程的名称。 |
osThreadGetPriority | 获取线程的当前优先级。 |
osThreadGetStackSize | 获取线程的堆栈大小。 |
osThreadGetStackSpace | 根据执行期间的堆栈水印记录获取线程的可用堆栈空间。 |
osThreadGetState | 获取线程的当前线程状态。 |
osThreadJoin | 等待指定线程终止。(暂未实现) |
osThreadNew | 创建一个线程并将其添加到活动线程中。 |
osThreadResume | 恢复线程的执行。 |
osThreadSetPriority | 更改线程的优先级。 |
osThreadSuspend | 暂停执行线程。 |
osThreadTerminate | 终止线程的执行。 |
osThreadYield | 将控制权传递给处于就绪状态的下一个线程。 |
表3 线程标志
接口名 | 接口描述 |
osThreadFlagsSet | 设置线程的指定线程标志。(暂未实现) |
osThreadFlagsClear | 清除当前正在运行的线程的指定线程标志。(暂未实现) |
osThreadFlagsGet | 获取当前正在运行的线程的当前线程标志。(暂未实现) |
osThreadFlagsWait | 等待当前正在运行的线程的一个或多个线程标志发出信号。(暂未实现) |
表4 事件标志
接口名 | 接口描述 |
osEventFlagsGetName | 获取事件标志对象的名称。(暂未实现) |
osEventFlagsNew | 创建并初始化事件标志对象。 |
osEventFlagsDelete | 删除事件标志对象。 |
osEventFlagsSet | 设置指定的事件标志。 |
osEventFlagsClear | 清除指定的事件标志。 |
osEventFlagsGet | 获取当前事件标志。 |
osEventFlagsWait | 等待一个或多个事件标志被发出信号。 |
表5 通用等待函数
接口名 | 接口描述 |
osDelay | 等待超时(时间延迟)。 |
osDelayUntil | 等到指定时间。 |
表6 计时器管理
接口名 | 接口描述 |
osTimerDelete | 删除计时器。 |
osTimerGetName | 获取计时器的名称。(暂未实现) |
osTimerIsRunning | 检查计时器是否正在运行。 |
osTimerNew | 创建和初始化计时器。 |
osTimerStart | 启动或重新启动计时器。 |
osTimerStop | 停止计时器。 |
表7 互斥管理
接口名 | 接口描述 |
osMutexAcquire | 获取互斥或超时(如果已锁定)。 |
osMutexDelete | 删除互斥对象。 |
osMutexGetName | 获取互斥对象的名称。(暂未实现) |
osMutexGetOwner | 获取拥有互斥对象的线程。 |
osMutexNew | 创建并初始化Mutex对象。 |
osMutexRelease | 释放由osMutexAcquire获取的Mutex。 |
表8 信号量
接口名 | 接口描述 |
osSemaphoreAcquire | 获取信号量令牌或超时(如果没有可用的令牌)。 |
osSemaphoreDelete | 删除一个信号量对象。 |
osSemaphoreGetCount | 获取当前信号量令牌计数。 |
osSemaphoreGetName | 获取信号量对象的名称。(暂未实现) |
osSemaphoreNew | 创建并初始化一个信号量对象。 |
osSemaphoreRelease | 释放信号量令牌,直到初始最大计数。 |
表9 内存池
接口名 | 接口描述 |
osMemoryPoolAlloc | 从内存池分配一个内存块。 |
osMemoryPoolDelete | 删除内存池对象。 |
osMemoryPoolFree | 将分配的内存块返回到内存池。 |
osMemoryPoolGetBlockSize | 获取内存池中的内存块大小。 |
osMemoryPoolGetCapacity | 获取内存池中最大的内存块数。 |
osMemoryPoolGetCount | 获取内存池中使用的内存块数。 |
osMemoryPoolGetName | 获取内存池对象的名称。 |
osMemoryPoolGetSpace | 获取内存池中可用的内存块数。 |
osMemoryPoolNew | 创建并初始化一个内存池对象。 |
表10 消息队列
接口名 | 接口描述 |
osMessageQueueDelete | 删除消息队列对象。 |
osMessageQueueGet | 从队列获取消息,或者如果队列为空,则从超时获取消息。 |
osMessageQueueGetCapacity | 获取消息队列中的最大消息数。 |
osMessageQueueGetCount | 获取消息队列中排队的消息数。 |
osMessageQueueGetMsgSize | 获取内存池中的最大消息大小。 |
osMessageQueueGetName | 获取消息队列对象的名称。(暂未实现) |
osMessageQueueGetSpace | 获取消息队列中消息的可用插槽数。 |
osMessageQueueNew | 创建和初始化消息队列对象。 |
osMessageQueuePut | 如果队列已满,则将消息放入队列或超时。 |
osMessageQueueReset | 将消息队列重置为初始空状态。(暂未实现) |
开发流程
CMSIS-RTOS2组件可以作为库或源代码提供(下图显示了库)。通过添加CMSIS-RTOS2组件(通常是一些配置文件),可以将基于CMSIS的应用程序扩展为具有RTOS功能。只需包含cmsis_os2.h头文件就可以访问RTOS API函数,这使用户应用程序能够处理RTOS内核相关事件,而在更换内核时无需重新编译源代码。
静态对象分配需要访问RTOS对象控制块定义。特定于实现的头文件(下图中的os_xx .h)提供对此类控制块定义的访问。对于OpenHarmony LiteOS-M内核,由文件名以los_开头的头文件提供,这些文件包含OpenHarmony LiteOS-M内核的这些定义。
编程实例
#include ...
#include "cmsis_os2.h"
/*----------------------------------------------------------------------------
* 应用程序主线程
*---------------------------------------------------------------------------*/
void app_main (void *argument) {
// ...
for (;;) {}
}
int main (void) {
// 系统初始化
MySystemInit();
// ...
osKernelInitialize(); // 初始化CMSIS-RTOS
osThreadNew(app_main, NULL, NULL); // 创建应用程序主线程
osKernelStart(); // 开始执行线程
for (;;) {}
}
POSIX支持
基本概念
OpenHarmony内核使用musl libc库以及自研接口,支持部分标准POSIX接口,开发者可基于POSIX标准接口开发内核之上的组件及应用。
开发指导
接口说明
表1 process
需要包含的头文件 | 接口名 | 描述 |
#include <stdlib.h> | void abort(void); | 中止线程执行 |
#include <assert.h> | void assert(scalar expression); | 断言为假终止线程 |
#include <pthread.h> | int pthread_cond_destroy(pthread_cond_t *cond); | 销毁条件变量 |
#include <pthread.h> | int pthread_cond_init(pthread_cond_t *restrict co nd, const pthread_condattr_t *restrict attr); | 初始化条件变量 |
#include <pthread.h> | int pthread_cond_timedwait(pthread_cond_t *restr ict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); | 等待条件 |
#include <pthread.h> | int pthread_condattr_init(pthread_condattr_t *attr); | 初始化条件变量属性对象 |
#include <pthread.h> | int pthread_mutex_unlock(pthread_mutex_t *mutex); | 解锁互斥锁 |
#include <pthread.h> | int pthread_create(pthread_t *thread, const pthread_ attr_t *attr, void *(*start_routine)(void *), void *arg); | 创建一个新的线程 |
#include <pthread.h> | int pthread_join(pthread_t thread, void **retval); | 等待指定的线程结束 |
#include <pthread.h> | pthread_t pthread_self(void); | 获取当前线程的ID |
#include <pthread.h> | int pthread_getschedparam(pthread_t thread, int * policy, struct sched_param *param); | 获取线程的调度策略和参数 |
#include <pthread.h> | int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param); | 设置线程的调度策略和参数 |
#include <pthread.h> | int pthread_mutex_init(pthread_mutex_t *__restrict m , const pthread_mutexattr_t *__restrict a); | 初始化互斥锁 |
#include <pthread.h> | int pthread_mutex_lock(pthread_mutex_t *m); | 互斥锁加锁操作 |
#include <pthread.h> | int pthread_mutex_trylock(pthread_mutex_t *m); | 互斥锁尝试加锁操作 |
#include <pthread.h> | int pthread_mutex_destroy(pthread_mutex_t *m); | 销毁互斥锁 |
#include <pthread.h> | int pthread_attr_init(pthread_attr_t *attr); | 初始化线程属性对象 |
#include <pthread.h> | int pthread_attr_destroy(pthread_attr_t *attr); | 销毁线程属性对象 |
#include <pthread.h> | int pthread_attr_getstacksize(const pthread_attr _t *attr, size_t *stacksize); | 获取线程属性对象的堆栈大小 |
#include <pthread.h> | int pthread_attr_setstacksize(pthread_attr_t *attr , size_t stacksize); | 设置线程属性对象的堆栈大小 |
#include <pthread.h> | int pthread_attr_getschedparam(const pthread_ attr_t *attr, struct sched_param *param); | 获取线程属性对象的调度参数属性 |
#include <pthread.h> | int pthread_attr_setschedparam(pthread_attr_t * attr, const struct sched_param *param); | 设置线程属性对象的调度参数属性 |
#include <pthread.h> | int pthread_getname_np(pthread_t pthread, char *name, size_t len); | 获取线程名称 |
#include <pthread.h> | int pthread_setname_np(pthread_t pthread, const char *name); | 设置线程名称 |
#include <pthread.h> | int pthread_cond_broadcast(pthread_cond_t *c); | 解除若干已被等待条件阻塞的线程 |
#include <pthread.h> | int pthread_cond_signal(pthread_cond_t *c); | 解除被阻塞的线程 |
#include <pthread.h> | int pthread_cond_wait(pthread_cond_t *__restrict c, pthread_mutex_t *__restrict m); | 等待条件 |
表2 fs
需要包含的头文件 | 接口名 | 描述 |
#include <libgen.h> | char *dirname(char *path); | 获取目录名 |
#include <dirent.h> | struct dirent *readdir(DIR *dirp); | 读目录 |
#include <sys/stat.h> | int stat(const char *restrict path, struct stat *restrict buf); | 获取文件信息 |
#include <unistd.h> | int unlink(const char *pathname); | 删除文件 |
#include <fcntl.h | int open(const char *path, int oflags, …); | 用于打开文件,如文件不存在,创建文件并打开 |
#include <nistd.h> | int close(int fd); | 关闭文件 |
#include <stdio.h> | int rename(const char *oldpath, const char *newpath); | 重命名指定的文件 |
#include <dirent.h> | DIR *opendir(const char *dirname); | 打开指定目录 |
#include <dirent.h> | int closedir(DIR *dir); | 关闭指定目录 |
#include <sys/mount.h> | int mount(const char *source, const char *target, con st char *filesystemtype, unsigned long mountflags, c onst void *data); | 挂载文件系统 |
#include <sys/mount.h> | int umount(const char *target); | 卸载文件系统 |
#include <sys/mount.h> | int umount2(const char *target, int flag); | 卸载文件系统 |
#include <sys/stat.h> | int fsync(int fd); | 将与指定文件描述符关联的文件同步到存储设备 |
#include <sys/stat.h> | int mkdir(const char *pathname, mode_t mode); | 创建目录 |
#include <unistd.h> | int rmdir(const char *path); | 删除目录 |
#include <sys/stat.h> | int fstat(int fd, struct stat *buf); | 获取文件状态信息 |
#include <sys/statfs.h> | int statfs(const char *path, struct statfs *buf); | 获取指定路径下文件的文件系统信息 |
表3 time
需要包含的头文件 | 接口名 | 描述 |
#include <sys/time.h> | int gettimeofday(struct timeval *tv, struct timezone *tz); | 获取时间。当前暂无时区概念,tz返回为空 |
#include <time.h> | struct tm *gmtime(const time_t *timep); | 将日期和时间转换为细分时间或ASCII |
#include <time.h> | struct tm *localtime(const time_t *timep); | 获取时间 |
#include <time.h> | struct tm *localtime_r(const time_t *timep, struct tm *result); | 获取时间 |
#include <time.h> | time_t mktime(struct tm *tm); | 将日期和时间转换为细分时间或ASCII |
#include <time.h> | size_t strftime(char *s, size_t max, const char * format,const struct tm *tm); | 格式化日期和时间字符串 |
#include <time.h> | time_t time(time_t *tloc); | 获得日历时间 |
#include <sys/times.h> | clock_t times(struct tms *buf); | 获取线程时间 |
#include <unistd.h> | int usleep(useconds_t usec); | 休眠(微秒单位) |
#include <time.h> | int nanosleep(const struct timespec *tspec1, struct timespec *tspec2); | 暂停当前线程直到指定的时间到达 |
#include <time.h> | int clock_gettime(clockid_t id, struct timespec *tspec); | 获取时钟的时间 |
#include <time.h> | int timer_create(clockid_t id, struct sigevent *__ restrict evp, timer_t *__restrict t); | 为线程创建计时器 |
#include <time.h> | int timer_delete(timer_t t); | 为线程删除计时器 |
#include <time.h> | int timer_settime(timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old); | 为线程设置计时器 |
#include <time.h> | time_t time (time_t *t); | 获取时间 |
#include <time.h> | char *strptime(const char *s, const char *format, struct tm *tm); | 将时间的字符串表示形式转换为时间tm结构 |
表4 util
需要包含的头文件 | 接口名 | 描述 |
#include <stdlib.h> | int atoi(const char *nptr); | 字符串转换整型(int) |
#include <stdlib.h> | long atol(const char *nptr); | 字符串转换整型(long) |
#include <stdlib.h> | long long atoll(const char *nptr); | 字符串转换整型(long long) |
#include <ctype.h> | int isalnum(int c); | 检查字母数字字符 |
#include <ctype.h> | int isascii(int c); | 检查ASCII |
#include <ctype.h> | int isdigit(int c); | 检查数字字符 |
#include <ctype.h> | int islower(int c); | 检查小写字符 |
#include <ctype.h> | int isprint(int c); | 检查任何可打印字符,包括空格 |
#include <ctype.h> | int isspace(int c); | 检查空格字符 |
#include <ctype.h> | int isupper(int c); | 检查所传的字符是否是大写字母 |
#include <ctype.h> | int isxdigit(int c); | 判断字符是否为十六进制数 |
#include <stdlib.h> | long int random (void); | 生成伪随机数 |
#include <stdlib.h> | void srandom(unsigned int seed); | 初始化随机数生成器 |
#include <ctype.h> | int tolower(int c); | 字母转换成小写 |
#include <ctype.h> | int toupper(int c); | 字母转换成大写 |
#include <stdarg.h> | type va_arg(va_list ap, type); | 获取可变参数的当前参数,返回指定类型并将指针指向下一参数 |
#include <stdarg.h> | void va_copy(va_list dest, va_list src); | 复制参数 |
#include <stdarg.h> | void va_end(va_list ap); | 清空va_list可变参数列表 |
#include <stdarg.h> | void va_start(va_list ap, last); | 定义变长参数列表的起始位置 |
#include <string.h> | char *strchr(const char *s, int c); | 在字符串中定位字符 |
#include <string.h> | int strcmp(const char *s1, const char *s2); | 比较字符串 |
#include <string.h> | size_t strcspn(const char *s, const char *reject); | 获取前缀子串的长度 |
#include <string.h> | char *strdup(const char *s); | 字符串拷贝到新建的位置处 |
#include <string.h> | size_t strlen(const char *s); | 计算字符串长度 |
#include <strings.h> | int strncasecmp(const char *s1, const char *s2, size_t n); | 比较固定长度字符串(忽略大小写) |
#include <strings.h> | int strcasecmp(const char *s1, const char *s2); | 比较字符串(忽略大小写) |
#include <string.h> | int strncmp(const char *s1, const char *s2, size_t n); | 比较字符串(指定长度) |
#include <string.h> | char *strrchr(const char *s, int c); | 在字符串中定位字符 |
#include <string.h> | char *strstr(const char *haystack, const char *needle); | 寻找指定的子串 |
#include <stdlib.h> | long int strtol(const char *nptr, char **endptr, int base); | 将字符串转换为long型整数 |
#include <stdlib.h> | unsigned long int strtoul(const char *nptr, char **endptr, int base); | 将字符串转换为unsigned long型整数 |
#include <stdlib.h> | unsigned long long int strtoull(const char *nptr, char **endptr,int base); | 将字符串转换为unsigned long long型整数 |
#include <regex.h> | int regcomp(regex_t *preg, const char *regex, int cflags); | 编译正则表达式 |
#include <regex.h> | int regexec(const regex_t *preg, const char * string, size_t nmatch,regmatch_t pmatch[], int eflags); | 匹配正则表达式 |
#include <regex.h> | void regfree(regex_t *preg); | 释放正则表达式 |
#include <string.h> | char *strerror(int errnum); | 返回描述错误号的字符串 |
表5 math
需要包含的头文件 | 接口名 | 描述 |
#include <stdlib.h> | int abs(int i); | 取绝对值 |
#include <math.h> | double log(double x); | 自然对数函数 |
#include <math.h> | double pow(double x, double y); | 求x的指数y次幂 |
#include <math.h> | double round(double x); | 从零开始,舍入到最接近的整数 |
#include <math.h> | double sqrt(double x); | 平方根 |
表6 IO
需要包含的头文件 | 接口名 | 描述 |
#include <stdio.h> | void clearerr(FILE *stream); | 清除流的文件结尾和错误指示 |
#include <stdio.h> | int fclose(FILE *stream); | 关闭文件流 |
#include <stdio.h> | FILE *fdopen(int fd, const char *mode); | 通过文件描述符打开文件流 |
#include <stdio.h> | int feof(FILE *stream); | 检测返回文件末尾指示位 |
#include <stdio.h> | int fflush(FILE *stream); | 刷新流 |
#include <stdio.h> | char *fgets(char *s, int size, FILE *stream); | 读取流的下一行 |
#include <stdio.h> | int fileno(FILE *stream); | 返回流的文件描述符 |
#include <stdio.h> | FILE *fopen(const char *path, const char *mode); | 打开流 |
#include <stdio.h> | int fputs(const char *s, FILE *stream); | 向指定流写入一行 |
#include <stdio.h> | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); | 读一个流 |
#include <stdio.h> | int fseek(FILE *stream, long offset, int whence); | 设置流指针的位置 |
#include <stdio.h> | long ftell(FILE *stream); | 获取流指针的位置 |
#include <stdio.h> | size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream); | 向流写入 |
#include <stdio.h> | void perror(const char *s); | 打印系统错误信息 |
#include <stdio.h> | void rewind(FILE *stream); | 重新定位流 |
#include <unistd.h> | ssize_t write(int fd, const void *buf, size_t size); | 写文件内容 |
#include <unistd.h> | ssize_t read(int fd, void *buf, size_t size); | 读文件内容 |
表7 net
需要包含的头文件 | 接口名 | 描述 |
#include <sys/socket.h> | void freeaddrinfo(struct addrinfo *res); | 释放调用getaddrinfo所分配的动态内存 |
#include <sys/socket.h> | int getaddrinfo(const char *restrict nodename,const char *restrict servname,const struct addrinfo *restrict hints,struct addrinfo **restrict res); | 网络地址和服务转换 |
#include <sys/socket.h> | int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,char *restrict node, socklen_t nodelen , char *restrict service,socklen_t servicelen, int flags); | 以协议无关的方式进行地址到名称的转换 |
#include <net/if.h> | unsigned int if_nametoindex(const char *ifname); | 通过网络接口名得到索引 |
#include <arpa/inet.h> | in_addr_t inet_addr(const char *cp); | 网络主机地址点分十进制形式转换位二进制形式 |
#include <arpa/inet.h> | char *inet_ntoa(struct in_addr in); | 网络主机地址二进制形式转换位点分十进制形式 |
#include <arpa/inet.h> | const char *inet_ntop(int af, const void *src,char *dst, socklen_t size); | 网络地址转换 |
#include <arpa/inet.h> | int inet_pton(int af, const char *src, void *dst); | 网络地址转换 |
#include <sys/socket.h> | int listen(int sockfd, int backlog); | 监听套接字 |
#include <sys/socket.h> | ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); | 从套接字接收消息.只支持iov大小为1的场景,且不支持ancillary消息 |
#include <sys/socket.h> | ssize_t send(int sockfd, const void *buf, size_t len, int flags); | 从socket发送消息 |
#include <sys/socket.h> | ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); | 从socket发送消息。不支持ancillary消息 |
#include <sys/socket.h> | ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); | 从socket发送消息 |
#include <sys/socket.h> | int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen); | 设置与套接字关联的选项 |
表8 mem
需要包含的头文件 | 接口名 | 描述 |
#include <string.h> | int memcmp(const void *s1, const void *s2, size_t n); | 内存比较 |
#include <string.h> | void *memcpy(void *dest, const void *src, size_t n); | 内存拷贝 |
#include <string.h> | void *memset(void *s, int c, size_t n); | 内存初始化 |
#include <stdlib.h> | void *realloc(void *ptr, size_t size); | 重分配内存 |
#include <stdlib.h> | void *malloc(size_t size); | 动态分配内存块大小 |
#include <stdlib.h> | void free(void *ptr); | 释放ptr所指向的内存空间 |
表9 IPC
需要包含的头文件 | 接口名 | 描述 |
#include <semaphore.h> | int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); | 计时锁定信号量 |
#include <semaphore.h> | int sem_destroy(sem_t *sem); | 销毁指定的无名信号量 |
#include <semaphore.h> | int sem_init(sem_t *sem, int pshared , unsigned int value); | 创建并初始化一个无名信号量 |
#include <semaphore.h> | int sem_post(sem_t *sem); | 增加信号量计数 |
#include <semaphore.h> | int sem_wait(sem_t *sem); | 获取信号量 |
#include <mqueue.h> | mqd_t mq_open(const char *mqName, int openFlag, …); | 此API用于打开一个具有指定名称的已有消息队列或创建一个新的消息队列 |
#include <mqueue.h> | int mq_close(mqd_t personal); | 此API用于关闭具有指定描述符的消息队列 |
#include <mqueue.h> | int mq_unlink(const char *mqName); | 此API用于删除具有指定名称的消息队列 |
#include <mqueue.h> | int mq_send(mqd_t personal, const char *msg,size_t msgLen, unsigned int msgPrio); | 此API用于将具有指定内容和长度的消息放入具有指定描述符的消息队列中 |
#include <mqueue.h> | ssize_t mq_receive(mqd_t personal, char *msg, size_t msgLen, unsigned int *msgPrio); | 此API用于从具有指定描述符的消息队列中删除最老的消息,并将其放入msg_ptr所指向的缓冲区中 |
#include <mqueue.h> | int mq_timedsend(mqd_t personal, const char *msg, size_t msgLen, unsigned int msgPrio, c onst struct timespec *absTimeout) | 此API用于在预定时间将具有指定内容和长度的消息放入具有描述符的消息队列中 |
#include <mqueue.h> | ssize_t mq_timedreceive(mqd_t personal, char *msg, size_t msgLen, unsigned int *msgPrio, const struct timespec *absTimeout); | 此API用于从具有指定描述符的消息队列消息中获取具有指定消息内容和长度的消息 |
#include <mqueue.h> | int mq_setattr(mqd_t mqdes, const struct mq_ attr *__restrict newattr, struct mq_attr *__restrict oldattr); | 设置描述符指定的消息队列属性 |
#include <libc.h> | const char *libc_get_version_string(void); | 获取libc版本字符串 |
#include <libc.h> | int libc_get_version(void); | 获取libc版本号 |
注意事项
常用错误码对照表:
错误码 | 值 | 描述 | 含义 |
ENOERR | 0 | Success | 成功 |
EPERM | 1 | Operation not permitted | 操作不允许 |
ENOENT | 2 | No such file or directory | 没有这样的文件或目录 |
ESRCH | 3 | No such process | 没有这样的进程(暂不支持) |
EINTR | 4 | Interrupted system call | 系统调用被中断 |
EIO | 5 | I/O error | I/O错误 |
ENXIO | 6 | No such device or address | 没有这样的设备或地址 |
E2BIG | 7 | Arg list too long | 参数列表太长 |
ENOEXEC | 8 | Exec format error | 执行格式错误 |
EBADF | 9 | Bad file number | 坏的文件描述符 |
ECHILD | 10 | No child processes | 没有子进程(暂不支持) |
EAGAIN | 11 | Try again | 资源暂时不可用 |
ENOMEM | 12 | Out of memory | 内存溢出 |
EACCES | 13 | Permission denied | 拒绝许可 |
EFAULT | 14 | Bad address | 错误的地址 |
ENOTBLK | 15 | Block device required | 块设备请求 |
EBUSY | 16 | Device or resource busy | 设备或资源忙 |
EEXIST | 17 | File exists | 文件存在 |
EXDEV | 18 | Cross-device link | 无效的交叉链接 |
ENODEV | 19 | No such device | 设备不存在 |
ENOTDIR | 20 | Not a directory | 不是一个目录 |
EISDIR | 21 | Is a directory | 是一个目录 |
EINVAL | 22 | Invalid argument | 无效的参数 |
ENFILE* | 23 | File table overflow | 打开太多的文件系统 |
EMFILE | 24 | Too many open files | 打开的文件过多 |
EFBIG | 27 | File too large | 文件太大 |
ENOSPC | 28 | No space left on device | 设备上没有空间 |
ESPIPE | 29 | Illegal seek | 非法移位 |
EROFS | 30 | Read-only file system | 只读文件系统 |
EMLINK | 31 | Too many links | 太多的链接 |
EDOM | 33 | Math argument out of domain | 数值结果超出范围 |
ERANGE | 34 | Math result not representable | 数值结果不具代表性 |
EDEADLK | 35 | Resource deadlock would occur | 资源死锁错误 |
ENAMETOOLONG | 36 | Filename too long | 文件名太长 |
ENOLCK | 37 | No record locks available | 没有可用锁 |
ENOSYS | 38 | Function not implemented | 功能没有实现 |
ENOTEMPTY | 39 | Directory not empty | 目录不空 |
ELOOP | 40 | Too many symbolic links encountered | 符号链接层次太多 |
ENOMSG | 42 | No message of desired type | 没有期望类型的消息 |
EIDRM | 43 | Identifier removed | 标识符删除 |
ELNRNG | 48 | Link number out of range | 链接数超出范围 |
EBADR | 53 | Invalid request descriptor | 请求描述符无效 |
EBADRQC | 56 | Invalid request code | 无效的请求代码 |
ENOSTR | 60 | Device not a stream | 设备不是字符流 |
ENODATA | 61 | No data available | 无可用数据 |
ETIME | 62 | Timer expired | 计时器过期 |
EPROTO | 71 | Protocol error | 协议错误 |
EBADMSG | 74 | Not a data message | 非数据消息 |
EOVERFLOW | 75 | Value too large for defined data type | 值太大,对于定义数据类型 |
EMSGSIZE | 90 | Message too long | 消息太长 |
编程实例
demo功能:
创建一个线程并将父线程中的信息传递给子线程,在子线程中打印传递过来的信息和自身线程id值。
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数DemoForTest。
#include <stdio.h>
#include <pthread.h>
pthread_t ntid;
void *ThreadFn(void *arg)
{
pthread_t tid;
while(1) {
tid = pthread_self();
printf("\n++++++++++++++ %s %s tid = %d ++++++++++++++\n", (char*)arg, __FUNCTION__, tid);
}
return ((void *)0);
}
void DemoForTest()
{
int err;
char* str = "Hello world";
err = pthread_create(&ntid, NULL, ThreadFn, (void*)str);
if(err != 0) {
printf("can't create thread\n");
}
}
执行DemoForTest运行结果如下:
++++++++++++++ Hello world ThreadFn tid = 48 ++++++++++++++
++++++++++++++ Hello world ThreadFn tid = 48 ++++++++++++++
++++++++++++++ Hello world ThreadFn tid = 48 ++++++++++++++
文章转载自:https://docs.openharmony.cn/pages/v3.2Beta/zh-cn/device-dev/kernel/kernel-mini-appx-code.md/