OpenHarmony设备开发 轻量系统内核(LiteOS-M)附录

zh_ff
发布于 2023-3-24 16:29
浏览
0收藏

版本: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内核的这些定义。

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/​

分类
已于2023-3-24 16:29:45修改
收藏
回复
举报
回复
    相关推荐