前言¶
概述
本文档介绍LiteOS的体系结构,并介绍如何进行LiteOS的开发和调试。
读者对象
本文档主要适用于LiteOS的开发者。
本文档主要适用于以下对象:
物联网端侧软件开发工程师
物联网架构设计师
符号约定
在本文中可能出现下列标志,它们所代表的含义如下。
符号 |
说明 |
|---|---|
|
表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。 |
|
表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。 |
|
表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。 |
|
用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。 |
|
对正文中重点信息的补充说明。 “说明”不是安全警示信息,不涉及人身、设备及环境伤害信息。 |
修订记录
文档版本 |
发布日期 |
修改说明 |
|---|---|---|
01 |
2026-03-17 |
第一次正式版本发布 |
概述¶
背景介绍¶
LiteOS是轻量级的实时操作系统,具备轻量级、低功耗、快速启动、组件丰富等关键能力。
图 1 LiteOS的基本框架图

LiteOS的基础内核包括不可裁剪的极小内核和可裁剪的其他模块。极小内核只包含任务管理和调度相关的基本功能。可裁剪的模块包括信号量、互斥锁、队列管理、事件管理、软件定时器、内存管理、中断管理、异常管理和系统时钟Tick。
LiteOS支持在单核环境上运行。
LiteOS的优势
高实时性,高稳定性。
低功耗。支持动态加载、分散加载。
支持功能静态裁剪。
快速启动。
内核各模块简介
任务管理
提供任务的创建、删除、延迟、挂起、恢复等功能,支持锁定和解锁任务调度,支持任务按优先级高低的抢占调度以及同优先级时间片轮转调度。
内存管理
提供静态内存和动态内存两种算法,支持内存的申请与释放。目前支持的内存管理算法包括固定大小的BOX算法、动态申请的bestfit算法和bestfit_little算法。
提供内存统计、内存越界检测功能。
硬件相关
提供中断管理、异常管理、系统时钟Tick等功能。
中断管理:提供中断的创建、删除、使能、禁止及请求位清除功能。
异常管理:系统运行过程中发生异常后,跳转至异常处理模块,打印当前异常的函数调用栈信息或保存当前系统状态。
Tick:Tick是操作系统调度的基本时间单位,其时长由每秒的Tick数决定,由用户配置。
IPC通信
提供消息队列、事件、信号量和互斥锁功能。
消息队列:支持消息队列的创建、删除、发送和接收功能。
事件:支持读事件和写事件功能。
信号量:支持信号量的创建、删除、申请和释放功能。
互斥锁:支持互斥锁的创建、删除、申请和释放功能。
软件定时器
软件定时器提供了定时器的创建、删除、启动、停止功能。
低功耗
Tickless:Tickless机制是通过计算下一次有意义的时钟中断的时间,来减少不必要的时钟中断,从而降低系统功耗。打开Tickless功能后,系统会在CPU空闲时启动Tickless机制。
扩展内核/维测
CPU占用率:可以获取系统或者指定任务的CPU占用率。
Trace事件监测:实时获取事件发生的上下文,并写入缓冲区。支持自定义缓冲区,监测指定模块的事件,开启/停止Trace,清除/输出trace缓冲区数据等。
Perf:一款性能分析工具,可以通过Perf查找性能瓶颈、定位热点代码。LiteOS支持配置采样数据缓冲区、配置采样事件、启动/停止Perf、输出采样数据等功能。
Shell:LiteOS Shell使用串口或Telnet工具接收用户输入的命令,通过命令的方式调用、执行相应的应用程序。LiteOS Shell支持常用的基本调试功能,包含系统、文件、网络和动态加载等相关命令,同时支持用户添加自定义命令。
C++支持
LiteOS支持部分STL特性、异常和RTTI特性,其他特性由编译器支持。
支持的核¶
支持的核 |
芯片 |
|---|---|
LinxCore131、LinxCore132、LinxCore170 |
Hi3863、Hi3853 |
使用约束¶
LiteOS提供一套自有OS接口,同时也支持POSIX和CMSIS接口。请勿混用这些接口。混用接口可能导致不可预知的错误,例如:用POSIX接口申请信号量,但用LiteOS接口释放信号量。
开发驱动程序只能用LiteOS的接口,上层APP建议用POSIX接口。
基础内核¶
任务¶
概述¶
基本概念
从系统角度看,任务是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用内存空间等系统资源,并独立于其它任务运行。
LiteOS的任务模块可以给用户提供多个任务,实现任务间的切换,帮助用户管理业务程序流程。LiteOS的任务模块具有如下特性:
支持多任务。
一个任务表示一个线程。
抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。
相同优先级任务支持时间片轮转调度方式。
共有32个优先级[0, 31],最高优先级为0,最低优先级为31。
任务存在两种属性(detached、joinable)。默认情况下任务创建的属性为detached。
任务相关概念
任务状态
LiteOS系统中的任务有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。
任务状态通常分为以下四种:
就绪(Ready):该任务在就绪队列中,只等待CPU。
运行(Running):该任务正在执行。
阻塞(Blocked):该任务不在就绪队列中,包含任务被挂起(suspend状态)、任务被延时(delay状态)、任务正在等待信号量、读写队列或者等待事件等。
退出态(Dead):该任务运行结束,等待系统回收资源,包含任务处于僵尸状态(zombie状态)、任务已经被回收(unused状态)。
任务状态迁移
图 1 任务状态示意图

任务状态迁移说明:
就绪态→运行态
任务创建后进入就绪态,发生任务切换时,就绪队列中最高优先级的任务被执行,从而进入运行态,同时该任务从就绪队列中移出。
运行态→阻塞态
正在运行的任务发生阻塞(挂起、延时、读信号量等)时,任务状态由运行态变成阻塞态,然后发生任务切换,取出并运行就绪队列中最高优先级任务。
阻塞态→就绪态(阻塞态→运行态)
阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪队列,从而由阻塞态变成就绪态;此时如果被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,该任务由就绪态变成运行态。
就绪态→阻塞态
任务也有可能在就绪态时被阻塞(挂起),此时任务状态由就绪态变为阻塞态,该任务从就绪队列中删除,不会参与任务调度,直到该任务被恢复。
运行态→就绪态
有更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪队列中最高优先级任务会被取出并切换到运行态,那么原先运行的任务由运行态变为就绪态,并插入到就绪队列中。
运行态→退出态
运行中的任务运行结束,任务状态由运行态变为退出态。退出态包含任务运行结束的正常退出状态以及僵尸状态。例如,joinable属性的任务运行结束但是没有被回收资源,对外呈现的就是僵尸状态,即退出态。
阻塞态→退出态
阻塞的任务调用删除接口,任务状态由阻塞态变为退出态。
任务ID
任务ID在任务创建时通过参数返回给用户,是任务的重要标识。系统中的ID号是唯一的。用户可以通过任务ID对指定任务进行任务挂起、任务恢复、查询任务名等操作。
任务优先级
优先级表示任务执行的优先顺序。任务的优先级决定了在发生任务切换时即将要执行的任务,就绪队列中最高优先级的任务将得到执行。
任务入口函数
新任务得到调度后将执行的函数。该函数由用户实现,在任务创建时通过任务创建结构体设置。
任务栈
每个任务都拥有一个独立的栈空间,我们称为任务栈。栈空间里保存的信息包含局部变量、寄存器、函数参数、函数返回地址等。
任务上下文
任务在运行过程中使用的一些资源如寄存器等,称为任务上下文。当这个任务挂起时,其他任务继续执行,可能会修改寄存器等资源中的值。如果任务切换时没有保存任务上下文,可能会导致任务恢复后出现未知错误。
因此,LiteOS在任务切换时会将切出任务的任务上下文信息,保存在自身的任务栈中,以便任务恢复后从栈空间中恢复挂起时的上下文信息,从而继续执行挂起时被打断的代码。
任务控制块TCB
每个任务都含有一个任务控制块(TCB)。TCB包含了任务上下文栈指针(stack pointer)、任务状态、任务优先级、任务ID、任务名、任务栈大小等信息,可以反映出每个任务运行情况。
任务切换
任务切换包含获取就绪队列中最高优先级任务、切出任务上下文保存、切入任务上下文恢复等动作。
任务属性
任务存在两种属性(detached、joinable),任务的不同属性可以决定任务的终止方式。joinable属性即为非分离状态,这种状态的任务退出后,不会自动释放资源,需要被其他任务通过调用LOS_TaskJoin接口等待获取其返回值,才能被释放资源。detached属性即为分离状态,这种状态的任务退出后将自动回收资源,并且这个任务不能被等待,等待是没有意义的。
调度器相关概念
LiteOS包含两种调度模式,即全局队列调度器模式和多队列调度器模式,在不同场景下各有优势,当前系统默认使用全局调度器模式。
全局队列调度器模式:系统仅使用一个优先级队列保存准备运行的任务,所有核使用同一优先级队列调度,所有任务抢占同一个优先级队列,因此全局队列调度器模式拥有更高的实时性。
多队列调度器模式:仅多核使用,系统按照核数为每个核创建一个优先级队列,每个核仅使用自己的优先级队列进行调度,系统通过负载均衡算法将任务在不同的优先级队列上进行迁移,相比全局队列调度模式可以有效减少频繁的核间迁移。
运作机制
用户创建任务时,系统会初始化任务栈,预置上下文。此外,系统还会将“任务入口函数”地址放在相应位置。这样在任务第一次启动进入运行态时,将会执行“任务入口函数”。
开发指导¶
使用场景
任务创建后,内核可以执行锁定任务调度、解锁任务调度、挂起、恢复、延时等操作,同时也可以设置任务优先级,获取任务优先级。
功能
LiteOS的任务管理模块提供下面几种功能,接口详细信息请参见API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建和删除任务 |
LOS_TaskCreateOnly |
创建任务,并使该任务进入suspend状态,不对该任务进行调度。如果需要调度,可以调用LOS_TaskResume使该任务进入ready状态。 |
LOS_TaskCreate |
创建任务,并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务。 |
|
LOS_TaskCreateOnlyStatic |
创建任务,任务栈由用户传入并使该任务进入suspend状态,不对该任务进行调度。如果需要调度,可以调用LOS_TaskResume使该任务进入ready状态。 |
|
LOS_TaskCreateStatic |
创建任务,任务栈由用户传入并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务。 |
|
LOS_TaskDelete |
删除指定的任务。 |
|
LOS_TaskJoin |
以阻塞的方式等待指定的任务并回收任务资源。 |
|
控制任务状态 |
LOS_TaskResume |
恢复挂起的任务,使该任务进入ready状态。 |
LOS_TaskSuspend |
挂起指定的任务,然后切换任务。 |
|
LOS_TaskDelay |
任务延时等待,释放CPU,等待时间到期后该任务会重新进入ready状态。 |
|
LOS_TaskYield |
当前任务释放CPU,并将其移到具有相同优先级的就绪任务队列的末尾。 |
|
LOS_TaskDetach |
将任务属性设置为detached。 |
|
控制任务调度 |
LOS_TaskLock |
锁定任务调度,但任务仍可被中断打断。 |
LOS_TaskUnlock |
解锁任务调度。 |
|
LOS_TaskUnlockNoSched |
解锁任务调度,并继续执行本任务。 |
|
控制任务优先级 |
LOS_CurTaskPriSet |
设置当前任务的优先级。 |
LOS_TaskPriSet |
设置指定任务的优先级。 |
|
LOS_TaskPriGet |
获取指定任务的优先级。 |
|
回收任务栈资源 |
LOS_TaskResRecycle |
回收所有待回收的任务栈资源。 |
获取任务信息 |
LOS_CurTaskIDGet |
获取当前任务的ID。 |
LOS_TaskInfoGet |
获取指定任务的信息,包括任务状态、优先级、任务栈大小、栈顶指针SP、任务入口函数和已使用的任务栈大小等。 |
|
LOS_TaskIsScheduled |
查询当前核是否已经开始任务调度。 |
|
任务信息维测 |
LOS_TaskSwitchHookReg |
注册任务上下文切换的钩子函数。只有开启LOSCFG_BASE_CORE_TSK_MONITOR宏开关后,这个钩子函数才会在任务发生上下文切换时被调用。 |
任务空闲处理回调 |
LOS_IdleHandlerHookReg |
注册空闲任务钩子函数,当系统空闲时调用。 |
申请任务的安全栈 |
LOS_TaskAllocSecureContext |
申请任务的安全栈。只有开启LOSCFG_TRUSTZONE宏开关后,该函数才可使用。 |
须知: 各个任务的任务栈大小可以在创建任务时进行针对性的设置,若设置为0,则使用默认任务栈大小(LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE)作为任务栈大小。
配置项
共享栈特性
共享栈特性说明:在原生系统中,每个任务都拥有自己的独立栈区,引入共享栈后,将会有部分任务共用同一片任务栈,称为共享栈,任务发生调度时,老任务的栈数据从共享栈拷出到堆,新任务的栈数据从堆拷回到共享栈。
共享栈使用说明:
LOSCFG_SHARED_STACK:共享栈总开关,开启后才能使用共享栈特性。
LOSCFG_SHARED_STACK_SIZE:配置共享栈的大小(Byte),宏默认值为16384,使用时可以根据实际情况调整,调整范围为:3072到65536,且需要能够整除16,建议配置为“参与共享的任务中任务栈的最大值”。
目前共享栈特性使用白名单策略,在名单内的任务为共享任务。配置方式如下(以hiwing为例):
在文件“Huawei_LiteOS/self_src/targets/hiwing/board.c”的g_taskBlackList数组中进行配置,一共3个字段,分别表示“任务名称”、“是否参与共享”、“是否在栈数据拷贝时进行压缩”。
示例如下:
/* 在此处设置共享栈白名单,名单内的任务参与共享 只有参与共享的任务才能参与压缩 */ const TaskBlackList g_sharedTask[LOSCFG_BASE_CORE_TSK_LIMIT + 1] = { // taskName 加入共享? 加入压缩? {"app_Task", TRUE, FALSE}, {"SerialShellTask", TRUE, FALSE}, {"SerialEntryTask", TRUE, FALSE}, {"TskInSharedBlackLits", TRUE, FALSE}, };
须知:如果任务创建时,指定任务使用创建时传入的栈,则该任务无法参与共享。
共享栈开启后,还可以开启LOSCFG_SHARED_TASK_STAT宏增加任务信息显示功能,但会增加内存开销约0.6KB。
功能分类
接口名
描述
显示任务的调度性能信息
SharedTaskStatPrint
打印任务性能信息:
第一组为总体性能数据,包括任务调度最大耗时、平均耗时以及调度耗时超过8000 cycleTime的次数。
第二组为单任务性能数据,包括任务名称、ID、调度次数、参与压缩拷贝次数(未参与或未开启压缩则为0)、压缩成功次数、压缩成功率、拷出到堆的最大耗时、拷入共享栈的最大耗时,以及拷贝耗时超过8000 cycleTime的次数。
加权公平调度特性
加权公平调度(后文简称WCFS)特性说明:
无WCFS的系统下,系统中存在一组优先级队列PriQueue,任务根据自己的优先级插入到不同的队列中,任务调度时,拿取优先级最高的队列的队首。
引入WCFS之后,任务将根据自己的优先级以及运行时间,加权计算得到一个虚拟运行时间,根据虚拟运行时间进行优先级排序,达到相对公平的状态。任务调度时,先从CFS队列中获取一个优先级最高的CFS任务CFSTask,然后获取优先级最高的RT任务(非CFS任务)RTTask,比较CFSTask与RTTask的优先级,选择其中优先级更高的执行,如果CFSTask与RTTask的优先级相同,则执行RTTask。RTTask任务和CFS任务都执行完时,CPU才会去执行Idle任务。
使用说明:
打开宏LOSCFG_WCFS_SCHEDULER即可使用CFS特性。目前加权公平调度特性使用黑名单策略,在名单内的任务不参与加权公平调度。配置方式如下(以hiwing为例):
在文件“LiteOS/Huawei_LiteOS/self_src/targets/hiwing/board.c”中的g_rtTaskName数组中进行配置,将不参与加权公平调度的线程名加入数组即可。
示例如下:
CHAR *g_rtTaskName[CFS_TASK_LIST_NUM] = { "app_Task", "SerialShellTask", "SerialEntryTask", };
其他:
目前LiteOS还提供UINT32 LOS_RemoveTaskFromCFS(UINT32 taskid);接口,该接口传入线程id,对应id的线程将被加入WCFS黑名单,成功则返回OK。
说明:
注意:如果该线程已经不参与WCFS,或者该线程为IdleTask,也返回OK。
TASK状态
LiteOS任务的大多数状态由内核维护,唯有自删除状态对用户可见,需要用户在创建任务时传入如下定义内容:
定义 |
实际数值 |
描述 |
|---|---|---|
LOS_TASK_STATUS_JOINABLE |
0x0080 |
任务不会自动释放资源,需要被其他任务等待获取其返回值,才能被释放资源 |
LOS_TASK_STATUS_DETACHED |
0x0100 |
任务是自删除的 |
用户在调用创建任务接口时,可以将创建任务的TSK_INIT_PARAM_S参数的uwResved域设置为LOS_TASK_STATUS_DETACHED(detached属性)或者LOS_TASK_STATUS_JOINABLE(joinable属性)。
须知: 任务joinable属性受LOSCFG_TASK_JOINABLE开关影响。
打开菜单(如何打开请参见“打开menuconfig菜单”),LOSCFG_TASK_JOINABLE配置位于Kernel ---> Basic Config ---> Task --->Enable Join/Detach mechanism。
LOSCFG_TASK_JOINABLE打开,只有将任务状态设置为LOS_TASK_STATUS_JOINABLE才能设置为joinable属性的任务,joinable属性的任务不会自删除,需要用户通过调用LOS_TaskJoin函数去回收joinable属性任务的资源,或者通过调用LOS_TaskDetach接口设置任务为detached属性,detached属性的任务运行完成之后会自动回收资源。
LOSCFG_TASK_JOINABLE关闭,任务只有detached属性,不管TSK_INIT_PARAM_S参数的uwResved域是否设置为LOS_TASK_STATUS_JOINABLE。
LOS_TaskDelete接口无法回收joinable属性的任务资源,只能通过LOS_TaskJoin接口。
TASK优先级
LiteOS任务的优先级当前支持范围为(LOS_TASK_PRIORITY_HIGHEST ~ LOS_TASK_PRIORITY_LOWEST),数值越小,优先级越高。
定义 |
实际数值 |
描述 |
|---|---|---|
LOS_TASK_PRIORITY_HIGHEST |
0 |
任务最高优先级。 |
LOS_TASK_PRIORITY_LOWEST |
31 |
任务最低优先级。 |
TASK错误码
创建任务、删除任务、挂起任务、恢复任务、延时任务等操作存在失败的可能,失败时会返回对应的错误码,以便快速定位错误原因。
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|
LOS_ERRNO_TSK_NO_MEMORY |
0x03000200 |
内存空间不足。 |
增大动态内存空间,有两种方式可以实现:
|
LOS_ERRNO_TSK_PTR_NULL |
0x02000201 |
传递给任务创建接口的任务参数initParam为空指针,或者传递给任务信息获取的接口的参数为空指针。 |
确保传入的参数不为空指针。 |
LOS_ERRNO_TSK_STKSZ_NOT_ALIGN |
0x02000202 |
创建一个由用户自行传入任务栈的任务时,任务栈起始地址或任务栈大小没有按照LOSCFG_STACK_POINT_ALIGN_SIZE对齐。 |
任务栈起始地址和任务栈大小按照LOSCFG_STACK_POINT_ALIGN_SIZE对齐。 |
LOS_ERRNO_TSK_PRIOR_ERROR |
0x02000203 |
创建任务或者设置任务优先级时,传入的优先级参数不正确。 |
检查任务优先级,必须在[0, 31]的范围内。 |
LOS_ERRNO_TSK_ENTRY_NULL |
0x02000204 |
创建任务时传入的任务入口函数为空指针。 |
定义任务入口函数。 |
LOS_ERRNO_TSK_NAME_EMPTY |
0x02000205 |
创建任务时传入的任务名为空指针。 |
设置任务名。 |
LOS_ERRNO_TSK_STKSZ_TOO_SMALL |
0x02000206 |
创建任务时传入的任务栈太小。 |
增大任务的任务栈大小使之不小于系统设置最小任务栈大小(配置项为LOS_TASK_MIN_STACK_SIZE)。 |
LOS_ERRNO_TSK_ID_INVALID |
0x02000207 |
超出OS支持范围内的无效的任务ID。 |
检查任务ID。 |
LOS_ERRNO_TSK_ALREADY_SUSPENDED |
0x02000208 |
挂起任务时,发现任务已经被挂起。 |
等待这个任务被恢复后,再去尝试挂起这个任务。 |
LOS_ERRNO_TSK_NOT_SUSPENDED |
0x02000209 |
恢复任务时,发现任务未被挂起。 |
挂起这个任务后,再去尝试恢复这个任务。 |
LOS_ERRNO_TSK_NOT_CREATED |
0x0200020A |
未创建任务。 |
创建这个任务,这个错误可能会发生在以下操作中:
|
LOS_ERRNO_TSK_DELETE_LOCKED |
0x0300020B |
删除任务时,任务处于锁定状态。 |
解锁任务之后再删除任务。 |
LOS_ERRNO_TSK_DELAY_IN_INT |
0x0300020D |
中断期间,进行任务延时。 |
等待退出中断后再进行延时操作。 |
LOS_ERRNO_TSK_DELAY_IN_LOCK |
0x0200020E |
在任务锁定状态下,延时该任务。 |
解锁任务之后再延时任务。 |
LOS_ERRNO_TSK_YIELD_IN_LOCK |
0x0200020F |
在任务锁定状态下,进行Yield操作。 |
任务解锁后再进行Yield操作。 |
LOS_ERRNO_TSK_YIELD_NOT_ENOUGH_TASK |
0x02000210 |
执行Yield操作时,发现具有相同优先级的就绪任务队列中没有其他任务。 |
增加与当前任务具有相同优先级的任务数。 |
LOS_ERRNO_TSK_TCB_UNAVAILABLE |
0x02000211 |
创建任务时,发现没有空闲的任务控制块可以使用。 |
调用LOS_TaskResRecyle接口回收空闲的任务控制块,如果回收后依然创建失败,再增加系统的任务控制块数量。 |
LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK |
0x02000214 |
不允许删除、挂起、延时系统级别的任务,例如idle任务、软件定时器任务,也不允许修改系统级别的任务优先级。 |
检查任务ID,不要操作系统任务。 |
LOS_ERRNO_TSK_SUSPEND_LOCKED |
0x03000215 |
不允许将处于锁定状态的任务挂起。 |
任务解锁后,再尝试挂起任务。 |
LOS_ERRNO_TSK_STKSZ_TOO_LARGE |
0x02000220 |
创建任务时,设置了过大的任务栈。 |
减小任务栈大小。 |
LOS_ERRNO_TSK_CPU_AFFINITY_MASK_ERR |
0x03000223 |
设置指定任务的运行CPU集合时,传入了错误的CPU集合。 |
检查传入的CPU掩码。 |
LOS_ERRNO_TSK_YIELD_IN_INT |
0x02000224 |
不允许在中断中对任务进行Yield操作。 |
不要在中断中进行Yield操作。 |
LOS_ERRNO_TSK_MP_SYNC_RESOURCE |
0x02000225 |
跨核任务删除同步功能,资源申请失败。 |
通过设置更大的LOSCFG_BASE_IPC_SEM_LIMIT的值,增加系统支持的信号量个数。 |
LOS_ERRNO_TSK_MP_SYNC_FAILED |
0x02000226 |
跨核任务删除同步功能,任务未及时删除。 |
需要检查目标删除任务是否存在频繁的状态切换,导致系统无法在规定的时间内完成删除的动作。 |
LOS_ERRNO_TSK_ALLOC_SECURE_INT |
0x02000227 |
不允许在中断中申请任务安全栈。 |
不要在中断中申请任务安全栈。 |
LOS_ERRNO_TSK_SECURE_ALREADY_ALLOC |
0x02000228 |
任务的安全栈已经申请过。 |
不要重复为同一个任务申请安全栈。 |
LOS_ERRNO_TSK_ALLOC_SECURE_FAILED |
0x02000229 |
申请安全栈失败。 |
安全世界的内存不足,建议释放部分安全世界的内存。 |
LOS_ERRNO_TSK_FREE_SECURE_FAILED |
0x0200022A |
释放安全栈失败。 |
当前未使用。 |
LOS_ERRNO_TSK_NOT_ALLOW_IN_INT |
0x0200022B |
此接口不允许在中断上下文中。 |
不要在中断中调用对应接口。 |
LOS_ERRNO_TSK_SCHED_LOCKED |
0x0200022C |
此接口可能存在阻塞,不允许关闭调度。 |
检查调度锁逻辑。 |
LOS_ERRNO_TSK_ALREADY_JOIN |
0x0200022D |
joinable的任务不能被多个任务同时等待。 |
检查是否已经存在其他任务正在等待。 |
LOS_ERRNO_TSK_IS_DETACHED |
0x0200022E |
当前任务属性为detached。 |
检查是否join一个detached属性的任务或者重复设置任务为detached属性。 |
LOS_ERRNO_TSK_NOT_JOIN_SELF |
0x0200022F |
任务不能join自己。 |
检查taskid是否传入正确。 |
LOS_ERRNO_TSK_IS_ZOMBIE |
0x02000230 |
不允许操作一个处于僵尸状态的任务。 |
检查是否对一个处于僵尸状态的任务进行删除、设置优先级/属性/CPU亲和性、suspend、resume,不要去操作一个僵尸状态的任务。 |
须知:
错误码定义见“错误码简介”错误码简介。8~15位代表的所属模块为任务模块,值为0x02。
任务模块中的错误码序号 0x16、0x1C,未被定义,不可用。
开发流程
本部分以创建任务为例,讲解开发流程。
打开菜单,进入Kernel ---> Basic Config ---> Task菜单,完成任务模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_CORE_TSK_LIMIT
系统支持的最大任务数。
(0,256]
不同平台默认值不一样
无
LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE
最小任务栈大小,一般使用默认值即可。
(0,OS_SYS_MEM_SIZE)
不同平台默认值不一样
无
LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE
默认任务栈大小。
(0,OS_SYS_MEM_SIZE)
不同平台默认值不一样
无
LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE
idle任务栈大小,一般使用默认值即可。
(0,OS_SYS_MEM_SIZE)
不同平台默认值不一样
无
LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO
默认任务优先级,一般使用默认配置即可。
[0,31]
10
无
LOSCFG_BASE_CORE_TIMESLICE
任务时间片调度开关。
YES/NO
YES
无
LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
同优先级任务最长执行时间。(单位:Tick)
(0,65535]
不同平台默认值不一样
无
LOSCFG_OBSOLETE_API
使能后,任务参数使用旧方式UINTPTR auwArgs[4],否则使用新的任务参数VOID *pArgs。建议关闭此开关,使用新的任务参数。
YES/NO
不同平台默认值不一样
无
LOSCFG_BASE_CORE_TSK_MONITOR
任务栈溢出检查和监测开关。
YES/NO
YES
无
LOSCFG_TASK_STACK_STATIC_ALLOCATION
支持创建任务时,由用户传入任务栈。
YES/NO
NO
无
LOSCFG_TASK_JOINABLE
开启内核join/detach机制。
YES/NO
NO
无
LOSCFG_TASK_STACK_DYNAMIC_ALLOCATION
支持创建任务时,给任务栈动态分配内存。
YES/NO
YES
无
打开菜单,进入Kernel ---> Basic Config ---> Enable Scheduler菜单,完成任务调度相关配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_SCHED_SQ
全局队列调度器模式。
YES/NO
YES
无
LOSCFG_SCHED_MQ
多队列调度器模式。
YES/NO
NO
LOSCFG_KERNEL_SMP
LOSCFG_SCHED_LOAD_BALANCE_SIMPLE
Simple均衡算法。
YES/NO
NO
LOSCFG_SCHED_MQ
LOSCFG_SCHED_LOAD_BALANCE_CPUP
基于CPUP均衡算法。
YES/NO
NO
LOSCFG_SCHED_MQ
LOSCFG_KERNEL_CPUP
锁任务调度LOS_TaskLock,防止高优先级任务调度。
创建任务LOS_TaskCreate,或静态创建任务LOS_TaskCreateStatic(需要打开LOSCFG_TASK_STACK_STATIC_ALLOCATION宏)。
解锁任务LOS_TaskUnlock,让任务按照优先级进行调度。
延时任务LOS_TaskDelay,任务延时等待。
挂起指定的任务LOS_TaskSuspend,任务挂起等待恢复操作。
恢复挂起的任务LOS_TaskResume。
平台差异性
无。
DFX
OsDbgTskInfoGet函数:调用OsDbgTskInfoGet函数,传参为taskId,将展示该任务的任务名、入口函数、taskId、优先级、当前状态、栈大小、使用峰值、sp指针位置、栈顶、CPUP状态(启用CPUP特性时打印)等。传参为0xFFFFFFFF,将展示所有任务的信息。
开启加权公平调度特性时,参与公平调度的任务优先级后,会带有[CFS]标志。
开启共享栈特性时,参与共享栈的任务的栈大小、栈顶、峰值相同。
注意事项¶
任务创建:
任务名是指针,并没有分配空间,在设置任务名时,禁止将局部变量的地址赋值给任务名指针。
创建任务时,会按照16Byte大小或者sizeof(UINTPTR) * 2对齐的方式为任务栈分配内存。确定任务栈大小的原则是:够用就行,多了浪费,少了任务栈溢出。
锁任务:
锁任务调度,并不关中断,因此任务仍可被中断打断。
锁任务调度必须和解锁任务调度配合使用。
任务优先级:
设置任务优先级时可能会发生任务调度。
LOS_CurTaskPriSet和LOS_TaskPriSet接口不能在中断中使用,也不能用于修改软件定时器任务的优先级。
LOS_TaskPriGet接口传入的task ID对应的任务未创建或者超过最大任务数,统一返回0xffff。
禁止操作:
挂起当前任务时,如果任务已经被锁定,则无法挂起。
idle任务及软件定时器任务不能被挂起或者删除。
在中断处理函数中或者在锁任务的情况下,执行LOS_TaskDelay会失败。
任务资源:
执行Idle任务时,会对之前已删除任务的任务控制块和任务栈进行回收。
可配置的系统最大任务数是指整个系统的任务总个数,而非用户能使用的任务个数。例如:系统软件定时器多占用一个任务资源,那么用户能使用的任务资源就会减少一个。
在删除任务时要保证任务申请的资源(如互斥锁、信号量等)已被释放。
编程实例¶
实例描述
本实例介绍基本的任务操作方法,包含2个不同优先级任务的创建、任务延时、任务锁与解锁调度、挂起和恢复等操作,并阐述任务优先级调度的机制以及各接口的应用。
编程示例
前提条件:在menuconfig菜单中完成任务模块的配置。
UINT32 g_taskHiId;
UINT32 g_taskLoId;
#define TSK_PRIOR_HI 4
#define TSK_PRIOR_LO 5
UINT32 Example_TaskHi(VOID)
{
UINT32 ret;
printf("Enter TaskHi Handler.\r\n");
/* 延时2个Tick,延时后该任务会挂起,执行剩余任务中最高优先级的任务(g_taskLoId任务) */
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
printf("Delay Task Failed.\r\n");
return LOS_NOK;
}
/* 2个Tick时间到了后,该任务恢复,继续执行 */
printf("TaskHi LOS_TaskDelay Done.\r\n");
/* 挂起自身任务 */
ret = LOS_TaskSuspend(g_taskHiId);
if (ret != LOS_OK) {
printf("Suspend TaskHi Failed.\r\n");
return LOS_NOK;
}
printf("TaskHi LOS_TaskResume Success.\r\n");
return ret;
}
/* 低优先级任务入口函数 */
UINT32 Example_TaskLo(VOID)
{
UINT32 ret;
printf("Enter TaskLo Handler.\r\n");
/* 延时2个Tick,延时后该任务会挂起,执行剩余任务中最高优先级的任务(背景任务) */
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
printf("Delay TaskLo Failed.\r\n");
return LOS_NOK;
}
printf("TaskHi LOS_TaskSuspend Success.\r\n");
/* 恢复被挂起的任务g_taskHiId */
ret = LOS_TaskResume(g_taskHiId);
if (ret != LOS_OK) {
printf("Resume TaskHi Failed.\r\n");
return LOS_NOK;
}
printf("TaskHi LOS_TaskDelete Success.\r\n");
return ret;
}
/* 任务测试入口函数,创建两个不同优先级的任务 */
UINT32 Example_TskCaseEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S initParam;
/* 锁任务调度,防止新创建的任务比本任务高而发生调度 */
LOS_TaskLock();
printf("LOS_TaskLock() Success!\r\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskHi;
initParam.usTaskPrio = TSK_PRIOR_HI;
initParam.pcName = "TaskHi";
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
/* 创建高优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskHiId, &initParam);
if (ret != LOS_OK) {
LOS_TaskUnlock();
printf("Example_TaskHi create Failed!\r\n");
return LOS_NOK;
}
printf("Example_TaskHi create Success!\r\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskLo;
initParam.usTaskPrio = TSK_PRIOR_LO;
initParam.pcName = "TaskLo";
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
/* 创建低优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskLoId, &initParam);
if (ret != LOS_OK) {
LOS_TaskUnlock();
printf("Example_TaskLo create Failed!\r\n");
return LOS_NOK;
}
printf("Example_TaskLo create Success!\r\n");
/* 解锁任务调度,此时会发生任务调度,执行就绪队列中最高优先级任务 */
LOS_TaskUnlock();
return LOS_OK;
}
结果验证
编译运行得到的结果为:
LOS_TaskLock() Success!
Example_TaskHi create Success!
Example_TaskLo create Success!
Enter TaskHi Handler.
Enter TaskLo Handler.
TaskHi LOS_TaskDelay Done.
TaskHi LOS_TaskSuspend Success.
TaskHi LOS_TaskResume Success.
TaskHi LOS_TaskDelete Success.
内存¶
概述¶
基本概念
内存管理模块用于管理系统的内存资源,它是操作系统的核心模块之一,主要包括内存的初始化、分配以及释放。
在系统运行过程中,内存管理模块通过对内存的申请/释放来管理用户和OS对内存的使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。
LiteOS的内存管理分为静态内存管理和动态内存管理。
动态内存管理:在动态内存池中分配用户指定大小的内存块,支持bestfit(也称为dlink)和bestfit_little两种内存管理算法。
优点:按需分配。
缺点:内存池中可能出现碎片。
静态内存管理:在静态内存池中分配用户初始化时预设(固定)大小的内存块。
优点:分配和释放效率高,静态内存池中无碎片。
缺点:只能申请到初始化预设大小的内存块,不能按需申请。
动态内存运作机制——bestfit
动态内存管理,即在内存资源充足的情况下,根据用户需求从系统配置的一块比较大的连续内存(内存池,也是堆内存)中分配任意大小的内存块。当用户不需要该内存块时,又可以释放回系统供下一次使用。
与静态内存相比,动态内存管理的优点是按需分配,缺点是内存池中容易出现碎片。
bestfit内存管理结构如图1所示。
图 1 bestfit动态内存管理结构图

第一部分
堆内存(也称内存池)的起始地址 及堆区域总大小。
第二部分
本身是一个数组,每个元素是一个双向链表,所有free节点的控制头都会被分类挂在这个数组的双向链表中。
假设内存允许的最小节点为2min字节,则数组的第一个双向链表存储的是所有size为2min<=size< 2min+1的free节点,第二个双向链表存储的是所有size为2min+1<=size< 2min+2的free节点,依次类推第n个双向链表存储的是所有size为2min+n-1<=size< 2min+n的free节点。每次申请内存的时候,会从这个数组检索最合适大小的free节点以分配内存。每次释放内存时,会将该内存作为free节点存储至这个数组以便下次再使用。
第三部分
占用内存池极大部分的空间,是用于存放各节点的实际区域。以下是LosMemDynNode节点结构体申明以及简单介绍:
typedef struct { union { LOS_DL_LIST freeNodeInfo; /* Free memory node */ struct { UINT32 magic; UINT32 taskId : 16; }; }; struct tagLosMemDynNode *preNode; UINT32 sizeAndFlag; } LosMemCtlNode; typedef struct tagLosMemDynNode { LosMemCtlNode selfNode; } LosMemDynNode;
图 2 LosMemDynNode结构体介绍图

图 3 对齐方式申请内存结果示意

当申请到的节点包含的数据空间首地址不符合对齐要求时,需要进行对齐,可以通过增加Gap域确保返回的指针符合对齐要求。
动态内存运作机制——bestfit_little
bestfit_little算法也是一种最佳适配算法,相比于bestfit算法,少掉了图1中的第二部分,因此体积更小,但是申请释放性能不如bestfit。一般建议在内存空间充裕的情况下,选择bestfit算法。
slab运作机制
最佳适配算法使得每次分配内存时,都会选择内存池中最小最适合的内存块进行分配,而slab机制可以用于分配固定大小的内存块,从而减小产生内存碎片的可能性。
LiteOS内存管理中的slab机制支持配置slab class数目及每个class的最大空间。
现以内存池中共有4个slab class,每个slab class的最大空间为512Byte为例说明slab机制。这4个slab class是从内存池中按照最佳适配算法分配出来的。第一个slab class被分为32个16Byte的slab块,第二个slab class被分为16个32Byte的slab块,第三个slab class被分为8个64Byte的slab块,第四个slab class被分为4个128Byte的slab块。
初始化内存模块时,首先初始化内存池,然后在初始化后的内存池中按照最佳适配算法申请4个slab class,再逐个按照slab内存管理机制初始化4个slab class。
每次申请内存时,先在满足申请大小的最佳slab class中申请(比如用户申请20Byte内存,就在slab块大小为32Byte的slab class中申请),如果申请成功,就将slab内存块整块返回给用户,释放时整块回收。需要注意的是,如果满足条件的slab class中已无可以分配的内存块,则从内存池中按照最佳适配算法申请,而不会继续从有着更大slab块空间的slab class中申请。释放内存时,先检查释放的内存块是否属于slab class,如果是则还回对应的slab class中,否则还回内存池中。
图 4 内存数据结构概览

静态内存运作机制
静态内存实质上是一个静态数组,静态内存池内的块大小在初始化时设定,初始化后块大小不可变更。
静态内存池由一个控制块和若干相同大小的内存块构成。控制块位于内存池头部,用于内存块管理。内存块的申请和释放以块大小为粒度。
图 5 静态内存示意图

动态内存¶
开发指导¶
使用场景
动态内存管理的主要工作是动态分配并管理用户申请到的内存区间。
动态内存管理主要用于用户需要使用大小不等的内存块的场景。当用户需要使用内存时,可以通过操作系统的动态内存申请函数索取指定大小的内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
功能
LiteOS系统中的动态内存管理模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化和删除内存池 |
LOS_MemInit |
初始化一块指定的动态内存池,大小为size(开启LOSCFG_MEM_MUL_POOL_ALLOC时该接口需谨慎使用,初始化后需要主动申请内存避免内存池被错误释放)。 |
LOS_MemDeInit |
删除指定内存池,仅打开LOSCFG_MEM_MUL_POOL时有效。 |
|
LOS_MemPoolInit |
初始化一块指定的动态内存池,大小为size,属性为attr(表示是否开启slab功能)。 |
|
申请、释放动态内存 |
LOS_MemAlloc |
从指定动态内存池中申请size长度的内存。 |
LOS_MemFree |
释放已申请的内存。 |
|
LOS_MemRealloc |
按size大小重新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请成功,则释放原内存块。 |
|
LOS_MemAllocAlign |
从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存。 |
|
多内存池申请、释放内存 |
LOS_MulPoolRegister |
内存池地址获取和释放钩子注册,从钩子中获取内存池的地址和大小,仅LOSCFG_MEM_MUL_POOL_ALLOC时生效。(不支持重复注册) |
LOS_MulPoolAlloc |
从非系统池子中申请size长度的内存,内存不够时自动创建新内存池,仅LOSCFG_MEM_MUL_POOL_ALLOC时生效。 |
|
LOS_MulPoolRealloc |
从非系统池子中重申请size长度的内存,内存不够时自动创建新内存池,仅LOSCFG_MEM_MUL_POOL_ALLOC时生效。 |
|
LOS_MulPoolFree |
从非系统池子中释放已申请的内存,仅LOSCFG_MEM_MUL_POOL_ALLOC时生效。 |
|
LOS_MulPoolShrink |
释放没有使用的内存池,仅LOSCFG_MEM_MUL_POOL_ALLOC时生效。 |
|
获取内存池信息 |
LOS_MemPoolSizeGet |
获取指定动态内存池的总大小。 |
LOS_MemTotalUsedGet |
获取指定动态内存池的总使用量大小。 |
|
LOS_MemInfoGet |
获取指定内存池的内存结构信息,包括空闲内存大小、已使用内存大小、空闲内存块数量、已使用的内存块数量、最大的空闲内存块大小。 |
|
LOS_MemPoolList |
打印系统中已初始化的所有内存池,包括内存池的起始地址、内存池大小、空闲内存总大小、已使用内存总大小、最大的空闲内存块大小、空闲内存块数量、已使用的内存块数量。仅打开LOSCFG_MEM_MUL_POOL时有效。 |
|
获取内存块信息 |
LOS_MemFreeBlksGet |
获取指定内存池的空闲内存块数量。 |
LOS_MemTotalPeakUsedGet |
返回内存池的最大使用峰值(依赖LOSCFG_MEM_TASK_STAT宏) |
|
LOS_MemTotalUsedCostGet |
返回内存池中使用的内存实际占用大小(包括内存管理部分) |
|
LOS_MemUsedBlksGet |
获取指定内存池已使用的内存块数量。 |
|
LOS_MemTaskIdGet |
获取申请了指定内存块的任务ID,仅LOSCFG_MEM_DEBUG或 LOSCFG_MEM_TASK_STAT时生效。 |
|
LOS_MemLastUsedGet |
获取内存池尾节点使用的最后一个字节后的第一个字节的地址。 如果尾节点是已使用节点,则尾节点使用的最后一个字节为节点尾部。 如果尾节点是空节点,则尾节点使用的最后一个字节为节点头结构体。 |
|
LOS_MemNodeSizeCheck |
获取指定内存块的总大小和可用大小,仅打开LOSCFG_BASE_MEM_NODE_SIZE_CHECK时有效。 |
|
LOS_MemFreeNodeShow |
打印指定内存池的空闲内存块的大小及数量。 |
|
检查指定内存池的完整性 |
LOS_MemIntegrityCheck |
对指定内存池做完整性检查,仅打开LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK时有效。 |
设置、获取内存检查级别(仅打开LOSCFG_BASE_MEM_NODE_SIZE_CHECK时有效) |
LOS_MemCheckLevelSet |
设置内存检查级别。 |
LOS_MemCheckLevelGet |
获取内存检查级别。 |
|
为指定模块申请、释放动态内存(仅打开LOSCFG_MEM_MUL_MODULE时有效) |
LOS_MemMalloc |
从指定动态内存池分配size长度的内存给指定模块,并纳入模块统计。 |
LOS_MemMfree |
释放已经申请的内存块,并纳入模块统计。 |
|
LOS_MemMallocAlign |
从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存给指定模块,并纳入模块统计。 |
|
LOS_MemMrealloc |
按size大小重新分配内存块给指定模块,并将原内存块内容拷贝到新内存块,同时纳入模块统计。如果新内存块申请成功,则释放原内存块。 |
|
获取指定模块的内存使用量(仅打开LOSCFG_MEM_MUL_MODULE时有效) |
LOS_MemMusedGet |
获取指定模块的内存使用量,单位:Byte。 |
配置slab size |
LOS_SlabSizeCfg |
用来配置slab class size,必须在LOS_MemInit之前调用。 |
获取任务使用内存 |
LOS_MemTaskHeapInfoGet |
打印指定任务使用的内存信息。 |
dfx功能 |
LOS_SaveCallerRa |
保存当前任务的调用者。 |
LOS_IsSetCallerRa |
判断当前任务是否已经保存调用者。 |
|
LOS_ResetCallerRa |
重置当前任务的调用者。 |
|
LOS_MemDfxOpsReg |
注册一个DFX操作集,使系统能够记录内存操作信息。 |
|
LOS_MemAllocDfxHook |
在内存分配操作后被调用,记录分配的相关信息。 |
|
LOS_MemFreeDfxHook |
在内存释放操作后被调用,记录释放的相关信息。 |
|
LOS_MemPoolOpsReg |
注册一个DFX操作集,使系统能够记录内存池操作信息。 |
|
LOS_MemPoolMemAllocHook |
在内存池分配操作后被调用,记录分配的相关信息。 |
|
LOS_MemPoolMemFreeHook |
在内存池释放操作后被调用,记录释放的相关信息。 |
须知:
动态内存提供了内存调测功能,具体使用方法见“内存调测方法”。
对于bestfit_little算法,只支持“多内存池机制”和“内存合法性检查”,不支持其他内存调测功能。
上述接口中,通过宏开关控制的都是内存调测功能相关的接口。
通过LOS_MemAllocAlign/LOS_MemMallocAlign申请的内存进行LOS_MemRealloc/LOS_MemMrealloc操作后,不能保障新的内存首地址保持对齐。
对于bestfit_little算法,不支持对LOS_MemAllocAlign申请的内存进行LOS_MemRealloc操作,否则将返回失败。
对于多内存池申请、释放功能仅支持在bestfit算法下使用。
说明: LiteOS提供了多模块内存统计功能,该功能基于普通内存接口的封装接口,增加模块ID作为入参。不同业务模块进行内存操作时,调用对应封装接口,可统计各模块的内存使用情况,并通过模块ID获取指定模块的内存使用情况。 应用场景:系统业务模块化清晰,用户需统计各模块的内存占用情况。 依赖:目前只有bestfit内存管理算法支持该功能,需要使能LOSCFG_KERNEL_MEM_BESTFIT。 使用方法:
该功能配置项为LOSCFG_MEM_MUL_MODULE,配置路径是Kernel ---> Memory Management ---> Dynamic Memory Management ---> Enable Memory module statistics;
每个业务模块配置唯一module ID,模块代码中在内存操作时调用对应接口,并传入相应模块ID;
通过LOS_MemMusedGet接口获取指定模块的内存使用情况,可用于模块内存占用优化分析。 注意事项:
模块ID由宏MEM_MODULE_MAX限定,当系统模块个数超过该值时,需通过配置LOSCFG_MEM_MODULE_MAX来修改MEM_MODULE_MAX的大小,配置路径:Kernel ---> Memory Management --->Enable Memory module statistics ---> Max Memory Module Number。
模块中所有内存操作都需调用LOS_MemM开头的接口,否则可能导致统计不准确。
开发流程
本节介绍使用动态内存的典型场景开发流程。
在“los_config.h”文件中配置项动态内存池起始地址与大小。
配置项
含义
取值范围
默认值
依赖
OS_SYS_MEM_ADDR
系统动态内存起始地址
[0,n)
__heap_start
无
OS_SYS_MEM_SIZE
系统动态内存池的大小(DDR自适应配置),以byte为单位
[0,n)
从bss段末尾至系统DDR末尾
无
OS_SYS_MEM_ADDR:一般使用默认值即可。
OS_SYS_MEM_SIZE:一般使用默认值即可。
打开菜单,进入Kernel ---> Memory Management菜单,完成动态内存管理模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_MEM_BESTFIT
选择bestfit内存管理算法
YES/NO
YES
无
LOSCFG_KERNEL_MEM_BESTFIT_LITTLE
选择bestfit_little内存管理算法
YES/NO
NO
无
LOSCFG_KERNEL_MEM_SLAB_EXTENTION
使能slab功能,可以降低系统持续运行过程中内存碎片化的程度
YES/NO
NO
无
LOSCFG_KERNEL_MEM_SLAB_AUTO_EXPANSION_MODE
slab自动扩展,当分配给slab的内存不足时,能够自动从系统内存池中申请新的空间进行扩展
YES/NO
NO
LOSCFG_KERNEL_MEM_SLAB_EXTENTION
LOSCFG_MEM_TASK_STAT
使能任务内存统计
YES/NO
YES
LOSCFG_KERNEL_MEM_BESTFIT或LOSCFG_KERNEL_MEM_BESTFIT_LITTLE
LOSCFG_MEM_MUL_MODULE
多模块内存统计
YES/NO
NO
LOSCFG_KERNEL_MEM_BESTFIT
LOSCFG_MEM_MODULE_MAX
多模块内存统计最大模块数量
8-1024
32
LOSCFG_MEM_MUL_MODULE
LOSCFG_MEM_MUL_POOL
多内存池链接统计功能
YES/NO
NO
LOSCFG_KERNEL_MEM_BESTFIT或LOSCFG_KERNEL_MEM_BESTFIT_LITTLE
LOSCFG_MEM_MUL_POOL_ALLOC
多内存池弹性扩展功能
YES/NO
NO
LOSCFG_MEM_MUL_POOL和LOSCFG_KERNEL_MEM_BESTFIT且关闭LOSCFG_EXC_INTERACTION
初始化LOS_MemInit。
初始一个内存池后如图,生成一个EndNode,并且剩余的内存全部被标记为FreeNode节点。
说明:
EndNode作为内存池末尾的节点,size为0。
申请任意大小的动态内存LOS_MemAlloc。
判断动态内存池中是否存在申请量大小的空间,若存在,则划出一块内存块,以指针形式返回;若不存在,返回NULL。
调用三次LOS_MemAlloc函数可以创建三个节点,假设分别为UsedA、UsedB、UsedC,大小分别为sizeA、sizeB、sizeC。因为刚初始化内存池的时候只有一个大的FreeNode,所以这些内存块是从这个FreeNode中切割出来的。

当内存池中存在多个FreeNode的时候进行malloc,将会适配最合适大小的FreeNode用来新建内存块,减少内存碎片。若新建的内存块不等于被使用的FreeNode的大小,则在新建内存块后,多余的内存又会被标记为一个新的FreeNode。
释放动态内存LOS_MemFree。回收内存块,供下一次使用。
假设调用LOS_MemFree释放内存块UsedB,则会回收内存块UsedB,并且将其标记为FreeNode。在回收内存块时,相邻的FreeNode会自动合并。

平台差异性
无。
注意事项¶
内存分配:
对齐分配内存接口LOS_MemAllocAlign/LOS_MemMallocAlign由于需要进行地址对齐,可能会额外消耗部分内存,因此存在一些遗失内存。当系统释放该对齐内存时,会同时回收因对齐导致的遗失内存。
重新分配内存接口LOS_MemRealloc/LOS_MemMrealloc如果分配成功,系统会自己判定是否需要释放原来申请的内存,并返回重新分配的内存地址。如果重新分配失败,原来的内存保持不变,并返回NULL。禁止使用pPtr = LOS_MemRealloc(pool, pPtr, uwSize),即:不能使用原来的旧内存地址pPtr变量来接收返回值。
内存释放:
对同一块内存多次调用LOS_MemFree/LOS_MemMfree时,第一次会返回成功,但对同一块内存多次重复释放会导致非法指针操作,结果不可预知。
其他限制:
由于动态内存管理需要管理控制块数据结构来管理内存,这些数据结构会额外消耗内存,故实际用户可使用内存总量小于配置项OS_SYS_MEM_SIZE的大小。
由于动态内存管理的内存节点控制块结构体LosMemDynNode中,成员sizeAndFlag的数据类型为UINT32,高两位为标志位,余下的30位表示内存结点大小,因此用户初始化内存池的大小不能超过1G,否则会出现不可预知的结果。
编程实例¶
普通内存使用实例¶
实例描述
前提条件:在menuconfig菜单中完成动态内存的配置。
本实例执行以下步骤:
初始化一个动态内存池。
从动态内存池中申请一个内存块。
在内存块中存放一个数据。
打印出内存块中的数据。
释放该内存块。
编程示例
#define TEST_POOL_SIZE (2*1024*1024)
UINT8 g_testPool[TEST_POOL_SIZE];
VOID Example_DynMem(VOID)
{
UINT32 *mem = NULL;
UINT32 ret;
ret = LOS_MemInit(g_testPool, TEST_POOL_SIZE);
if (LOS_OK == ret) {
dprintf("内存池初始化成功!\n");
} else {
dprintf("内存池初始化失败!\n");
return;
}
/*分配内存*/
mem = (UINT32 *)LOS_MemAlloc(g_testPool, 4);
if (NULL == mem) {
dprintf("内存分配失败!\n");
return;
}
dprintf("内存分配成功\n");
/*赋值*/
*mem = 828;
dprintf("*mem = %d\n", *mem);
/*释放内存*/
ret = LOS_MemFree(g_testPool, mem);
if (LOS_OK == ret) {
dprintf("内存释放成功!\n");
} else {
dprintf("内存释放失败!\n");
}
return;
}
结果验证
编译运行得到的结果为:
内存池初始化成功!
内存分配成功
*mem = 828
内存释放成功!
多模块内存使用实例¶
实例描述
前提条件:在menuconfig菜单中完成多模块内存统计的配置。
本实例执行以下步骤:
从动态内存池中为模块0申请一个内存块。
获取模块0的内存使用量。
从动态内存池中为模块1申请一个内存块。
获取模块1的内存使用量。
释放模块0和模块1的内存。
编程示例
void test(void)
{
void *ptr = NULL;
void *ptrTmp = NULL;
UINT32 size = 0x10;
UINT32 module = 0;
UINT32 memUsed = 0;
ptr = LOS_MemMalloc(OS_SYS_MEM_ADDR, size, module);
if (ptr == NULL) {
PRINTK("module %d malloc failed\n", module);
} else {
PRINTK("module %d malloc successed\n", module);
}
memUsed = LOS_MemMusedGet(module);
PRINTK("module %d mem used size %d\n", module, memUsed);
module = 1;
ptrTmp = LOS_MemMalloc(OS_SYS_MEM_ADDR, size, module);
if (ptrTmp == NULL) {
PRINTK("module %d malloc failed\n", module);
} else {
PRINTK("module %d malloc successed\n", module);
}
memUsed = LOS_MemMusedGet(module);
PRINTK("module %d mem used size %d\n", module, memUsed);
module = 0;
(VOID)LOS_MemMfree(OS_SYS_MEM_ADDR, ptr, module);
module = 1;
(VOID)LOS_MemMfree(OS_SYS_MEM_ADDR, ptrTmp, module);
}
结果验证
编译运行得到的结果为:
module 0 malloc successed
module 0 mem used size 32
module 1 malloc successed
module 1 mem used size 32
静态内存¶
开发指导¶
使用场景
当用户需要使用固定长度的内存时,可以通过静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。
功能
LiteOS的静态内存管理主要为用户提供以下功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化静态内存池 |
LOS_MemboxInit |
初始化一个静态内存池,根据入参设定其起始地址、总大小及每个内存块大小。 |
清除静态内存块内容 |
LOS_MemboxClr |
清零指定静态内存块的内容。 |
申请、释放静态内存 |
LOS_MemboxAlloc |
从指定的静态内存池中申请一块静态内存块。 |
LOS_MemboxFree |
释放指定的一块静态内存块。 |
|
获取、打印静态内存池信息 |
LOS_MemboxStatisticsGet |
获取指定静态内存池的信息,包括内存池中总内存块数量、已经分配出去的内存块数量、每个内存块的大小。 |
LOS_ShowBox |
打印指定静态内存池所有节点信息(打印等级是LOS_INFO_LEVEL),包括内存池起始地址、内存块大小、总内存块数量、每个空闲内存块的起始地址、所有内存块的起始地址。 |
开发流程
本节介绍使用静态内存的典型场景开发流程。
打开菜单,进入Kernel ---> Memory Management菜单,完成静态内存管理模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_MEMBOX
使能membox内存管理
YES/NO
YES
无
LOSCFG_KERNEL_MEMBOX_STATIC
选择静态内存方式实现membox
YES/NO
YES
LOSCFG_KERNEL_MEMBOX
LOSCFG_KERNEL_MEMBOX_DYNAMIC
选择动态内存方式实现membox
YES/NO
NO
LOSCFG_KERNEL_MEMBOX
规划一片内存区域作为静态内存池。
调用LOS_MemboxInit初始化静态内存池。
初始化会将入参指定的内存区域分割为N块(N值取决于静态内存总大小和块大小),将所有内存块挂到空闲链表,在内存起始处放置控制头。
调用LOS_MemboxAlloc接口分配静态内存。
系统将会从空闲链表中获取第一个空闲块,并返回该内存块的起始地址。
调用LOS_MemboxClr接口。
将入参地址对应的内存块清零。
调用LOS_MemboxFree接口。
将该内存块加入空闲链表。
平台差异性
无。
注意事项¶
如果静态内存池区域是通过动态内存分配方式获得的,在不需要该静态内存池时,应释放该段内存,以避免内存泄露。
编程实例¶
实例描述
前提条件:在menuconfig菜单中完成静态内存的配置。
本实例执行以下步骤:
初始化一个静态内存池。
从静态内存池中申请一块静态内存。
在内存块存放一个数据。
打印出内存块中的数据。
清除内存块中的数据。
释放该内存块。
编程示例
VOID Example_StaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize = 10;
UINT32 boxSize = 100;
UINT32 boxMem[1000];
UINT32 ret;
ret = LOS_MemboxInit(&boxMem[0], boxSize, blkSize);
if(ret != LOS_OK) {
printf("内存池初始化失败!\n");
return;
} else {
printf("内存池初始化成功!\n");
}
/*申请内存块*/
mem = (UINT32 *)LOS_MemboxAlloc(boxMem);
if (NULL == mem) {
printf("内存分配失败!\n");
return;
}
printf("内存分配成功\n");
/*赋值*/
*mem = 828;
printf("*mem = %d\n", *mem);
/*清除内存内容*/
LOS_MemboxClr(boxMem, mem);
printf("清除内存内容成功\n *mem = %d\n", *mem);
/*释放内存*/
ret = LOS_MemboxFree(boxMem, mem);
if (LOS_OK == ret) {
printf("内存释放成功!\n");
} else {
printf("内存释放失败!\n");
}
return;
}
结果验证
编译运行得到的结果为:
内存池初始化成功!
内存分配成功
*mem = 828
清除内存内容成功
*mem = 0
内存释放成功!
中断¶
概述¶
基本概念
中断是指出现需要时,CPU暂停执行当前程序转而执行新程序的过程。即在程序运行过程中,出现了一个必须由CPU立即处理的事务。此时,CPU暂时中止当前程序的执行转而处理这个事务,这个过程就叫做中断。
外设可以在没有CPU介入的情况下完成一定的工作,但某些情况下也需要CPU为其执行一定的工作。通过中断机制,在外设不需要CPU介入时,CPU可以执行其它任务;而当外设需要CPU时,将通过产生中断信号使CPU立即中断当前任务来响应中断请求。这样可以使CPU避免把大量时间耗费在等待、查询外设状态的操作上,有效提高系统实时性以及执行效率。
LiteOS的中断特性:
中断嵌套,即高优先级的中断可抢占低优先级的中断,且可配置。
使用独立中断栈,可配置。
可配置支持的中断优先级个数。
可配置支持的中断数。
中断底半部,且可配置。
中断相关的硬件介绍
与中断相关的硬件可以划分为三类:设备、中断控制器、CPU本身。
设备
发起中断的源,当设备需要请求CPU时,产生一个中断信号,该信号连接至中断控制器。
中断控制器
中断控制器是CPU众多外设中的一个,它一方面接收其它外设中断引脚的输入,另一方面,它会发出中断信号给CPU。可以通过对中断控制器编程来打开和关闭中断源、设置中断源的优先级和触发方式。常用的中断控制器有VIC(Vector Interrupt Controller)、GIC(General Interrupt Controller)、PLIC(Platform-Level Interrupt Controller)和CLIC(Core-Local Interrupt Controller)。在RISC-V中使用的中断控制器是PLIC和CLIC。
CPU
CPU会响应中断源的请求,中断当前正在执行的任务,转而执行中断处理程序。
中断相关概念
中断号:
每个中断请求信号都会有特定的标志,使得计算机能够判断是哪个设备提出的中断请求,这个标志就是中断号。
中断请求:
“紧急事件”需向CPU提出申请(发一个电脉冲信号),要求中断,及要求CPU暂停当前执行的任务,转而处理该“紧急事件”,这一申请过程称为中断请求。
中断优先级:
为使系统能够及时响应并处理所有中断,系统根据中断时间的重要性和紧迫程度,将中断源分为若干个级别,称作中断优先级。
中断处理程序:
当外设产生中断请求后,CPU暂停当前的任务,转而响应中断申请,即执行中断处理程序。产生中断的每个设备都有相应的中断处理程序。
中断嵌套:
中断嵌套也称为中断抢占,指的是正在执行一个中断处理程序时,如果有另一个优先级更高的中断源提出中断请求,这时会暂时终止当前正在执行的优先级较低的中断源的中断处理程序,转而去处理更高优先级的中断请求,待处理完毕,再返回到之前被中断的处理程序中继续执行。
中断触发:
中断源向中断控制器发送中断信号,中断控制器对中断进行仲裁,确定优先级,将中断信号送给CPU。中断源产生中断信号的时候,会将中断触发器置“1”,表明该中断源产生了中断,要求CPU去响应该中断。
中断触发类型:
外部中断申请通过一个物理信号发送到NVIC/GIC,可以是电平触发或边沿触发。
中断向量:
中断服务程序的入口地址。
中断向量表:
存储中断向量的存储区,中断向量与中断号对应,中断向量在中断向量表中按照中断号顺序存储。
中断共享:
当外设较少时,可以实现一个外设对应一个中断号,但为了支持更多的硬件设备,可以让多个设备共享一个中断号,共享同一个中断号的中断处理程序形成一个链表。当外部设备产生中断申请时,系统会遍历执行中断号对应的中断处理程序链表直到找到对应设备的中断处理程序。在遍历执行过程中,各中断处理程序可以通过检测设备ID,判断是否是这个中断处理程序对应的设备产生的中断。
核间中断:
对于多核系统,中断控制器允许一个CPU的硬件线程去中断其他CPU的硬件线程,这种方式被称为核间中断。核间中断的实现基础是多CPU内存共享,采用核间中断可以减少某个CPU负荷过大,有效提升系统效率。目前只有GIC中断控制器与LingLong中断控制器支持核间中断。
中断底半部:
区别于中断处理程序,中断底半部运行在任务上下文,用户可在中断处理程序中,将实时性要求不高,非紧急的业务添加到中断底半部执行。
运作机制
LiteOS的中断嵌套:
GIC与NVIC的中断嵌套由硬件实现。
RISC-V的中断嵌套实现机制:中断嵌套下,中断A触发后会将当前的操作进行压栈,调用中断处理程序前,将MIE设置为1,允许新的中断被响应。在A执行中断处理程序的过程中,如果有更高优先级的中断B被触发,B会将当前的操作即中断A相关的操作进行压栈,然后执行B的中断处理程序。待B的中断处理程序执行完后,会暂时的将mstatus寄存器中的MIE域置为0,关闭中断响应,将中断A相关的操作进行出栈,将MIE设置为1,允许处理器再次响应中断,中断B结束,继续执行中断A。
LiteOS的中断底半部:
中断底半部依赖链表、任务与事件机制。
在中断处理程序中,用户添加底半部处理函数到队列中,待中断退出后,在中断底半部任务中逐个执行用户添加的底半部处理函数。
开发指导¶
使用场景
当有中断请求产生时,CPU暂停当前的任务转而去响应外设请求。根据需要,用户通过中断申请,注册中断处理程序,可以指定CPU响应中断请求时所执行的具体操作。
功能
LiteOS的中断模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建和删除中断 |
LOS_HwiCreate |
中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时,handleIrq会调用该中断处理程序。 |
LOS_HwiDelete |
删除中断,请注意不要在注册的中断 handler中调用此接口,共享模式下,可能会访问非法内存。 |
|
打开和关闭所有中断 |
LOS_IntUnLock |
打开当前处理器所有中断响应。 |
LOS_IntLock |
关闭当前处理器所有中断响应。 |
|
LOS_IntRestore |
恢复到使用LOS_IntLock关闭所有中断之前的状态。 |
|
使能和屏蔽指定中断 |
LOS_HwiDisable |
中断屏蔽(通过设置寄存器,禁止CPU响应该中断)。 |
LOS_HwiEnable |
中断使能(通过设置寄存器,允许CPU响应该中断)。 |
|
设置中断优先级 |
LOS_HwiSetPriority |
设置中断优先级。 |
触发中断 |
LOS_HwiTrigger |
触发中断(通过写中断控制器的相关寄存器模拟外部中断)。 |
清除中断寄存器状态 |
LOS_HwiClear |
清除中断号对应的中断寄存器的状态位,此接口依赖中断控制器版本,非必需。 |
设置中断前处理钩子函数 |
LOS_HwiPreHookReg |
设置中断前处理钩子函数。 |
设置中断后处理钩子函数 |
LOS_HwiPostHookReg |
设置中断后处理钩子函数。 |
获取中断响应次数 |
LOS_HwiRespCntGet |
获取中断的响应次数。 |
添加中断底半部函数 |
LOS_HwiBhworkAdd |
添加中断底半部函数。 |
HWI错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_HWI_NUM_INVALID (OS_ERRNO_HWI_NUM_INVALID) |
0x02000900 |
创建或删除中断时,传入了无效中断号。老版本的错误码OS_ERRNO_HWI_NUM_INVALID后续不再支持。 |
检查中断号,给定有效中断号。 |
2 |
LOS_ERRNO_HWI_PROC_FUNC_NULL (OS_ERRNO_HWI_PROC_FUNC_NULL) |
0x02000901 |
创建中断时,传入的中断处理程序指针为空;如果调用其他接口返回此错误码则表示该接口功能不支持。老版本的错误码OS_ERRNO_HWI_PROC_FUNC_NULL后续不再支持。 |
传入非空中断处理程序指针。 |
3 |
LOS_ERRNO_HWI_NO_MEMORY (OS_ERRNO_HWI_NO_MEMORY) |
0x02000903 |
创建中断时,出现内存不足的情况。老版本的错误码OS_ERRNO_HWI_NO_MEMORY后续不再支持。 |
增大动态内存空间,有两种方式可以实现:
|
4 |
LOS_ERRNO_HWI_ALREADY_CREATED (OS_ERRNO_HWI_ALREADY_CREATED) |
0x02000904 |
创建中断时,发现要注册的中断号已经创建。老版本的错误码OS_ERRNO_HWI_ALREADY_CREATED后续不再支持。 |
对于非共享中断号的情况,检查传入的中断号是否已经被创建;对于共享中断号的情况,检查传入中断号的链表中是否已经有匹配函数参数的设备ID。 |
5 |
LOS_ERRNO_HWI_PRIO_INVALID (OS_ERRNO_HWI_PRIO_INVALID) |
0x02000905 |
创建中断时,传入的中断优先级无效。老版本的错误码OS_ERRNO_HWI_PRIO_INVALID后续不再支持。 |
传入有效中断优先级。优先级有效范围依赖于硬件,外部可配。 |
6 |
LOS_ERRNO_HWI_INTERR (OS_ERRNO_HWI_INTERR) |
0x02000908 |
在中断中调用request_irq接口或bus_request_intr接口。老版本的错误码OS_ERRNO_HWI_INTERR后续不再支持。注意开源版本中没有这两个接口。 |
查看request_irq、bus_request_intr接口的使用是否正确。 |
7 |
LOS_ERRNO_HWI_SHARED_ERROR (OS_ERRNO_HWI_SHARED_ERROR) |
0x02000909 |
创建中断时:发现hwiMode指定创建共享中断,但是未设置设备ID;或hwiMode指定创建非共享中断,但是该中断号之前已创建为共享中断;或配置LOSCFG_SHARED_IRQ为NO,但是创建中断时,入参指定创建共享中断。 删除中断时:设备号创建时指定为共享中断,删除时未设置设备ID,删除错误。老版本的错误码OS_ERRNO_HWI_SHARED_ERROR后续不再支持。 |
检查入参,创建时参数hwiMode与irqParam保持一致,hwiMode为0,表示不共享,此时irqParam应为NULL;当hwiMode为IRQF_SHARD时表示共享,irqParam需设置设备ID;LOSCFG_SHARED_IRQ为NO时,即非共享中断模式下,只能创建非共享中断。删除中断时irqParam要与创建中断时的参数一样。 |
9 |
LOS_ERRNO_HWI_NO_CPUP_MEMORY |
0x0200090C |
使能CPUP开关LOSCFG_CPUP_CB_NUM_CONFIGURABLE后,创建中断时,如果CPUP的中断相关控制块数目不够,会返回此错误。 |
根据实际的中断个数,而非中断最大标号,确定配置宏LOSCFG_CPUP_IRQ_CB_NUM,最大不超过LOSCFG_PLATFORM_HWI_LIMIT。 |
10 |
LOS_ERRNO_HWI_NOT_INTERRUPT_CONTEXT |
0x0200090D |
调用LOS_HwiBhworkAdd时不在中断上下文。 |
在中断上下文中调用LOS_HwiBhworkAdd。 |
11 |
LOS_ERRNO_HWI_PTR_NULL |
0x0200090E |
函数入参指针为空。 |
入参传入有效的非空指针。 |
12 |
LOS_ERRNO_HWI_ARG_NOT_ENABLED |
0x0200090F |
没有开启LOSCFG_HWI_WITH_ARG时,LOS_HwiCreate最后一个参数传入了非空指针。 |
LOS_HwiCreate最后一参数传入NULL或者开启LOSCFG_HWI_WITH_ARG。 |
13 |
LOS_ERRNO_HWI_AFFI_INVALID |
0x02000910 |
传入LOS_HwiSetAffinity时传入了不非法的cpuMask。 |
入参传入有效的cpuMask。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为中断模块,值为0x09。
开发流程
打开菜单,进入Kernel ---> Interrupt Management菜单,完成中断模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_ARCH_INTERRUPT_PREEMPTION
使能中断嵌套
YES/NO
NO
GICv3、RISC-V
LOSCFG_IRQ_USE_STANDALONE_STACK
使用独立中断栈
YES/NO
YES
依赖CPU架构,目前Cortex-A、Cortex-R、ARM64、LingLong支持可选,RISC-V默认使用独立中断栈
LOSCFG_PLATFORM_HWI_LIMIT
最大中断使用数
根据芯片手册适配
根据芯片手册适配
无
LOSCFG_HWI_PRIO_LIMIT
可设置的中断优先级个数
根据芯片手册适配
根据芯片手册适配
无
LOSCFG_IRQ_STACK_SIZE
中断栈大小
根据板子自适配
0x2000
目前只有RISC-V/ARM64可配置
LOSCFG_NMI_STACK_SIZE
不可屏蔽中断栈大小
根据板子自适配
0x800
目前只有RISC-V可配置
LOSCFG_HWI_PRE_POST_PROCESS
中断前后处理钩子函数
YES/NO
NO
无
LOSCFG_HWI_BOTTOM_HALF
使能中断底半部
YES/NO
NO
LOSCFG_BASE_IPC_EVENT
LOSCFG_HWI_BOTTOM_HALF_WORK_LIMIT
中断底半部队列最多缓存的个数
根据板子自适配
16
LOSCFG_HWI_BOTTOM_HALF
LOSCFG_HWI_WITH_ARG
使用带参数的中断创建
YES/NO
YES
LOSCFG_KERNEL_MEM_ALLOC
LOSCFG_ARCH_INTERRUPT_TAKEOVER
开启中断接管
YES/NO
YES
无
调用中断创建接口LOS_HwiCreate创建中断。
调用LOS_HwiEnable接口使能指定中断。
调用LOS_HwiTrigger接口触发指定中断(该接口通过写中断控制器的相关寄存器模拟外部中断,一般的外设设备,不需要执行这一步)。
在中断处理函数中,调用LOS_HwiBhworkAdd接口添加中断底半部处理函数(如果无底半部处理函数,则可以不执行这一步)。
调用LOS_HwiDisable接口屏蔽指定中断,此接口根据实际情况使用,判断是否需要屏蔽中断。
调用LOS_HwiDelete接口删除指定中断,此接口根据实际情况使用,判断是否需要删除中断。
注意事项¶
中断处理:
中断处理程序耗时不能过长,否则会影响CPU对中断的及时响应。
中断响应过程中不能执行引起调度的函数。
中断共享:
中断共享机制,支持不同的设备使用相同的中断号注册同一中断处理程序,但中断处理程序的入参pDevId(设备号)必须唯一,代表不同的设备。即同一中断号,同一dev只能挂载一次;但同一中断号,同一中断处理程序,dev不同则可以重复挂载。
中断触发:
RISC-V himideerV200不支持LOS_HwiTrigger接口,因为RISC-V himideerV200中断控制器pending寄存器不可写;用户需要模拟中断触发进行测试,请使用软中断或外设中断验证中断触发流程。
中断寄存器状态清除:
RISC-V himideerV200开启中断嵌套时,对于电平触发方式,需要在中断处理程序结束时调用LOS_HwiClear接口,主动清除中断寄存器状态。即对于RISC-V himideerV200,LOS_HwiClear接口只有在中断嵌套场景生效;非嵌套场景用户无需清除中断控制器状态,LOS_HwiClear接口无清除中断状态效果。
中断底半部:
中断底半部用户处理函数中禁止调用延时接口,避免调用阻塞类接口。中断底半部任务运行时机具有不确定性,只有当中断底半部任务优先级最高,才会运行。
兼容接口:
LiteOS有兼容Linux的中断接口,request_irq、disable_irq、enable_irq、free_irq,详见“中断”。开发者可根据自身需要,选择适合的接口。
其他限制:
根据具体硬件,配置支持的最大中断数及可设置的中断优先级个数。
中断恢复LOS_IntRestore()的入参必须是与之对应的LOS_IntLock()的返回值(即关中断之前的CPSR值)。
Cortex-M系列处理器中0-15中断为内部使用,Cortex-A7中0-31中断为内部使用,因此不建议用户去申请和创建。
编程实例¶
实例描述
本实例实现如下功能:
创建中断。
设置中断亲和性。
使能中断。
触发中断。
屏蔽中断。
删除中断。
编程示例
前提条件:menuconfig菜单中配置中断使用最大数、配置可设置的中断优先级个数。
代码实现如下:
#include "los_hwi.h"
#include "los_typedef.h"
#include "los_task.h"
STATIC UINT32 g_BhworkCnt = 0;
STATIC VOID HwiBhfunc(VOID *data)
{
UINT32 *count = (UINT32 *data);
(*count)++;
}
STATIC VOID HwiUsrIrq(VOID)
{
UINT32 ret;
ret = LOS_HwiBhworkAdd(HwiBhfunc, &g_BhworkCnt);
if (ret != LOS_OK) {
printf("\n hwi bhwork add failed, %x \n", ret);
}
printf("\n in the func HwiUsrIrq \n");
}
/* cpu0 trigger, cpu0 response */
UINT32 It_Hwi_001(VOID)
{
UINT32 ret;
HWI_HANDLE_T irqNum = 26;
HWI_PRIOR_T irqPri = 0x3;
ret = LOS_HwiCreate(irqNum, irqPri, 0, (HWI_PROC_FUNC)HwiUsrIrq, 0);
if (ret != LOS_OK) {
return LOS_NOK;
}
#ifdef LOSCFG_KERNEL_SMP
ret = LOS_HwiSetAffinity(irqNum, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
if (ret != LOS_OK) {
return LOS_NOK;
}
#endif
ret = LOS_HwiEnable(irqNum);
if (ret != LOS_OK) {
return LOS_NOK;
}
ret = LOS_HwiTrigger(irqNum);
if (ret != LOS_OK) {
return LOS_NOK;
}
LOS_TaskDelay(1);
ret = LOS_HwiDisable(irqNum);
if (ret != LOS_OK) {
return LOS_NOK;
}
ret = LOS_HwiDelete(irqNum, NULL);
if (ret != LOS_OK) {
return LOS_NOK;
}
return LOS_OK;
}
异常接管¶
概述¶
基本概念
异常接管是操作系统对运行期间发生的异常情况(芯片硬件异常)进行处理的一系列动作,例如打印异常发生时当前函数的调用栈信息、CPU现场信息、任务的堆栈情况等。
异常接管作为一种调测手段,可以在系统发生异常时给用户提供有用的异常信息,譬如异常类型、发生异常时的系统状态等,方便用户定位分析问题。
LiteOS的异常接管,在系统发生异常时的处理动作为:显示异常发生时正在运行的任务信息(包括任务名、任务号、堆栈大小等)以及CPU现场等信息。针对某些RISC-V架构的芯片,对内存size要求较高的场景,LiteOS提供了极小特性宏LOSCFG_EXC_SIMPLE_INFO(menuconfig菜单项为:Kernel ---> Exception Management ---> Enable Exception Simple Info),用于裁剪多余的异常提示字符串信息,但是仍然保留发生异常时的CPU执行环境的所有信息。
运作机制
每个函数都有自己的栈空间,称为栈帧。调用函数时,会创建子函数的栈帧,同时将函数入参、局部变量、寄存器入栈。栈帧从高地址向低地址生长。
以RISC-V 32位架构为例,每个栈帧中通常保存了返回地址(RA)、栈指针(SP)和帧指针(FP)的历史值,用于回溯函数调用关系。
堆栈分析:
RA寄存器(x1):返回地址寄存器,指向当前函数返回后要执行的下一条指令地址。
SP寄存器(x2):栈指针寄存器,指向当前栈帧的栈顶。
FP寄存器(x8):可用作帧指针寄存器(frame pointer),在编译时开启特定选项后,它指向当前函数的父函数栈帧的起始地址。RISC-V中x8也被称为s0或fp。
默认情况下,GCC编译器可能会将x8作为通用寄存器使用(例如用于保存局部变量),此时无法直接通过FP回溯栈帧。为了支持调用栈解析,需要在编译参数中添加“-fno-omit-frame-pointer”,强制编译器将x8用作帧指针。这样,每个函数入口会保存上一帧的FP和RA,形成链式结构。
当系统发生异常时,CPU会打印异常现场的寄存器值(包括ra、sp、fp 等)。通过当前栈帧中保存的fp可以找到父函数的栈帧,进而从父栈帧中取出其保存的ra(即父函数的返回地址)以及它的fp,如此迭代,即可追溯出完整的函数调用栈,帮助开发者快速定位异常原因。
堆栈分析原理如下所示,实际堆栈信息根据不同CPU架构有所差异,此处仅做示意。
假设函数调用链:main函数调用func1,func1又调用func2。此时正在执行func2,其栈帧如下(地址从高到低,每格4字节):
图 1 堆栈分析原理示意图

关键点说明:
sp(栈指针):始终指向当前栈帧的最低地址(即最后分配的空间)。在func2执行时,sp指向func2局部变量下方的空闲区域(或已占用的底部)。
fp(帧指针):指向当前栈帧中保存上一帧fp的位置。例如,func2的fp指向的地址存放着func1的fp值,而 func1的fp又指向main的fp位置,从而形成链表。
返回地址ra:每个函数在调用子函数前,会将当前的ra保存到自己的栈帧中(通常位于保存的fp上方4字节处,如图1中func2的ra在fp+4位置)。这样,当函数返回时,可以从栈帧中恢复ra。
异常回溯过程:
当发生异常时,CPU会保存当前寄存器值(包括sp、fp、ra 等)。假设当前在func2中发生异常,我们可以通过以下步骤回溯调用栈:
从当前fp指向的地址读取内容,得到上一帧的fp(即func1的fp值)。
从当前fp + 4地址读取内容,得到当前函数func2的返回地址(即func1中调用func2的下一条指令地址)。
将上一步得到的fp作为新的当前fp,重复步骤3.a和3.b,即可依次获得func1的返回地址(即main中调用func1的下一条指令)和main的fp,直到fp为0或无效值。
这种链式结构让调试器和异常处理程序能够精确还原函数调用序列,快速定位问题发生的上下文。
开发指导¶
使用场景
异常接管对系统运行期间发生的芯片硬件异常进行处理,不同芯片的异常类型存在差异,具体异常类型可以查看芯片手册。
功能
LiteOS的异常模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
暂停核 |
LOS_Panic |
暂停当前核运行并打印信息。 |
栈回溯 |
LOS_BackTrace |
打印当前任务的栈回溯信息。 |
LOS_TaskBackTrace |
打印指定任务的栈回溯信息。 |
定位流程
异常接管一般的定位步骤如下:
打开编译后生成的镜像反汇编(asm)文件。
搜索PC指针(指向当前正在执行的指令)在asm中的位置,找到发生异常的函数。
根据LR值查找异常函数的父函数。
重复3,得到函数间的调用关系,找到异常原因。
具体的定位方法会在实例中举例说明。
配置项
打开菜单,进入Kernel ---> Exception Management菜单,完成异常接管模块的配置。
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_EXC_SIMPLE_INFO |
使能后将减少异常输出的信息 |
YES/NO |
NO |
无 |
LOSCFG_EXC_STACK_SIZE |
异常栈大小 |
根据板子自适配 |
0x800 |
目前只有RISC-V可配置 |
LOSCFG_SVC_STACK_SIZE |
系统调用(SVC)栈大小 |
根据板子自适配 |
0x2000 |
LOSCFG_ARCH_ARM_CORTEX_A 或 LOSCFG_ARCH_ARM_CORTEX_R 或 LOSCFG_ARCH_ARM_926 |
注意事项¶
要查看调用栈信息,必须添加编译选项宏“-fno-omit-frame-pointer”支持stack frame,否则编译时FP寄存器是关闭的。
用户可通过调用ArchSetExcHook和ArchSetNMIHook接口来设定自定义的异常处理函数。然而,在异常处理函数内部必须使用LOS_TaskLock锁定任务调度机制,以避免因触发调度而导致二次异常或其他潜在问题的发生。
编程实例¶
通过错误释放内存,触发系统异常。系统异常被挂起后,能在串口中看到异常调用栈打印信息和关键寄存器信息,如下所示,其中Type表示异常类型,此处值为0x2表示非法指令 ,其它数值可以查看芯片手册。通过这些信息可以定位到异常所在函数和其调用栈关系。

定位步骤如下:
打开编译后生成的.asm 反汇编文件(默认生成在“LiteOS/out/<platform>“”目录下,其中的platform为具体的平台名)。
搜索mepc指针2616ecfa在.asm文件中的位置(去掉0x)。
mepc地址指向发生异常时程序正在执行的指令。在当前执行的二进制文件对应的asm文件中,查找mepc值2616ecfa,找到当前发生异常的文件,得到如下图所示结果。

从图中可以看到:
异常时CPU正在执行的指令unimp。
异常发生在“It_los_task_045.c”文件的“__asm volatile (".word 0x00000000")”语句。
接下来,需要查找谁调用了这个语句。
根据ra返回寄存器值查找调用栈。
从异常信息的backtrace begin开始,打印的是调用栈信息。在asm文件中查找traceback 0~2对应的ra,如下图所示。



可见,是FuncC调用了“__asm volatile (".word 0x00000000")”语句发生异常。依此方法,可得到异常时函数调用关系如下:FuncA(业务函数) ---> FuncB---> FuncC ---> __asm volatile (".word 0x00000000")。
最终,可以通过该方法排查出整个异常调用栈的信息。
错误处理¶
概述¶
基本概念
错误处理指程序运行错误时,调用错误处理模块的接口函数,上报错误信息,并调用注册的钩子函数进行特定处理,保存现场以便定位问题。
通过错误处理,可以控制和提示程序中的非法输入,防止程序崩溃。
运作机制
错误处理是一种机制,用于处理异常状况。当程序出现错误时,会显示相应的错误码。此外,如果注册了相应的错误处理函数,则会执行这个函数。
图 1 错误处理示意图

开发指导¶
错误码简介
调用API接口时可能会出现错误,此时接口会返回对应的错误码,以便快速定位错误原因。
错误码是一个32位的无符号整型数,31~24位表示错误等级,23~16位表示错误码标志(当前该标志值为0),15~8位代表错误码所属模块,7~0位表示错误码序号。
例如将任务模块中的错误码LOS_ERRNO_TSK_NO_MEMORY定义为致命级别的错误,模块ID为LOS_MOD_TSK,错误码序号为0,其定义如下:
#define LOS_ERRNO_TSK_NO_MEMORY LOS_ERRNO_OS_FATAL(LOS_MOD_TSK, 0x00)
#define LOS_ERRNO_OS_FATAL(MID, ERRNO) \
(LOS_ERRTYPE_FATAL | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | ((UINT32)(ERRNO)))
说明:
LOS_ERRTYPE_FATAL:错误等级为致命,值为0x03000000。
LOS_ERRNO_OS_ID:错误码标志,值为0x000000。
MID:所属模块,LOS_MOD_TSK的值为0x2。
ERRNO:错误码序号。 所以LOS_ERRNO_TSK_NO_MEMORY的值为0x03000200。
错误码接管
有时只靠错误码不能快速准确的定位问题,为方便用户分析错误,错误处理模块支持注册错误处理的钩子函数,发生错误时,用户可以调用LOS_ErrHandle接口以执行错误处理函数。
LiteOS的错误处理模块为用户提供下面几个接口,接口详细信息请参见API参考。
接口名 |
描述 |
参数 |
备注 |
|---|---|---|---|
LOS_RegErrHandle |
注册错误处理钩子函数。 |
func:错误处理钩子函数 |
- |
LOS_ErrHandle |
调用钩子函数,处理错误。 |
fileName:存放错误日志的文件名 |
系统内部调用时,入参为“os_unspecific_file”。 |
lineNo:发生错误的代码行号 |
系统内部调用时,若值为0xA1B2C3F8,表示未传递行号。 |
||
errnoNo:错误码 |
- |
||
paraLen:入参para的长度 |
系统内部调用时,入参为0。 |
||
para:错误标签 |
系统内部调用时,入参为NULL。 |
须知: 系统内部会在某些难以定位的错误处,主动调用注册的钩子函数(目前只在互斥锁模块和信号量模块中主动调用了钩子函数)。
注意事项¶
系统中有且仅有一个错误处理的钩子函数。当多次注册钩子函数时,最后一次注册的钩子函数会覆盖前一次注册的函数。
编程实例¶
实例描述
本实例演示功能如下:
注册错误处理钩子函数。
执行错误处理函数。
编程示例
代码实现如下:
#include "los_err.h"
#include "los_typedef.h"
#include <stdio.h>
void Test_ErrHandle(CHAR *fileName, UINT32 lineNo, UINT32 errorNo, UINT32 paraLen, VOID *para)
{
printf("err handle ok\n");
}
static UINT32 TestCase(VOID)
{
UINT32 errNo = 0;
UINT32 ret;
UINT32 errLine = 16;
LOS_RegErrHandle(Test_ErrHandle);
ret = LOS_ErrHandle("os_unspecific_file", errLine, errNo, 0, NULL);
if (ret != LOS_OK) {
return LOS_NOK;
}
return LOS_OK;
}
结果验证
编译运行得到的结果为:
HuaWei LiteOS # err handle ok
队列¶
概述¶
基本概念
队列又称消息队列,是一种常用于任务间通信的数据结构。队列接收来自任务或中断的不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。
任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。如果将读队列和写队列的超时时间设置为0,则不会挂起任务,接口会直接返回,这就是非阻塞模式。
消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用。
LiteOS中使用队列实现任务异步通信,具有如下特性:
消息以先进先出的方式排队,支持异步读写。
读队列和写队列都支持超时机制。
每读取一条消息,就会将该消息节点设置为空闲。
发送消息类型由通信双方约定,可以允许不同长度(不超过队列的消息节点大小)的消息。
一个任务能够从任意一个消息队列接收和发送消息。
多个任务能够从同一个消息队列接收和发送消息。
创建队列时所需的队列空间,默认支持接口内系统自行动态申请内存的方式,同时也支持将用户分配的队列空间作为接口入参传入的方式。
运作机制
队列控制块:
typedef enum {
OS_QUEUE_READ =0,
OS_QUEUE_WRITE =1,
OS_QUEUE_N_RW =2
} QueueReadWrite;
/**
* Queue information block structure
*/
typedef struct
{
UINT8 *queueHandle; /* 队列指针 */
UINT8 queueState; /* 队列状态 */
UINT8 queueMemType; /* 创建队列时内存分配的方式 */
UINT16 queueLen; /* 队列中消息节点个数,即队列长度 */
UINT16 queueSize; /* 消息节点大小 */
UINT32 queueId; /* 队列ID */
UINT16 queueHead; /* 消息头节点位置(数组下标)*/
UINT16 queueTail; /* 消息尾节点位置(数组下标)*/
UINT16 readWriteableCnt[OS_QUEUE_N_RW]; /* 数组下标0的元素表示队列中可读消息数,
数组下标1的元素表示队列中可写消息数 */
LOS_DL_LIST readWriteList[OS_QUEUE_N_RW]; /* 读取或写入消息的任务等待链表,
下标0:读取链表,下标1:写入链表 */
LOS_DL_LIST memList; /* CMSIS-RTOS中的MailBox模块使用的内存块链表 */
} LosQueueCB;
每个队列控制块中都含有队列状态,表示该队列的使用情况:
OS_QUEUE_UNUSED:队列没有被使用。
OS_QUEUE_INUSED:队列被使用中。
每个队列控制块中都含有创建队列时的内存分配方式:
OS_QUEUE_ALLOC_DYNAMIC:创建队列时所需的队列空间,由系统自行动态申请内存获取。
OS_QUEUE_ALLOC_STATIC:创建队列时所需的队列空间,由接口调用者自行申请后传入接口。
队列运作原理
表 1 队列运作原理描述表
功能 |
运作原理描述 |
|---|---|
创建队列 |
创建队列成功会返回队列ID。 在队列控制块中维护着一个消息头节点位置Head和一个消息尾节点位置Tail,用于表示当前队列中消息的存储情况。
(注:队列刚创建时,Head和Tail均指向队列起始位置。) |
写队列 |
根据readWriteableCnt[1]判断队列是否可以写入,不能对已满(readWriteableCnt[1]为0)队列进行写操作。 写队列支持以下2种写入方式。
|
读队列 |
根据readWriteableCnt[0]判断队列是否有消息需要读取,对全部空闲(readWriteableCnt[0]为0)队列进行读操作会引起任务挂起。
|
删除队列 |
根据队列ID找到对应队列,把队列状态置为未使用,把队列控制块置为初始状态。如果是通过系统动态申请内存方式创建的队列,还会释放队列所占内存。 |
图 1 队列读写数据操作示意图

图1对读写队列做了示意,图中只画了尾节点写入方式,没有画头节点写入,但是两者是类似的。
开发指导¶
使用场景
队列用于任务间通信,可以实现消息的异步处理。同时消息的发送方和接收方不需要彼此联系,两者间是解耦的。
功能
LiteOS中的队列模块提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建/删除消息队列 |
LOS_QueueCreate |
创建一个消息队列,由系统动态申请队列空间。 |
LOS_QueueCreateStatic |
创建一个消息队列,由用户分配队列内存空间传入接口。 |
|
LOS_QueueDelete |
根据队列ID删除一个指定队列。 |
|
读/写队列(不带拷贝) |
LOS_QueueRead |
读取指定队列头节点中的数据(队列节点中的数据实际上是一个地址)。 |
LOS_QueueWrite |
向指定队列尾节点中写入入参bufferAddr的值(即buffer的地址)。 |
|
LOS_QueueWriteHead |
向指定队列头节点中写入入参bufferAddr的值(即buffer的地址)。 |
|
读/写队列(带拷贝) |
LOS_QueueReadCopy |
读取指定队列头节点中的数据。 |
LOS_QueueWriteCopy |
向指定队列尾节点中写入入参bufferAddr中保存的数据。 |
|
LOS_QueueWriteHeadCopy |
向指定队列头节点中写入入参bufferAddr中保存的数据。 |
|
获取队列信息 |
LOS_QueueInfoGet |
获取指定队列的信息,包括队列ID、队列长度、消息节点大小、头节点、尾节点、可读节点数量、可写节点数量、等待读操作的任务、等待写操作的任务、等待mail操作的任务。 |
获取全量队列信息 |
LOS_QueueInfoAllGet |
功能与LOS_QueueInfoGet相同,相比与LOS_QueueInfoGet接口,支持任务数大于64的场景。 |
队列错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_QUEUE_NO_MEMORY |
0x02000601 |
队列初始化时,从动态内存池申请内存失败。 |
设置更大的系统动态内存池,配置项为OS_SYS_MEM_SIZE,或减少系统支持的最大队列数。 |
2 |
LOS_ERRNO_QUEUE_CREATE_NO_MEMORY |
0x02000602 |
创建队列时,从动态内存池申请内存失败 |
设置更大的系统动态内存池,配置项为OS_SYS_MEM_SIZE,或减少要创建队列的队列长度和消息节点大小。 |
3 |
LOS_ERRNO_QUEUE_SIZE_TOO_BIG |
0x02000603 |
创建队列时消息节点大小超过上限。 |
更改入参消息节点大小,使之不超过上限。 |
4 |
LOS_ERRNO_QUEUE_CB_UNAVAILABLE |
0x02000604 |
创建队列时,系统中已经没有空闲队列。 |
增加系统支持的最大队列数。 |
5 |
LOS_ERRNO_QUEUE_NOT_FOUND |
0x02000605 |
传递给删除队列接口的队列ID大于等于系统支持的最大队列数。 |
确保队列ID是有效的。 |
6 |
LOS_ERRNO_QUEUE_PEND_IN_LOCK |
0x02000606 |
当任务被锁定时,禁止在队列中阻塞等待写消息或读消息。 |
使用队列前解锁任务。 |
7 |
LOS_ERRNO_QUEUE_TIMEOUT |
0x02000607 |
等待处理队列超时。 |
检查设置的超时时间是否合适。 |
8 |
LOS_ERRNO_QUEUE_IN_TSKUSE |
0x02000608 |
队列存在阻塞任务而不能被删除。 |
使任务能够获得资源而不是在队列中被阻塞。 |
9 |
LOS_ERRNO_QUEUE_WRITE_IN_INTERRUPT |
0x02000609 |
在中断处理程序中不能以阻塞模式写队列。 |
将写队列设为非阻塞模式,即将写队列的超时时间设置为0。 |
10 |
LOS_ERRNO_QUEUE_NOT_CREATE |
0x0200060A |
队列未创建。 |
创建该队列,或更换为一个已经创建的队列。 |
11 |
LOS_ERRNO_QUEUE_IN_TSKWRITE |
0x0200060B |
队列读写不同步。 |
同步队列的读写,即多个任务不能并发读写同一个队列。 |
12 |
LOS_ERRNO_QUEUE_CREAT_PTR_NULL |
0x0200060C |
对于创建队列接口,保存队列ID的入参为空指针。 |
确保传入的参数不为空指针。 |
13 |
LOS_ERRNO_QUEUE_PARA_ISZERO |
0x0200060D |
对于创建队列接口,入参队列长度或消息节点大小为0。 |
传入正确的队列长度和消息节点大小。 |
14 |
LOS_ERRNO_QUEUE_INVALID |
0x0200060E |
传递给读队列或写队列或获取队列信息接口的队列ID大于等于系统支持的最大队列数。 |
确保队列ID有效。 |
15 |
LOS_ERRNO_QUEUE_READ_PTR_NULL |
0x0200060F |
传递给读队列接口的指针为空。 |
确保传入的参数不为空指针。 |
16 |
LOS_ERRNO_QUEUE_READSIZE_IS_INVALID |
0x02000610 |
传递给读队列接口的缓冲区大小为0或者大于0xFFFB。 |
传入的一个正确的缓冲区大小需要大于0且小于0xFFFC。 |
17 |
LOS_ERRNO_QUEUE_WRITE_PTR_NULL |
0x02000612 |
传递给写队列接口的缓冲区指针为空 |
确保传入的参数不为空指针 |
18 |
LOS_ERRNO_QUEUE_WRITESIZE_ISZERO |
0x02000613 |
传递给写队列接口的缓冲区大小为0。 |
传入正确的缓冲区大小。 |
19 |
LOS_ERRNO_QUEUE_WRITE_SIZE_TOO_BIG |
0x02000615 |
传递给写队列接口的缓冲区大小比队列的消息节点大小要大。 |
减小缓冲区大小,或增大队列的消息节点大小。 |
20 |
LOS_ERRNO_QUEUE_ISFULL |
0x02000616 |
写队列时没有可用的空闲节点。 |
写队列之前,确保在队列中存在可用的空闲节点,或者使用阻塞模式写队列,即设置大于0的写队列超时时间。 |
21 |
LOS_ERRNO_QUEUE_PTR_NULL |
0x02000617 |
传递给获取队列信息接口的指针为空。 |
确保传入的参数不为空指针。 |
22 |
LOS_ERRNO_QUEUE_READ_IN_INTERRUPT |
0x02000618 |
在中断处理程序中不能以阻塞模式读队列。 |
将读队列设为非阻塞模式,即将读队列的超时时间设置为0。 |
23 |
LOS_ERRNO_QUEUE_MAIL_HANDLE_INVALID |
0x02000619 |
CMSIS-RTOS 1.0中的mail队列,释放内存块时,发现传入的mail队列ID无效。 |
确保传入的mail队列ID是正确的。 |
24 |
LOS_ERRNO_QUEUE_MAIL_PTR_INVALID |
0x0200061A |
CMSIS-RTOS 1.0中的mail队列,释放内存块时,发现传入的mail内存池指针为空。 |
传入非空的mail内存池指针。 |
25 |
LOS_ERRNO_QUEUE_MAIL_FREE_ERROR |
0x0200061B |
CMSIS-RTOS 1.0中的mail队列,释放内存块失败。 |
传入非空的mail队列内存块指针。 |
26 |
LOS_ERRNO_QUEUE_ISEMPTY |
0x0200061D |
队列已空。 |
读队列之前,确保队列中存在未读的消息,或者使用阻塞模式读队列,即设置大于0的读队列超时时间。 |
27 |
LOS_ERRNO_QUEUE_READ_SIZE_TOO_SMALL |
0x0200061F |
传递给读队列接口的读缓冲区大小小于队列消息节点大小。 |
增加缓冲区大小,或减小队列消息节点大小。 |
28 |
LOS_ERRNO_QUEUE_INFO_SIZE_TOO_SMALL |
0x02000620 |
读取队列信息时传入的缓冲区大小小于要存储的信息大小。 |
增加缓冲区大小,或减少要获取的任务数量。 |
须知:
错误码定义见“错误码简介”。8~15位代表的所属模块为队列模块,值为0x06。
队列模块中的错误码序号0x11、0x14未被定义,不可用。
开发流程
使用队列模块的典型流程如下:
打开菜单,进入Kernel ---> Enable Queue菜单,完成队列模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_IPC_QUEUE
队列模块裁剪开关
YES/NO
YES
无
LOSCFG_QUEUE_STATIC_ALLOCATION
支持以用户分配内存的方式创建队列
YES/NO
NO
LOSCFG_BASE_IPC_QUEUE
LOSCFG_BASE_IPC_QUEUE_LIMIT
系统支持的最大队列数
<65535>
1024
LOSCFG_BASE_IPC_QUEUE
LOSCFG_QUEUE_DYNAMIC_ALLOCATION
支持创建队列时,动态分配内存
YES/NO
YES
LOSCFG_KERNEL_MEM_ALLOC
LOSCFG_QUEUE_PEAK_STAT
支持队列峰值统计
YES/NO
NO
无
创建队列。创建成功后,可以得到队列ID。
写队列。
读队列。
获取队列信息。
删除队列。
注意事项¶
队列创建:
系统支持的最大队列数是指:整个系统的队列资源总个数,而非用户能使用的个数。例如:系统软件定时器多占用一个队列资源,那么用户能使用的队列资源就会减少一个。
创建队列时传入的队列名和flags暂时未使用,作为以后的预留参数。
队列读写:
LOS_QueueReadCopy和LOS_QueueWriteCopy及LOS_QueueWriteHeadCopy是一组接口,LOS_QueueRead和LOS_QueueWrite及LOS_QueueWriteHead是一组接口,两组接口需要配套使用。
鉴于LOS_QueueWrite和LOS_QueueWriteHead和LOS_QueueRead这组接口实际操作的是数据地址,用户必须保证调用LOS_QueueRead获取到的指针所指向的内存区域在读队列期间没有被异常修改或释放,否则可能导致不可预知的后果。
鉴于LOS_QueueWrite和LOS_QueueWriteHead和LOS_QueueRead这组接口实际操作的是数据地址,也就意味着实际写和读的消息长度仅仅是一个指针数据,因此用户使用这组接口之前,需确保创建队列时的消息节点大小为一个指针的长度,避免不必要的浪费和读取失败。
其他:
队列接口函数中的入参timeout是相对时间。
当队列使用结束后,如果存在动态申请的内存,需要及时释放这些内存。
编程实例¶
实例描述
创建一个队列,两个任务。任务1调用写队列接口发送消息,任务2通过读队列接口接收消息。
通过LOS_TaskCreate创建任务1和任务2。
通过LOS_QueueCreate创建一个消息队列。
在任务1 send_Entry中发送消息。
在任务2 recv_Entry中接收消息。
通过LOS_QueueDelete删除队列。
编程示例
前提条件:在menuconfig菜单中完成队列模块的配置。
#include "los_task.h"
#include "los_queue.h"
static UINT32 g_queue;
#define BUFFER_LEN 50
VOID send_Entry(VOID)
{
UINT32 i = 0;
UINT32 ret = 0;
CHAR abuf[] = "test is message x";
UINT32 len = sizeof(abuf);
while (i < 5) {
abuf[len -2] = '0' + i;
i++;
ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0);
if(ret != LOS_OK) {
dprintf("send message failure, error: %x\n", ret);
}
LOS_TaskDelay(5);
}
}
VOID recv_Entry(VOID)
{
UINT32 ret = 0;
CHAR readBuf[BUFFER_LEN] = {0};
UINT32 readLen = BUFFER_LEN;
while (1) {
ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0);
if(ret != LOS_OK) {
dprintf("recv message failure, error: %x\n", ret);
break;
}
dprintf("recv message: %s\n", readBuf);
LOS_TaskDelay(5);
}
while (LOS_OK != LOS_QueueDelete(g_queue)) {
LOS_TaskDelay(1);
}
dprintf("delete the queue success!\n");
}
UINT32 Example_CreateTask(VOID)
{
UINT32 ret = 0;
UINT32 task1, task2;
TSK_INIT_PARAM_S initParam;
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)send_Entry;
initParam.usTaskPrio = 9;
initParam.uwStackSize = LOS_TASK_MIN_STACK_SIZE;
initParam.pcName = "sendQueue";
#ifdef LOSCFG_KERNEL_SMP
initParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endif
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
LOS_TaskLock();
ret = LOS_TaskCreate(&task1, &initParam);
if(ret != LOS_OK) {
dprintf("create task1 failed, error: %x\n", ret);
return ret;
}
initParam.pcName = "recvQueue";
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)recv_Entry;
ret = LOS_TaskCreate(&task2, &initParam);
if(ret != LOS_OK) {
dprintf("create task2 failed, error: %x\n", ret);
return ret;
}
ret = LOS_QueueCreate("queue", 5, &g_queue, 0, BUFFER_LEN);
if(ret != LOS_OK) {
dprintf("create queue failure, error: %x\n", ret);
}
dprintf("create the queue success!\n");
LOS_TaskUnlock();
return ret;
}
结果验证
编译运行得到的结果为:
create the queue success!
recv message: test is message 0
recv message: test is message 1
recv message: test is message 2
recv message: test is message 3
recv message: test is message 4
recv message failure, error: 200061d
delete the queue success!
事件¶
概述¶
基本概念
事件(Event)是一种任务间通信的机制,可用于任务间的同步。
多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步。事件可以提供一对多、多对多的同步操作。
一对多同步模型:一个任务等待多个事件的触发。可以是任意一个事件发生时唤醒任务处理事件,也可以是几个事件都发生后才唤醒任务处理事件。
多对多同步模型:多个任务等待多个事件的触发。
LiteOS提供的事件具有如下特点:
任务通过创建事件控制块来触发事件或等待事件。
事件间相互独立,内部通过一个32位无符号整型来实现,每一位标识一种事件类型。第25位不可用,因此最多可支持31种事件类型。
事件仅用于任务间的同步,不支持数据传输。
若多次向事件控制块写入同一事件类型,在被清零前等效于只写入一次。
多个任务可以对同一事件进行读写操作。
支持事件读写超时机制。
事件控制块
/**
* Event control structure
*/
typedef struct tagEvent {
UINT32 uwEventID; /* 事件ID,每一位标识一种事件类型 */
LOS_DL_LIST stEventList; /* 读取事件的任务链表 */
} EVENT_CB_S, *PEVENT_CB_S;
uwEventID:用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生),一共31种事件类型。(第25位系统保留。)
事件读取模式
在读事件时,可以选择读取模式。读取模式如下:
所有事件(LOS_WAITMODE_AND):逻辑与,基于接口传入的事件类型掩码eventMask,只有这些事件都已经发生才能读取成功,否则该任务将阻塞等待或者返回错误码。
任一事件(LOS_WAITMODE_OR):逻辑或,基于接口传入的事件类型掩码eventMask,只要这些事件中有任一种事件发生就可以读取成功,否则该任务将阻塞等待或者返回错误码。
清除事件(LOS_WAITMODE_CLR):这是一种附加读取模式,需要与所有事件模式或任一事件模式结合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或 LOS_WAITMODE_OR | LOS_WAITMODE_CLR)。在这种模式下,当设置的所有事件模式或任一事件模式读取成功后,会自动清除事件控制块中对应的事件类型位。
运作机制
任务在调用LOS_EventRead接口读事件时,可以根据入参事件掩码类型eventMask读取事件的单个或者多个事件类型。事件读取成功后,如果设置LOS_WAITMODE_CLR会清除已读取到的事件类型,反之不会清除已读到的事件类型,需显式清除。可以通过入参选择读取模式,读取事件掩码类型中所有事件还是读取事件掩码类型中任意事件。
任务在调用LOS_EventWrite接口写事件时,对指定事件控制块写入指定的事件类型,可以一次同时写多个事件类型。写事件会触发任务调度。
任务在调用LOS_EventClear接口清除事件时,根据入参事件和待清除的事件类型,对事件对应位进行清零操作。
图 1 事件唤醒任务示意图

开发指导¶
使用场景
事件可应用于多种任务同步场景,在某些同步场景下可替代信号量。
功能
LiteOS的事件模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化事件 |
LOS_EventInit |
初始化一个事件控制块。 |
读/写事件 |
LOS_EventRead |
读取指定事件类型,超时时间为相对时间:单位为Tick。 |
LOS_EventWrite |
写指定的事件类型。 |
|
LOS_EventCondRead |
读取满足指定条件的事件,超时时间为相对时间:单位为Tick,与LOS_EventCondWrite成对使用。 |
|
LOS_EventCondWrite |
写指定的事件,与LOS_EventCondRead成对使用。 |
|
清除事件 |
LOS_EventClear |
清除指定的事件类型。 |
校验事件掩码 |
LOS_EventPoll |
根据用户传入的事件ID、事件掩码及读取模式,返回用户传入的事件是否符合预期。 |
销毁事件 |
LOS_EventDestroy |
销毁指定的事件控制块。 |
Event错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_EVENT_SETBIT_INVALID |
0x02001C00 |
写事件时,将事件ID的第25个bit设置为1。这个比特位OS内部保留,不允许设置为1。 |
事件ID的第25bit置为0。 |
2 |
LOS_ERRNO_EVENT_READ_TIMEOUT |
0x02001C01 |
读事件超时。 |
增加等待时间或者重新读取。 |
3 |
LOS_ERRNO_EVENT_EVENTMASK_INVALID |
0x02001C02 |
入参的事件ID是无效的。 |
传入有效的事件ID参数。 |
4 |
LOS_ERRNO_EVENT_READ_IN_INTERRUPT |
0x02001C03 |
在中断中读取事件。 |
启动新的任务来获取事件。 |
5 |
LOS_ERRNO_EVENT_FLAGS_INVALID |
0x02001C04 |
读取事件的mode无效。 |
传入有效的mode参数。 |
6 |
LOS_ERRNO_EVENT_READ_IN_LOCK |
0x02001C05 |
任务锁住,不能读取事件。 |
解锁任务,再读取事件。 |
7 |
LOS_ERRNO_EVENT_PTR_NULL |
0x02001C06 |
传入的参数为空指针。 |
传入非空入参。 |
8 |
LOS_ERRNO_EVENT_SHOULD_NOT_DESTORY |
0x02001C08 |
事件链表上仍有任务,无法被销毁。 |
先检查事件链表是否为空。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为事件模块,值为0x1C。
开发流程
使用事件模块的典型流程如下:
打开菜单,进入Kernel ---> Enable Event菜单,完成事件模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_IPC_EVENT
事件功能的裁剪开关
YES/NO
YES
无
调用事件初始化LOS_EventInit接口,初始化事件等待队列。
写事件LOS_EventWrite或LOS_EventCondWrite,写入指定的事件类型或事件。
读事件LOS_EventRead或LOS_EventCondRead,读取指定的事件类型或条件事件。
清除事件LOS_EventClear,清除指定的事件类型,该接口只适用于事件类型的事件。
销毁事件LOS_EventDestroy。
平台差异性
无。
注意事项¶
在系统初始化之前不能调用读写事件接口。如果调用,系统会运行异常。
在中断中,可以对事件对象进行写操作,但不能进行读操作。
在锁定任务调度状态下,禁止任务阻塞于读事件。
为了区别LOS_EventRead接口返回的是事件还是错误码,事件掩码的第25位不能使用。
LOS_EventClear入参值是要清除的指定事件类型的反码(~events)。
LOS_EventWrite和LOS_EventRead成对使用,LOS_EventCondWrite和LOS_EventCondRead成对使用,这两组接口的区别在于,前面的用于读取或写入指定事件类型的事件;而后面的一组接口,用于读取或写入指定事件条件的事件,事件条件由用户实现,写事件任务不用关心事件条件,而是广播式告知读事件任务,即唤醒读任务,读任务判断读取的事件条件是否成功,这样可能会带来一定的性能开销,但是可简化写事件任务。
编程实例¶
基于事件类型的读写¶
实例描述
示例中,任务Example_TaskEntry创建一个任务Example_Event,Example_Event读事件阻塞,Example_TaskEntry向该任务写事件。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。
在任务Example_TaskEntry创建任务Example_Event,其中任务Example_Event优先级高于Example_TaskEntry。
在任务Example_Event中读事件0x00000001,阻塞,发生任务切换,执行任务Example_TaskEntry。
在任务Example_TaskEntry向任务Example_Event写事件0x00000001,发生任务切换,执行任务Example_Event。
Example_Event得以执行,直到任务结束。
Example_TaskEntry得以执行,直到任务结束。
编程示例
前提条件:在menuconfig菜单中完成事件模块的配置。
代码实现如下:
#include "los_event.h"
#include "los_task.h"
#include "securec.h"
/* 任务ID */
UINT32 g_testTaskId;
/* 事件控制结构体 */
EVENT_CB_S g_exampleEvent;
/* 等待的事件类型 */
#define EVENT_WAIT 0x00000001
/* 用例任务入口函数 */
VOID Example_Event(VOID)
{
UINT32 ret;
UINT32 event;
/* 超时等待方式读事件,超时时间为100 ticks, 若100 ticks后未读取到指定事件,读事件超时,任务直接唤醒 */
printf("Example_Event wait event 0x%x \n", EVENT_WAIT);
event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, 100);
if (event == EVENT_WAIT) {
printf("Example_Event,read event :0x%x\n", event);
} else {
printf("Example_Event,read event timeout\n");
}
}
UINT32 Example_TaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
/* 事件初始化 */
ret = LOS_EventInit(&g_exampleEvent);
if (ret != LOS_OK) {
printf("init event failed .\n");
return -1;
}
/* 创建任务 */
(VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event;
task1.pcName = "EventTsk1";
task1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId, &task1);
if (ret != LOS_OK) {
printf("task create failed .\n");
return LOS_NOK;
}
/* 写g_testTaskId 等待事件 */
printf("Example_TaskEntry write event .\n");
ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT);
if (ret != LOS_OK) {
printf("event write failed .\n");
return LOS_NOK;
}
/* 清标志位 */
printf("EventMask:%d\n", g_exampleEvent.uwEventID);
LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID);
printf("EventMask:%d\n", g_exampleEvent.uwEventID);
/* 删除任务 */
ret = LOS_TaskDelete(g_testTaskId);
if (ret != LOS_OK) {
printf("task delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
结果验证
编译运行结果如下:
Example_Event wait event 0x1
Example_TaskEntry write event .
Example_Event,read event :0x1
EventMask:1
EventMask:0
基于事件条件的读写¶
实例描述
示例中,任务ExampleTaskEntry中创建两个读事件任务readTask1和readTask2和一个写事件任务writeTask,由writeTask依次将readTask1和readTask2等待的条件置为真,并写事件,readTask1和readTask2读取的事件符合预期,结束阻塞。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。
在任务ExampleTaskEntry中创建两个读事件任务readTask1和readTask2;
任务readTask1等待的条件EventCondition1当前为假,当前未读取到目的事件,且未超时,任务阻塞;
任务readTask2等待的条件EventCondition2当前为假,当前未读取到目的事件,且未超时,任务阻塞;
在任务ExampleTaskEntry中创建写事件任务writeTask;
writeTask将readTask1等待的条件置为真,写事件;
readTask1和readTask2都得到唤醒,但因只有readTask1的条件为真,所以readTask1读事件成功,向下执行,readTask2读事件失败,继续阻塞;
writeTask将readTask2等待的条件置为真,写事件;
readTask2得到唤醒,条件为真,所以readTask2读事件成功,向下执行。
编程示例
前提条件:在menuconfig菜单中完成事件模块的配置。
代码实现如下:
#include "los_event.h"
#include "los_task.h"
#include "securec.h"
/* 任务ID */
static UINT32 g_readTskId1;
static UINT32 g_readTskId2;
static UINT32 g_writeTskId;
/* g_readTskId1和g_readTskId2等待的条件 */
static int g_testData1 = 0;
static int g_testData2 = 0;
/* 事件控制结构体 */
static EVENT_CB_S g_event;
/* 写事件任务入口函数 */
static VOID WriteTaskFunc(VOID)
{
UINT32 ret;
g_testData1 = 1;
printf("\nWriteTaskFunc: write for ReadTaskFunc1\n\n");
ret = LOS_EventCondWrite(&g_event);
if (ret != LOS_OK) {
printf("WriteTaskFunc failed: ret = %u\n", ret);
return;
}
LOS_TaskDelay(2); /* 2: delay 2 ticks */
printf("\nWriteTaskFunc: write for ReadTaskFunc2\n\n");
g_testData2 = 1;
ret = LOS_EventCondWrite(&g_event);
if (ret != LOS_OK) {
printf("WriteTaskFunc failed: ret = %u\n", ret);
return;
}
LOS_TaskDelay(2); /* 2: delay 2 ticks */
return;
}
/* 读事件任务1预期想读取的事件条件 */
static BOOL EventCondition1(VOID)
{
BOOL ret;
ret = (g_testData1 == 1) ? TRUE : FALSE;
printf("EventCondition1: ret = %s\n", (ret ? "TRUE" : "FALSE"));
return ret;
}
/* 读事件任务1入口函数 */
static VOID ReadTaskFunc1(VOID)
{
UINT32 ret;
ret = LOS_EventCondRead(&g_event, EventCondition1(), LOS_WAIT_FOREVER);
if (ret != LOS_OK) {
printf("ReadTaskFunc1 read failed!\n");
return;
}
printf("ReadTaskFunc1 read succeeded!\n");
return;
}
/* 读事件任务2预期想读取的事件条件 */
static BOOL EventCondition2(VOID)
{
BOOL ret;
ret = (g_testData2 == 1) ? TRUE : FALSE;
printf("EventCondition2: ret = %s\n", (ret ? "TRUE" : "FALSE"));
return ret;
}
/* 读事件任务2入口函数 */
static VOID ReadTaskFunc2(VOID)
{
UINT32 ret;
ret = LOS_EventCondRead(&g_event, EventCondition2(), LOS_WAIT_FOREVER);
if (ret != LOS_OK) {
printf("ReadTaskFunc2 read failed!\n");
return;
}
printf("ReadTaskFunc2 read succeeded!\n");
return;
}
static VOID InitTaskParam(TSK_INIT_PARAM_S *task, char *taskName, TSK_ENTRY_FUNC entry, UINT16 prio, UINT16 affi)
{
task->pcName = taskName;
task->pfnTaskEntry = entry;
task->usTaskPrio = prio;
task->uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task->uwResved = LOS_TASK_STATUS_DETACHED;
#ifdef LOSCFG_KERNEL_SMP
task->usCpuAffiMask = affi;
#endif
}
static UINT32 ExampleTaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S readTask1 = {0};
TSK_INIT_PARAM_S readTask2 = {0};
TSK_INIT_PARAM_S writeTask = {0};
ret = LOS_EventInit(&g_event);
if (ret != LOS_OK) {
return ret;
}
InitTaskParam(&readTask1, "EventReadTsk1", (TSK_ENTRY_FUNC)ReadTaskFunc1, 5, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
ret = LOS_TaskCreate(&g_readTskId1, &readTask1);
if (ret != LOS_OK) {
return ret;
}
InitTaskParam(&readTask2, "EventReadTsk2", (TSK_ENTRY_FUNC)ReadTaskFunc2, 5, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
ret = LOS_TaskCreate(&g_readTskId2, &readTask2);
if (ret != LOS_OK) {
return ret;
}
InitTaskParam(&writeTask, "EventWriteTsk45", (TSK_ENTRY_FUNC)WriteTaskFunc, 5, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
ret = LOS_TaskCreate(&g_writeTskId, &writeTask);
if (ret != LOS_OK) {
return ret;
}
(VOID)LOS_TaskDelay(6); /* 6: delay 6 ticks */
(VOID)LOS_EventDestroy(&g_event);
return LOS_OK;
}
结果验证
编译运行得到的结果为:
EventCondition1: ret = FALSE
EventCondition2: ret = FALSE
WriteTaskFunc: write for ReadTaskFunc1
EventCondition1: ret = TRUE
ReadTaskFunc1 read succeeded!
EventCondition2: ret = FALSE
WriteTaskFunc: write for ReadTaskFunc2
EventCondition2: ret = TRUE
EventCondition2: ret = TRUE
ReadTaskFunc2 read succeeded!
信号量¶
概述¶
基本概念
信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。
一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况:
0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。
正值,表示该信号量当前可被获取。
以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同:
用作互斥时,初始信号量计数值不为0,表示可用的共享资源个数。在需要使用共享资源前,先获取信号量,然后使用一个共享资源,使用完毕后释放信号量。这样在共享资源被取完,即信号量计数减至0时,其他需要获取信号量的任务将被阻塞,从而保证了共享资源的互斥访问。另外,当共享资源数为1时,建议使用二值信号量。二值信号量的计数值最大为1,是一种类似于互斥锁的机制。
用作同步时,初始信号量计数值为0。任务1获取信号量而阻塞,直到任务2或者某中断释放信号量,任务1才得以进入Ready或Running态,从而达到了任务间的同步。
运作机制
信号量控制块:
/**
* Semaphore control structure.
*/
typedef struct {
UINT8 semStat; /* 是否使用标志位 */
UINT8 semType; /* 信号量类型 */
UINT16 semCount; /* 信号量计数 */
UINT32 semId; /* 信号量索引号 */
LOS_DL_LIST semList; /* 挂接阻塞于该信号量的任务 */
} LosSemCB;
信号量运作原理
信号量初始化:为配置的N个信号量申请内存(N值可以由用户自行配置,通过LOSCFG_BASE_IPC_SEM_LIMIT宏实现),并把所有信号量初始化成未使用,加入到未使用链表中供系统使用。
信号量创建:从未使用的信号量链表中获取一个信号量,并设定初值。
信号量申请:若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。
信号量释放:若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。
信号量删除:将正在使用的信号量置为未使用信号量,并挂回到未使用链表。
信号量允许多个任务在同一时刻访问共享资源,但会限制同一时刻访问此资源的最大任务数目。当访问资源的任务数达到该资源允许的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。
图 1 信号量运作示意图

开发指导¶
使用场景
在多任务系统中,信号量是一种非常灵活的同步方式,可以运用在多种场合中,实现锁、同步、资源计数等功能,也能方便的用于任务与任务,中断与任务的同步中。信号量常用于协助一组相互竞争的任务访问共享资源。
功能
LiteOS的信号量模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建/删除信号量 |
LOS_SemCreate |
创建信号量,返回信号量ID。 |
LOS_BinarySemCreate |
创建二值信号量,其计数值最大为1。 |
|
LOS_SemDelete |
删除指定的信号量。 |
|
申请/释放信号量 |
LOS_SemPend |
申请指定的信号量,并设置超时时间。 |
LOS_SemPost |
释放指定的信号量。 |
说明: 信号量有三种申请模式:无阻塞模式、永久阻塞模式、定时阻塞模式。
无阻塞模式:即任务申请信号量时,入参timeout等于0。若当前信号量计数值不为0,则申请成功,否则立即返回申请失败。
永久阻塞模式:即任务申请信号量时,入参timeout等于0xFFFFFFFF。若当前信号量计数值不为0,则申请成功。否则该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,直到有其他任务释放该信号量,阻塞任务才会重新得以执行。
定时阻塞模式:即任务申请信号量时,0<timeout<0xFFFFFFFF。若当前信号量计数值不为0,则申请成功。否则,该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,超时前如果有其他任务释放该信号量,则该任务可成功获取信号量继续执行,若超时前未获取到信号量,接口将返回超时错误码。
信号量错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_SEM_NO_MEMORY |
0x02000700 |
初始化信号量时,内存空间不足。 |
调整OS_SYS_MEM_SIZE以确保有足够的内存供信号量使用,或减小系统支持的最大信号量数LOSCFG_BASE_IPC_SEM_LIMIT。 |
2 |
LOS_ERRNO_SEM_INVALID |
0x02000701 |
信号量ID不正确或信号量未创建。 |
传入正确的信号量ID或创建信号量后再使用。 |
3 |
LOS_ERRNO_SEM_PTR_NULL |
0x02000702 |
传入空指针。 |
传入合法指针。 |
4 |
LOS_ERRNO_SEM_ALL_BUSY |
0x02000703 |
创建信号量时,系统中已经没有未使用的信号量。 |
及时删除无用的信号量或增加系统支持的最大信号量数LOSCFG_BASE_IPC_SEM_LIMIT。 |
5 |
LOS_ERRNO_SEM_UNAVAILABLE |
0x02000704 |
无阻塞模式下未获取到信号量。 |
选择阻塞等待或根据该错误码适当处理。 |
6 |
LOS_ERRNO_SEM_PEND_INTERR |
0x02000705 |
中断期间非法调用LOS_SemPend申请信号量。 |
中断期间禁止调用LOS_SemPend。 |
7 |
LOS_ERRNO_SEM_PEND_IN_LOCK |
0x02000706 |
任务被锁,无法获得信号量。 |
在任务被锁时,不能调用LOS_SemPend申请信号量。 |
8 |
LOS_ERRNO_SEM_TIMEOUT |
0x02000707 |
获取信号量超时。 |
将时间设置在合理范围内。 |
9 |
LOS_ERRNO_SEM_OVERFLOW |
0x02000708 |
信号量计数值已达到最大值,无法再继续释放该信号量。 |
根据该错误码适当处理。 |
10 |
LOS_ERRNO_SEM_PENDED |
0x02000709 |
等待信号量的任务队列不为空。 |
唤醒所有等待该信号量的任务后,再删除该信号量。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为信号量模块,值为0x07。
开发流程
信号量的开发典型流程:
打开菜单,进入Kernel ---> Enable Sem菜单,完成信号量的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_IPC_SEM
信号量模块裁剪开关
YES/NO
YES
无
LOSCFG_BASE_IPC_SEM_LIMIT
系统支持的信号量最大数
[0, 65535]
1024
无
创建信号量LOS_SemCreate,若要创建二值信号量则调用LOS_BinarySemCreate。
申请信号量LOS_SemPend。
释放信号量LOS_SemPost。
删除信号量LOS_SemDelete。
平台差异性
无。
注意事项¶
由于中断不能被阻塞,因此不能在中断中使用阻塞模式申请信号量。
编程实例¶
实例描述
本实例实现如下功能:
测试任务Example_TaskEntry创建一个信号量,锁任务调度,创建两个任务Example_SemTask1、Example_SemTask2,Example_SemTask2优先级高于Example_SemTask1,两个任务中申请同一信号量,解锁任务调度后两任务阻塞,测试任务Example_TaskEntry释放信号量。
Example_SemTask2得到信号量,被调度,然后任务休眠20Tick,Example_SemTask2延迟,Example_SemTask1被唤醒。
Example_SemTask1定时阻塞模式申请信号量,等待时间为10Tick,因信号量仍被Example_SemTask2持有,Example_SemTask1挂起,10Tick后仍未得到信号量,Example_SemTask1被唤醒,试图以永久阻塞模式申请信号量,Example_SemTask1挂起。
20Tick后Example_SemTask2唤醒, 释放信号量后,Example_SemTask1得到信号量被调度运行,最后释放信号量。
Example_SemTask1执行完,40Tick后任务Example_TaskEntry被唤醒,执行删除信号量,删除两个任务。
编程示例
前提条件:在menuconfig菜单中完成信号量的配置。
代码实现如下:
#include "los_sem.h"
#include "securec.h"
/* 任务ID */
static UINT32 g_testTaskId01;
static UINT32 g_testTaskId02;
/* 测试任务优先级 */
#define TASK_PRIO_TEST 5
/* 信号量结构体id */
static UINT32 g_semId;
VOID Example_SemTask1(VOID)
{
UINT32 ret;
printf("Example_SemTask1 try get sem g_semId ,timeout 10 ticks.\n");
/* 定时阻塞模式申请信号量,定时时间为10ticks */
ret = LOS_SemPend(g_semId, 10);
/* 申请到信号量 */
if (ret == LOS_OK) {
LOS_SemPost(g_semId);
return;
}
/* 定时时间到,未申请到信号量 */
if (ret == LOS_ERRNO_SEM_TIMEOUT) {
printf("Example_SemTask1 timeout and try get sem g_semId wait forever.\n");
/*永久阻塞模式申请信号量*/
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
printf("Example_SemTask1 wait_forever and get sem g_semId .\n");
if (ret == LOS_OK) {
LOS_SemPost(g_semId);
return;
}
}
}
VOID Example_SemTask2(VOID)
{
UINT32 ret;
printf("Example_SemTask2 try get sem g_semId wait forever.\n");
/* 永久阻塞模式申请信号量 */
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("Example_SemTask2 get sem g_semId and then delay 20ticks .\n");
}
/* 任务休眠20 ticks */
LOS_TaskDelay(20);
printf("Example_SemTask2 post sem g_semId .\n");
/* 释放信号量 */
LOS_SemPost(g_semId);
return;
}
UINT32 ExampleTaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2;
/* 创建信号量 */
LOS_SemCreate(0,&g_semId);
/* 锁任务调度 */
LOS_TaskLock();
/* 创建任务1 */
(VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask1;
task1.pcName = "TestTsk1";
task1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = TASK_PRIO_TEST;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed .\n");
return LOS_NOK;
}
/* 创建任务2 */
(VOID)memset_s(&task2, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask2;
task2.pcName = "TestTsk2";
task2.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = (TASK_PRIO_TEST - 1);
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed .\n");
return LOS_NOK;
}
/* 解锁任务调度 */
LOS_TaskUnlock();
ret = LOS_SemPost(g_semId);
/* 任务休眠40 ticks */
LOS_TaskDelay(40);
/* 删除信号量 */
LOS_SemDelete(g_semId);
/* 删除任务1 */
ret = LOS_TaskDelete(g_testTaskId01);
if (ret != LOS_OK) {
printf("task1 delete failed .\n");
return LOS_NOK;
}
/* 删除任务2 */
ret = LOS_TaskDelete(g_testTaskId02);
if (ret != LOS_OK) {
printf("task2 delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
结果验证
编译运行得到的结果为:
Example_SemTask2 try get sem g_semId wait forever.
Example_SemTask1 try get sem g_semId ,timeout 10 ticks.
Example_SemTask2 get sem g_semId and then delay 20ticks .
Example_SemTask1 timeout and try get sem g_semId wait forever.
Example_SemTask2 post sem g_semId .
Example_SemTask1 wait_forever and get sem g_semId .
读写信号量¶
概述¶
基本概念
读写信号量(rwsem)与普通信号量的功能相似,常用于共享资源的互斥访问和任务同步。读写信号量将持有者分为读者和写者,如果一个持有者需要对该信号量保护的共享资源进行写操作,那么将这类持有者定义为写者;如果持有者只是访问共享资源,而不修改,那么将这类持有者定义为读者。
读写信号量支持一个读写信号量同时拥有不受上限的读者数,即读者不排他可并发;但是一个读写信号量同时只能有一个写者,写者是排他性的、独占性的,写写互斥、读写互斥。
运作机制
读写信号量控制块:
typedef struct {
UINT8 rwsemState; /* 是否使用标志位 */
INT16 rwsemCount; /* 读写信号量拥有者的数量 */
UINT32 rwsemId; /* 读写信号量索引号 */
LOS_DL_LIST waitList; /* 等待队列,用于记录阻塞于该读写信号量的任务 */
} OsRwsemCB;
读写信号量运作原理
读写信号量资源初始化:为配置的N个读写信号量申请内存(N值可以由用户自行配置,通过LOSCFG_BASE_IPC_RWSEM_LIMIT宏实现),并把所有读写信号量初始化成未使用,加入到未使用链表中供系统使用。
创建读写信号量:从未使用链表中获取一个读写信号量。
申请读信号量:若该信号量还未被持有,或者正在被其他读者持有,返回成功;否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个读写信号量阻塞时,该任务会被挂到读写信号量等待队列的队尾。
申请写信号量:若该信号量还未被持有,返回成功;否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个读写信号量阻塞时,该任务会被挂到读写信号量等待队列的队尾。
释放读信号量:若没有任务等待该读写信号量,则直接返回;当信号量处于读状态,等待队列中的队首任务只能是写者,唤醒这一个写者,然后返回。
释放写信号量:若没有任务等待该读写信号量,则直接返回;如果等待队列的队首任务是写者,则唤醒这一个写者,然后返回;如果等待队列的队首任务是读者,则唤醒这个读者及后面连续的多个读者,直到等待队列为空,或者遇到写者停止。
降级写信号量为读信号量:信号量变更为读信号量,若没有任务等待该读写信号量或者等待队列的队首任务是写者,则直接返回;否则唤醒等待队列的队首读者及后面连续的多个读者,直到等待队列为空,或者遇到写者停止。
删除读写信号量:将正在使用的读写信号量设置为未使用的状态,并挂回到未使用链表。
说明:
读写信号量允许多个任务在同一时刻访问共享资源,但同一时刻只能有一个任务可修改共享资源,这样对于读任务多于写任务的场景,可有效提高系统的整体性能。
开发指导¶
使用场景
读写信号量是基于普通信号量优化的一种多任务同步互斥机制,可以运用在多种场合中,实现锁、同步、资源计数等功能,也能方便的用于任务与任务同步中,适用于读任务多于写任务的场景。
功能
LiteOS的读写信号量模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建/删除读写信号量 |
LOS_RwsemCreate |
创建读写信号量,返回读写信号量ID。 |
LOS_RwsemDelete |
删除指定的读写信号量。 |
|
申请/释放读写信号量 |
LOS_RwsemPendRead |
申请指定的读写信号量为读信号量,支持超时等待获取。 |
LOS_RwsemPostRead |
释放指定的读信号量。 |
|
LOS_RwsemPendWrite |
申请指定的读写信号量为写信号量,支持超时等待获取。 |
|
LOS_RwsemPostWrite |
释放指定的写信号量。 |
|
LOS_RwsemDowngradeWrite |
将指定的写信号量降级为读信号量。 |
说明: 读写信号量支持三种申请模式:无阻塞模式、永久阻塞模式、定时阻塞模式。
无阻塞模式:即任务申请读写信号量时,入参timeout等于0。若当前读写信号量可获取,则申请成功,否则立即返回申请失败。
永久阻塞模式:即任务申请读写信号量时,入参timeout等于0xFFFFFFFF。若当前读写信号量可获取,则申请成功。否则该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,直到其他任务释放该读写信号量,使得该读写信号量可重新被获取,阻塞任务才会重新得以执行。
定时阻塞模式:即任务申请读写信号量时,0<timeout<0xFFFFFFFF。若当前读写信号量可获取,则申请成功。否则,该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,如果超时前其他任务释放该信号量,使得该读写信号量可重新被获取,则该任务可成功获取读写信号量继续执行,若超时前未获取到读写信号量,接口将返回超时错误码。
读写信号量错误码
对于存在失败可能性的操作系统会返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_RWSEM_INVALID |
0x02002200 |
读写信号量ID不正确或读写信号量未创建。 |
传入正确的读写信号量ID或创建读写信号量后再使用。 |
2 |
LOS_ERRNO_RWSEM_PTR_NULL |
0x02002201 |
传入空指针。 |
传入合法指针。 |
3 |
LOS_ERRNO_RWSEM_ALL_BUSY |
0x02002202 |
创建读写信号量时,系统中已经没有未使用的读写信号量。 |
及时删除无用的读写信号量或增加系统支持的最大读写信号量数LOSCFG_BASE_IPC_RWSEM_LIMIT。 |
4 |
LOS_ERRNO_RWSEM_PEND_INTERR |
0x02002203 |
中断期间非法调用API,例如以阻塞的方式申请读写信号量。 |
合理调用API,不要在中断期间以阻塞方式获取读写信号量。 |
5 |
LOS_ERRNO_RWSEM_PEND_IN_LOCK |
0x02002204 |
任务被锁,无法获得读写信号量。 |
在任务被锁时,不要调用API获取读写信号量。 |
6 |
LOS_ERRNO_RWSEM_TIMEOUT |
0x02002205 |
获取读写信号量超时。 |
将时间设置在合理范围内。 |
7 |
LOS_ERRNO_RWSEM_PENDED |
0x02002206 |
等待读写信号量的任务队列不为空。 |
唤醒所有等待该读写信号量的任务后,再删除该读写信号量。 |
8 |
LOS_ERRNO_RWSEM_INVALID_STATUS |
0x02002207 |
释放或降级读写信号量时,信号量的状态不正确。 |
请正确释放和降级读写信号量。 |
9 |
LOS_ERRNO_RWSEM_UNAVAILABLE |
0x02002208 |
无阻塞模式下未获取到读写信号量。 |
选择阻塞等待或根据该错误码适当处理。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为信号量模块,值为0x22。
开发流程
读写信号量的典型开发流程如下:
打开菜单,进入Kernel ---> Enable Rwsem菜单,完成读写信号量的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_IPC_RWSEM
读写信号量模块裁剪开关
YES/NO
YES
无
LOSCFG_BASE_IPC_RWSEM_LIMIT
系统支持的读写信号量最大数
[0,65535]
1024
LOSCFG_BASE_IPC_RWSEM
创建读写信号量LOS_RwsemCreate。
申请读信号量LOS_RwsemPendRead。
释放读信号量LOS_RwsemPostRead。
删除读写信号量LOS_RwsemDelete。
平台差异性
无。
注意事项¶
由于中断不能被阻塞,因此不能在中断中使用阻塞模式申请读写信号量。
编程实例¶
实例描述
本实例实现如下功能:
测试任务ExampleTaskEntry创建一个读写信号量,并申请写信号量,锁任务调度,创建三个任务ExampleReadTask1、ExampleReadTask2和ExampleWriteTask2;ExampleReadTask1和ExampleReadTask2的优先级高于ExampleWriteTask2任务,并申请读信号量;ExampleWriteTask2申请写信号量。
ExampleTaskEntry解锁任务调度后,ExampleReadTask1、ExampleReadTask2获取读信号量失败,永久阻塞等待,ExampleWriteTask2等待1tick获取写信号量失败,改为永久阻塞等待获取写信号量。
ExampleTaskEntry降级写信号量为读信号量,ExampleReadTask1和ExampleReadTask2获取读信号量成功,ExampleWriteTask2继续阻塞。
在ExampleReadTask1、ExampleReadTask2和ExampleTaskEntry释放读信号量之后,ExampleWriteTask2获取写信号量成功。
ExampleWriteTask2释放写信号量。
编程示例
前提条件:在menuconfig菜单中完成读写信号量的配置。
代码实现如下:
#include "los_rwsem.h"
#include "securec.h"
static UINT32 g_rwsemId; /* 读写信号量结构体id */
VOID ExampleReadTask1(VOID)
{
UINT32 ret;
/* 定时时间到,未申请到读信号量 */
printf("[ExampleReadTask1] try get rwsem as read, wait forever.\n");
/* 永久阻塞模式申请读信号量 */
ret = LOS_RwsemPendRead(g_rwsemId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("[ExampleReadTask1] get rwsem success.\n");
LOS_TaskDelay(3);
printf("[ExampleReadTask1] post rwsem.\n");
LOS_RwsemPostRead(g_rwsemId);
return;
}
}
VOID ExampleReadTask2(VOID)
{
UINT32 ret;
/* 定时时间到,未申请到读信号量 */
printf("[ExampleReadTask2] try get rwsem as read, wait forever.\n");
/* 永久阻塞模式申请读信号量 */
ret = LOS_RwsemPendRead(g_rwsemId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("[ExampleReadTask2] get rwsem success.\n");
LOS_TaskDelay(3);
printf("[ExampleReadTask2] post rwsem.\n");
LOS_RwsemPostRead(g_rwsemId);
return;
}
}
VOID ExampleWriteTask2(VOID)
{
UINT32 ret;
printf("[ExampleWriteTask2] try get rwsem as write, timeout 1 tick.\n");
/* 定时阻塞模式申请写信号量,定时时间为1tick */
ret = LOS_RwsemPendWrite(g_rwsemId, 1);
/* 申请到写信号量 */
if (ret == LOS_OK) {
LOS_RwsemPostWrite(g_rwsemId);
return;
}
/* 定时时间到,未申请到写信号量 */
if (ret == LOS_ERRNO_RWSEM_TIMEOUT) {
printf("[ExampleWriteTask2] timeout and try get rwsem as write, wait forever.\n");
/* 永久阻塞模式申请写信号量 */
ret = LOS_RwsemPendWrite(g_rwsemId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("[ExampleWriteTask2] get rwsem success.\n");
LOS_TaskDelay(3);
printf("[ExampleWriteTask2] post rwsem.\n");
LOS_RwsemPostWrite(g_rwsemId);
return;
}
}
return;
}
static VOID InitTaskParam(TSK_INIT_PARAM_S *task, char *taskName, TSK_ENTRY_FUNC entry, UINT16 prio, UINT16 affi)
{
task->pcName = taskName;
task->pfnTaskEntry = entry;
task->usTaskPrio = prio;
task->uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task->uwResved = LOS_TASK_STATUS_DETACHED;
#ifdef LOSCFG_KERNEL_SMP
task->usCpuAffiMask = affi;
#endif
}
UINT32 ExampleTaskEntry(VOID)
{
UINT32 ret;
UINT32 readTsk1Id, readTsk2Id, writeTsk2Id;
TSK_INIT_PARAM_S readTask1 = {0};
TSK_INIT_PARAM_S readTask2 = {0};
TSK_INIT_PARAM_S writeTask2 = {0};
/* 创建读写信号量 */
ret = LOS_RwsemCreate(&g_rwsemId);
if (ret != LOS_OK) {
printf("[ExampleTaskEntry] create rwsem failed.\n");
return LOS_NOK;
}
ret = LOS_RwsemPendWrite(g_rwsemId, 0);
if (ret != LOS_OK) {
printf("[ExampleTaskEntry] get rwsem failed.\n");
return LOS_NOK;
}
printf("[ExampleTaskEntry] get rwsem success.\n");
/* 锁任务调度 */
LOS_TaskLock();
/* 创建读任务1 */
InitTaskParam(&readTask1, "resemReadTsk1", (TSK_ENTRY_FUNC)ExampleReadTask1, 5, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
ret = LOS_TaskCreate(&readTsk1Id, &readTask1);
if (ret != LOS_OK) {
printf("[ExampleTaskEntry] readTask1 create failed.\n");
return LOS_NOK;
}
/* 创建读任务2 */
InitTaskParam(&readTask2, "resemReadTsk2", (TSK_ENTRY_FUNC)ExampleReadTask2, 5, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
ret = LOS_TaskCreate(&readTsk2Id, &readTask2);
if (ret != LOS_OK) {
printf("[ExampleTaskEntry] readTask2 create failed.\n");
return LOS_NOK;
}
/* 创建写任务2 */
InitTaskParam(&writeTask2, "resemWriteTsk2", (TSK_ENTRY_FUNC)ExampleWriteTask2, 9, CPUID_TO_AFFI_MASK(ArchCurrCpuid()));
ret = LOS_TaskCreate(&writeTsk2Id, &writeTask2);
if (ret != LOS_OK) {
printf("[ExampleTaskEntry] writeTask2 create failed.\n");
return LOS_NOK;
}
/* 解锁任务调度 */
LOS_TaskUnlock();
/* 让出CPU 5tick,等待readTask1、readTask2、writeTask2先执行并阻塞 */
LOS_TaskDelay(5);
/* 写信号量降级为读信号量 */
printf("[ExampleTaskEntry] downgrade rwsem.\n");
LOS_RwsemDowngradeWrite(g_rwsemId);
/* 任务休眠5ticks */
LOS_TaskDelay(5);
LOS_RwsemPostRead(g_rwsemId);
/* 任务休眠10ticks */
LOS_TaskDelay(10);
/* 删除读写信号量 */
LOS_RwsemDelete(g_rwsemId);
return LOS_OK;
}
结果验证
编译运行所得结果如下:
[ExampleTaskEntry] get rwsem success.
[ExampleReadTask1] try get rwsem as read, wait forever.
[ExampleReadTask2] try get rwsem as read, wait forever.
[ExampleWriteTask2] try get rwsem as write, timeout 1 tick.
[ExampleWriteTask2] timeout and try get rwsem as write, wait forever.
[ExampleTaskEntry] downgrade rwsem.
[ExampleReadTask1] get rwsem success.
[ExampleReadTask2] get rwsem success.
[ExampleReadTask1] post rwsem.
[ExampleReadTask2] post rwsem.
[ExampleWriteTask2] get rwsem success.
[ExampleWriteTask2] post rwsem.
互斥锁¶
概述¶
基本概念
互斥锁又称互斥型信号量,是一种特殊的二值性信号量,用于实现对临界资源的独占式处理。另外,互斥锁可以解决信号量存在的优先级翻转问题。
任意时刻互斥锁只有两种状态,开锁或闭锁。当任务持有时,这个任务获得该互斥锁的所有权,互斥锁处于闭锁状态。当该任务释放锁后,任务失去该互斥锁的所有权,互斥锁处于开锁状态。当一个任务持有互斥锁时,其他任务不能再对该互斥锁进行开锁或持有。
LiteOS提供的互斥锁具有如下特点:
通过优先级继承算法,解决优先级翻转问题。
多任务阻塞等待同一个锁的场景,支持基于任务优先级等待和FIFO两种模式。
运作机制
多任务环境下会存在多个任务访问同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占使用。互斥锁怎样来避免这种冲突呢?
用互斥锁处理临界资源的同步访问时,如果有任务访问该资源,则互斥锁为加锁状态。此时其他任务如果想访问这个临界资源则会被阻塞,直到互斥锁被持有该锁的任务释放后,其他任务才能重新访问该公共资源,此时互斥锁再次上锁,如此确保同一时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的完整性。
图 1 互斥锁运作示意图

开发指导¶
使用场景
多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥锁可以提供任务间的互斥机制,防止两个任务在同一时刻访问相同的临界资源,从而实现独占式访问。
功能
LiteOS的互斥锁模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建/删除互斥锁 |
LOS_MuxCreate |
创建互斥锁。 |
LOS_MuxDelete |
删除指定互斥锁。 |
|
申请/释放互斥锁 |
LOS_MuxPend |
申请指定互斥锁。 |
LOS_MuxPost |
释放指定互斥锁。 |
说明: 申请互斥锁有三种模式:无阻塞模式、永久阻塞模式、定时阻塞模式。
无阻塞模式:即任务申请互斥锁时,入参timeout等于0。若当前没有任务持有该互斥锁,或者持有该互斥锁的任务和申请该互斥锁的任务为同一个任务,则申请成功,否则立即返回申请失败。
永久阻塞模式:即任务申请互斥锁时,入参timeout等于0xFFFFFFFF。若当前没有任务持有该互斥锁,则申请成功。否则,任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,直到有其他任务释放该互斥锁,阻塞任务才会重新得以执行。
定时阻塞模式:即任务申请互斥锁时,0<timeout<0xFFFFFFFF。若当前没有任务持有该互斥锁,则申请成功。否则该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,超时前如果有其他任务释放该互斥锁,则该任务可成功获取互斥锁继续执行,若超时前未获取到该互斥锁,接口将返回超时错误码。 释放互斥锁:
如果有任务阻塞于该互斥锁,则唤醒被阻塞任务中优先级最高的,该任务进入就绪态,并进行任务调度。
如果没有任务阻塞于该互斥锁,则互斥锁释放成功。
互斥锁错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_MUX_NO_MEMORY |
0x02001D 00 |
初始化互斥锁模块时,内存不足。 |
设置更大的系统动态内存池,配置项为OS_SYS_MEM_SIZE,或减少系统支持的最大互斥锁个数。 |
2 |
LOS_ERRNO_MUX_INVALID |
0x02001D01 |
互斥锁不可用。 |
传入有效的互斥锁ID。 |
3 |
LOS_ERRNO_MUX_PTR_NULL |
0x02001D02 |
创建互斥锁时,入参为空指针。 |
传入有效指针。 |
4 |
LOS_ERRNO_MUX_ALL_BUSY |
0x02001D03 |
创建互斥锁时,系统中已经没有可用的互斥锁。 |
增加系统支持的最大互斥锁个数。 |
5 |
LOS_ERRNO_MUX_UNAVAILABLE |
0x02001D04 |
申请互斥锁失败,因为锁已经被其他线程持有。 |
等待其他线程解锁或者设置等待时间。 |
6 |
LOS_ERRNO_MUX_PEND_INTERR |
0x02001D05 |
在中断中使用互斥锁。 |
禁止在中断中申请/释放互斥锁。 |
7 |
LOS_ERRNO_MUX_PEND_IN_LOCK |
0x02001D06 |
锁任务调度时,不允许以阻塞模式申请互斥锁。 |
以非阻塞模式申请互斥锁,或使能任务调度后再阻塞申请互斥锁。 |
8 |
LOS_ERRNO_MUX_TIMEOUT |
0x02001D07 |
申请互斥锁超时。 |
增加等待时间,或采用一直等待模式。 |
9 |
LOS_ERRNO_MUX_PENDED |
0x02001D09 |
删除正在使用的互斥锁。 |
等待解锁后再删除该互斥锁。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为互斥锁模块,值为0x1D。
开发流程
互斥锁典型场景的开发流程:
打开菜单,进入Kernel ---> Enable Mutex菜单,完成互斥锁的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_IPC_MUX
互斥锁模块裁剪开关
YES/NO
YES
无
LOSCFG_MUTEX_WAITMODE_PRIO
互斥锁基于任务优先级的等待模式
YES/NO
YES
LOSCFG_BASE_IPC_MUX
LOSCFG_MUTEX_WAITMODE_FIFO
互斥锁基于FIFO的等待模式
YES/NO
NO
LOSCFG_BASE_IPC_MUX
LOSCFG_BASE_IPC_MUX_LIMIT
系统支持的最大互斥锁个数
<65535
1024
LOSCFG_BASE_IPC_MUX
LOSCFG_BASE_IPC_MUX_CREATOR
记录锁的创建者信息
YES/NO
NO
LOSCFG_BASE_IPC_MUX
创建互斥锁LOS_MuxCreate。
申请互斥锁LOS_MuxPend。
释放互斥锁LOS_MuxPost。
删除互斥锁LOS_MuxDelete。
平台差异性
无。
注意事项¶
互斥锁不能在中断服务程序中使用。
LiteOS作为实时操作系统需要保证任务调度的实时性,尽量避免任务的长时间阻塞,因此在获得互斥锁之后,应该尽快释放互斥锁。
持有互斥锁的过程中,不得再调用LOS_TaskPriSet等接口更改持有互斥锁任务的优先级。
互斥锁不支持多个相同优先级任务翻转的场景。
编程实例¶
实例描述
本实例实现如下流程。
任务Example_TaskEntry创建一个互斥锁,锁任务调度,创建两个任务Example_MutexTask1、Example_MutexTask2。Example_MutexTask2优先级高于Example_MutexTask1,解锁任务调度,然后Example_TaskEntry任务休眠300Tick。
Example_MutexTask2被调度,以永久阻塞模式申请互斥锁,并成功获取到该互斥锁,然后任务休眠100Tick,Example_MutexTask2挂起,Example_MutexTask1被唤醒。
Example_MutexTask1以定时阻塞模式申请互斥锁,等待时间为10Tick,因互斥锁仍被Example_MutexTask2持有,Example_MutexTask1挂起。10Tick超时时间到达后,Example_MutexTask1被唤醒,以永久阻塞模式申请互斥锁,因互斥锁仍被Example_MutexTask2持有,Example_MutexTask1挂起。
100Tick休眠时间到达后,Example_MutexTask2被唤醒, 释放互斥锁,唤醒Example_MutexTask1。Example_MutexTask1成功获取到互斥锁后,释放锁。
300Tick休眠时间到达后,任务Example_TaskEntry被调度运行,删除互斥锁,删除两个任务。
编程示例
前提条件:打开菜单完成互斥锁的配置。
代码实现如下:
/* 互斥锁句柄id */
UINT32 g_testMux;
/* 任务ID */
UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
VOID Example_MutexTask1(VOID)
{
UINT32 ret;
printf("task1 try to get mutex, wait 10 ticks.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, 10);
if (ret == LOS_OK) {
printf("task1 get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
return;
} else if (ret == LOS_ERRNO_MUX_TIMEOUT ) {
printf("task1 timeout and try to get mutex, wait forever.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
return;
}
}
return;
}
VOID Example_MutexTask2(VOID)
{
printf("task2 try to get mutex, wait forever.\n");
/* 申请互斥锁 */
(VOID)LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
printf("task2 get mutex g_testMux and suspend 100 ticks.\n");
/* 任务休眠100Ticks */
LOS_TaskDelay(100);
printf("task2 resumed and post the g_testMux\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
return;
}
UINT32 Example_TaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2;
/* 创建互斥锁 */
LOS_MuxCreate(&g_testMux);
/* 锁任务调度 */
LOS_TaskLock();
/* 创建任务1 */
memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
task1.pcName = "MutexTsk1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
return LOS_NOK;
}
/* 创建任务2 */
memset(&task2, 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
task2.pcName = "MutexTsk2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = 4;
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
return LOS_NOK;
}
/* 解锁任务调度 */
LOS_TaskUnlock();
/* 休眠300Ticks */
LOS_TaskDelay(300);
/* 删除互斥锁 */
LOS_MuxDelete(g_testMux);
/* 删除任务1 */
ret = LOS_TaskDelete(g_testTaskId01);
if (ret != LOS_OK) {
printf("task1 delete failed .\n");
return LOS_NOK;
}
/* 删除任务2 */
ret = LOS_TaskDelete(g_testTaskId02);
if (ret != LOS_OK) {
printf("task2 delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
结果验证
编译运行得到的结果为:
task2 try to get mutex, wait forever.
task2 get mutex g_testMux and suspend 100 ticks.
task1 try to get mutex, wait 10 ticks.
task1 timeout and try to get mutex, wait forever.
task2 resumed and post the g_testMux
task1 wait forever,get mutex g_testMux.
软件定时器¶
概述¶
基本概念
软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick数后,会触发用户自定义的回调函数。
硬件定时器受硬件的限制,数量上不足以满足用户的实际需求。因此为了满足用户需求,提供更多的定时器,LiteOS提供了软件定时器功能,支持如下特性:
创建软件定时器。
启动软件定时器。
停止软件定时器。
删除软件定时器。
获取软件定时器剩余Tick数。
可配置支持的软件定时器个数。
运作机制
软件定时器是系统资源,在模块初始化的时候已经分配了一块连续内存。
软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先触发的准则。
软件定时器以Tick为基本计时单位,当创建并启动一个软件定时器时,LiteOS会根据当前系统Tick时间及设置的定时时长确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。
当Tick中断到来时,在Tick中断处理函数中扫描软件定时器的计时全局链表,检查是否有定时器超时,若有则将超时的定时器记录下来。Tick中断处理函数结束后,软件定时器任务(优先级为最高)被唤醒,在该任务中调用已经记录下来的定时器的回调函数。
定时器状态:
OS_SWTMR_STATUS_UNUSED(定时器未使用)
系统在定时器模块初始化时,会将系统中所有定时器资源初始化成该状态。
OS_SWTMR_STATUS_TICKING(定时器处于计数状态)
在定时器创建后调用LOS_SwtmrStart接口启动,定时器将变成该状态,是定时器运行时的状态。
OS_SWTMR_STATUS_CREATED(定时器创建后未启动,或已停止)
定时器创建后,不处于计数状态时,定时器将变成该状态。
OS_SWTMR_STATUS_DELETING (定时器被删除了)
定时器类型是单次触发类型,在定时器超时后,定时器将变成该状态。
定时器模式:
软件定时器提供了三类模式:
单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动删除。
周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动停止定时器,否则将永远持续执行下去。
单次触发定时器,但这类定时器超时触发后不会自动删除,需要调用定时器删除接口删除定时器。
开发指导¶
使用场景
创建一个单次触发的定时器,超时后执行用户自定义的回调函数。
创建一个周期性触发的定时器,超时后执行用户自定义的回调函数。
功能
LiteOS的软件定时器模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
创建/删除定时器 |
LOS_SwtmrCreate |
创建定时器,设置定时器的定时时长、定时器模式、回调函数,并返回定时器ID。 |
LOS_SwtmrDelete |
异步删除定时器,接口在入参检测后立即返回,不保障接口返回时软件定时器资源已经被释放,软件定时器资源将在目标定时器所有回调执行完毕后释放。 |
|
LOS_SwtmrSyncDelete |
同步删除定时器,接口在入参检测后,会等待目标定时器的所有回调执行完毕,而后删除定时器,释放软件定时器资源。 |
|
启动/停止定时器 |
LOS_SwtmrStart |
启动定时器。 |
LOS_SwtmrStop |
停止定时器。 |
|
获得软件定时器剩余Tick数 |
LOS_SwtmrTimeGet |
获得软件定时器剩余Tick数。 |
软件定时器错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_SWTMR_PTR_NULL |
0x02000300 |
软件定时器回调函数为空。 |
定义软件定时器回调函数。 |
2 |
LOS_ERRNO_SWTMR_INTERVAL_NOT_SUITED |
0x02000301 |
软件定时器的定时时长为0。 |
重新定义定时器的定时时长。 |
3 |
LOS_ERRNO_SWTMR_MODE_INVALID |
0x02000302 |
不正确的软件定时器模式。 |
可选范围为[0,2],暂不支持软件定时器模式设为3。 |
4 |
LOS_ERRNO_SWTMR_RET_PTR_NULL |
0x02000303 |
入参的软件定时器ID指针为NULL。 |
定义ID变量,传入有效指针。 |
5 |
LOS_ERRNO_SWTMR_MAXSIZE |
0x02000304 |
软件定时器个数超过最大值。 |
重新设置软件定时器最大个数,或者等待一个软件定时器释放资源。 |
6 |
LOS_ERRNO_SWTMR_ID_INVALID |
0x02000305 |
入参的软件定时器ID不正确。 |
确保入参合法。 |
7 |
LOS_ERRNO_SWTMR_NOT_CREATED |
0x02000306 |
软件定时器未创建。 |
创建软件定时器。 |
8 |
LOS_ERRNO_SWTMR_NO_MEMORY |
0x02000307 |
初始化软件定时器模块时,内存不足。 |
调整OS_SYS_MEM_SIZE,以确保有足够的内存供软件定时器使用。 |
9 |
LOS_ERRNO_SWTMR_HWI_ACTIVE |
0x02000309 |
在中断中使用定时器。 |
修改源代码确保不在中断中使用。 |
10 |
LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED |
0x0200030b |
在软件定时器初始化时,创建定时器队列失败。 |
调整OS_SYS_MEM_SIZE,以确保有足够的内存供软件定时器创建队列。 |
11 |
LOS_ERRNO_SWTMR_TASK_CREATE_FAILED |
0x0200030c |
在软件定时器初始化时,创建定时器任务失败。 |
调整OS_SYS_MEM_SIZE,以确保有足够的内存供软件定时器创建任务。 |
12 |
LOS_ERRNO_SWTMR_NOT_STARTED |
0x0200030d |
未启动软件定时器。 |
启动软件定时器。 |
13 |
LOS_ERRNO_SWTMR_STATUS_INVALID |
0x0200030e |
不正确的软件定时器状态。 |
检查确认软件定时器状态。 |
14 |
LOS_ERRNO_SWTMR_TICK_PTR_NULL |
0x02000310 |
用以获取软件定时器剩余Tick数的入参指针为NULL。 |
定义有效变量以传入有效指针。 |
15 |
LOS_ERRNO_SWTMR_SORTLINK_CREATE_FAILED |
0x02000311 |
在软件定时器初始化时,创建定时器链表失败。 |
调整OS_SYS_MEM_SIZE,以确保有足够的内存供软件定时器创建链表。 |
16 |
LOS_ERRNO_SWTMR_INVALID_SYNCDEL |
0x02000312 |
在中断或软件定时器回调中尝试同步删除定时器,删除失败。 |
调整代码逻辑,避免在中断或软件定时器回调中同步删除软件定时器。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为软件定时器模块,值为0x03。
开发流程
软件定时器的典型开发流程:
打开菜单,进入Kernel ---> Enable Software Timer菜单,完成软件定时器的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_CORE_SWTMR
软件定时器裁剪开关
YES/NO
YES
LOSCFG_BASE_IPC_QUEUE
LOSCFG_BASE_CORE_SWTMR_LIMIT
最大支持的软件定时器数
<65535
1024
LOSCFG_BASE_CORE_SWTMR
LOSCFG_BASE_CORE_SWTMR_IN_ISR
在中断中直接执行回调函数
YES/NO
NO
LOSCFG_BASE_CORE_SWTMR
LOSCFG_BASE_CORE_TSK_SWTMR_STACK_SIZE
(菜单路径为:Kernel ---> Basic Config ---> Task)
软件定时器任务栈大小
[LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE,OS_SYS_MEM_SIZE)
24576
LOSCFG_BASE_CORE_SWTMR
LOSCFG_SWTMR_SYNC_DELETE
支持软件定时器同步删除
YES/NO
NO
LOSCFG_BASE_CORE_SYS_RES_CHECK
LOSCFG_BASE_CORE_SWTMR_ALIGN
支持软件定时器对齐
YES/NO
NO
LOSCFG_BASE_CORE_USE_MULTI_LIST
创建定时器LOS_SwtmrCreate,设置定时器的定时时长、定时器模式、超时后的回调函数。
启动定时器LOS_SwtmrStart。
获得软件定时器剩余Tick数LOS_SwtmrTimeGet。
停止定时器LOS_SwtmrStop。
删除定时器LOS_SwtmrDelete。
注意事项¶
软件定时器的回调函数中不应执行过多操作,不建议使用可能引起任务挂起或者阻塞的接口或操作,如果使用会导致软件定时器响应不及时,可能会影响正常业务功能。
如果没有使能LOSCFG_BASE_CORE_SWTMR_IN_ISR宏,软件定时器将使用系统的一个任务资源。软件定时器任务的优先级设定为0,且不允许修改 。
系统可配置的软件定时器个数是指:整个系统可使用的软件定时器总个数,并非用户可使用的软件定时器个数。例如:系统多占用一个软件定时器,那么用户能使用的软件定时器资源就会减少一个。
创建单次不自删除属性的定时器,用户需要自行调用定时器删除接口删除定时器,回收定时器资源,避免资源泄露。
软件定时器的定时精度与系统Tick时钟的周期有关。
编程实例¶
实例描述
在下面的例子中,演示如下功能:
软件定时器创建、启动、停止、删除操作。
单次软件定时器,周期软件定时器使用方法。
编程示例
前提条件:在menuconfig菜单中完成软件定时器的配置。
代码实现如下:
UINT32 g_timerCount1 = 0;
UINT32 g_timerCount2 = 0;
VOID Timer1_CallBack(UINT32 arg)
{
UINT64 lastTick;
g_timerCount1++;
lastTick=(UINT32)LOS_TickCountGet();
dprintf("g_timerCount1=%d\n", g_timerCount1);
dprintf("tick_last1=%d\n", lastTick);
}
VOID Timer2_CallBack(UINT32 arg)
{
UINT64 lastTick;
lastTick=(UINT32)LOS_TickCountGet();
g_timerCount2++;
dprintf("g_timerCount2=%d\n", g_timerCount2);
dprintf("tick_last2=%d\n", lastTick);
}
VOID Timer_example(VOID)
{
UINT16 id1; // Timer1 id
UINT16 id2; // Timer2 id
UINT32 tick;
LOS_SwtmrCreate(1000, LOS_SWTMR_MODE_ONCE, Timer1_CallBack, &id1, 1);
LOS_SwtmrCreate(100, LOS_SWTMR_MODE_PERIOD, Timer2_CallBack, &id2, 1);
dprintf("create Timer1 success\n");
LOS_SwtmrStart(id1);
dprintf("start Timer1 sucess\n");
LOS_TaskDelay(200);
LOS_SwtmrTimeGet(id1, &tick);
dprintf("tick =%d\n", tick);
LOS_SwtmrStop(id1);
dprintf("stop Timer1 sucess\n");
LOS_SwtmrStart(id1);
LOS_TaskDelay(1000);
LOS_SwtmrDelete(id1);
dprintf("delete Timer1 sucess\n");
LOS_SwtmrStart(id2);
dprintf("start Timer2\n");
LOS_TaskDelay(1000);
LOS_SwtmrStop(id2);
LOS_SwtmrDelete(id2);
}
结果验证
得到的结果为:
Create Timer1 success
start Timer1 sucess
tick =800
Stop Timer1 sucess
g_timerCount1=1201
tick_last1=1201
delete Timer1 success
Start Timer2
g_timerCount2=1
tick_last2=1301
g_timerCount2=2
tick_last2=1401
g_timerCount2=3
tick_last2=1501
g_timerCount2=4
tick_last2=1601
g_timerCount2=5
tick_last2=1701
g_timerCount2=6
tick_last2=1801
g_timerCount2=7
tick_last2=1901
g_timerCount2=8
tick_last2=2001
g_timerCount2=9
tick_last2=2101
g_timerCount2=10
tick_last2=2201
自旋锁¶
概述¶
在多核环境中,由于使用相同的内存空间,存在对同一资源进行访问的情况,所以需要互斥访问机制来保证同一时刻只有一个核进行操作。自旋锁就是这样的一种机制。
自旋锁是指当一个线程在获取锁时,如果锁已经被其它线程获取,那么该线程将循环等待,并不断判断是否能够成功获取锁,直到获取到锁才会退出循环。因此建议保护耗时较短的操作,防止对系统整体性能有明显的影响。
自旋锁与互斥锁比较类似,它们都是为了解决对共享资源的互斥使用问题。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个持有者。但是两者在调度机制上略有不同,对于互斥锁,如果锁已经被占用,锁申请者会被阻塞;但是自旋锁不会引起调用者阻塞,会一直循环检测自旋锁是否已经被释放。
开发指导¶
使用场景
自旋锁可以提供任务之间的互斥访问机制,用来防止两个任务在同一时刻访问相同的共享资源。
功能
LiteOS的自旋锁模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
注意事项 |
|---|---|---|---|
初始化自旋锁 |
LOS_SpinInit |
动态初始化自旋锁。 |
由调用者保证入参合法。 |
SPIN_LOCK_INIT |
静态初始化自旋锁。 |
不涉及。 |
|
申请/释放自旋锁 |
LOS_SpinLock |
申请指定的自旋锁,如果无法获取锁,会一直循环等待。 |
由调用者保证入参合法。 |
LOS_SpinTrylock |
尝试申请指定的自旋锁,如果无法获取锁,直接返回失败,而不会一直循环等待。 |
由调用者保证入参合法。 |
|
LOS_SpinUnlock |
释放指定的自旋锁。 |
由调用者保证入参合法。 |
|
LOS_SpinUnlockNoSched |
释放指定的自旋锁,并继续执行本任务(建议参考注释使用在特殊场景,错误使用会导致调度时序错乱)。 |
由调用者保证入参合法。 |
|
申请/释放自旋锁(同时进行关中断保护) |
LOS_SpinLockSave |
关中断后,再申请指定的自旋锁。 |
由调用者保证入参合法。 |
LOS_SpinUnlockRestore |
先释放指定的自旋锁,再恢复中断状态。 |
由调用者保证入参合法。 |
|
获取自旋锁持有状态 |
LOS_SpinHeld |
检查自旋锁是否已经被持有。 |
由调用者保证入参合法。 |
开发流程
自旋锁的开发典型流程:
自旋锁依赖于SMP,打开菜单,配置项的菜单路径为:Kernel ---> Enable Kernel SMP。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_SMP
SMP控制开关
YES/NO
YES
硬件支持多核
LOSCFG_KERNEL_SMP_CORE_NUM
多核core数量
与架构相关
2
无
创建自旋锁:使用LOS_SpinInit初始化自旋锁,或者使用SPIN_LOCK_INIT初始化静态内存的自旋锁。
申请自旋锁:使用接口LOS_SpinLock/LOS_SpinTrylock/LOS_SpinLockSave申请指定的自旋锁,申请成功就继续往后执行锁保护的代码;申请失败在自旋锁申请中忙等,直到申请到自旋锁为止。
释放自旋锁:使用LOS_SpinUnlock/LOS_SpinUnlockRestore接口释放自旋锁。锁保护代码执行完毕后,释放对应的自旋锁,以便其他核申请自旋锁。
注意事项¶
主要注意函数入参的合法性由调用者保证。
同一个任务不能对同一把自旋锁进行多次加锁,否则会导致死锁。
自旋锁中会执行本核的锁任务操作,因此需要等到最外层完成解锁后本核才会进行任务调度。
LOS_SpinLock与LOS_SpinUnlock允许单独使用,即可以不进行关中断,但是用户需要保证使用的接口只会在任务或中断中使用。如果接口同时会在任务和中断中被调用,请使用LOS_SpinLockSave与LOS_SpinUnlockRestore,因为在未关中断的情况下使用LOS_SpinLock可能会导致死锁。
耗时的操作谨慎选用自旋锁,可使用互斥锁进行保护。
未开启SMP的单核场景下,自旋锁功能无效,只有LOS_SpinLockSave与LOS_SpinUnlockRestore接口有关闭恢复中断功能。
建议LOS_SpinLock和LOS_SpinUnlock、LOS_SpinLockSave和LOS_SpinUnlockRestore配对使用,避免出错。
编程实例¶
实例描述
本实例实现如下流程。
任务Example_TaskEntry初始化自旋锁,创建两个任务Example_SpinTask1、Example_SpinTask2,分别运行于两个核。
Example_SpinTask1、Example_SpinTask2中均执行申请自旋锁的操作,同时为了模拟实际操作,在持有自旋锁后进行延迟操作,最后释放自旋锁。
300Tick后任务Example_TaskEntry被调度运行,删除任务Example_SpinTask1和Example_SpinTask2。
须知: 多核的运行时序不是固定的,因此也存在任务执行顺序不同的情况。
编程示例
前提条件:在menuconfig中,将LOSCFG_KERNEL_SMP配置项打开,并设置多核core数量。
代码实现如下:
#include "los_spinlock.h"
#include "los_task.h"
/* 自旋锁句柄id */
SPIN_LOCK_S g_testSpinlock;
/* 任务ID */
UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
VOID Example_SpinTask1(VOID)
{
UINT32 i;
UINTPTR intSave;
/* 申请自旋锁 */
dprintf("task1 try to get spinlock\n");
LOS_SpinLockSave(&g_testSpinlock, &intSave);
dprintf("task1 got spinlock\n");
for(i = 0; i < 5000; i++) {
asm volatile("nop");
}
/* 释放自旋锁 */
dprintf("task1 release spinlock\n");
LOS_SpinUnlockRestore(&g_testSpinlock, intSave);
return;
}
VOID Example_SpinTask2(VOID)
{
UINT32 i;
UINTPTR intSave;
/* 申请自旋锁 */
dprintf("task2 try to get spinlock\n");
LOS_SpinLockSave(&g_testSpinlock, &intSave);
dprintf("task2 got spinlock\n");
for(i = 0; i < 5000; i++) {
asm volatile("nop");
}
/* 释放自旋锁 */
dprintf("task2 release spinlock\n");
LOS_SpinUnlockRestore(&g_testSpinlock, intSave);
return;
}
UINT32 Example_TaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S stTask1;
TSK_INIT_PARAM_S stTask2;
/* 初始化自旋锁 */
LOS_SpinInit(&g_testSpinlock);
/* 创建任务1 */
memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SpinTask1;
stTask1.pcName = "SpinTsk1";
stTask1.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
stTask1.usTaskPrio = 5;
#ifdef LOSCFG_KERNEL_SMP
/* 绑定任务到CPU0运行 */
stTask1.usCpuAffiMask = CPUID_TO_AFFI_MASK(0);
#endif
ret = LOS_TaskCreate(&g_testTaskId01, &stTask1);
if(ret != LOS_OK) {
dprintf("task1 create failed .\n");
return LOS_NOK;
}
/* 创建任务2 */
memset(&stTask2, 0, sizeof(TSK_INIT_PARAM_S));
stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SpinTask2;
stTask2.pcName = "SpinTsk2";
stTask2.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
stTask2.usTaskPrio = 5;
#ifdef LOSCFG_KERNEL_SMP
/* 绑定任务到CPU1运行 */
stTask1.usCpuAffiMask = CPUID_TO_AFFI_MASK(1);
#endif
ret = LOS_TaskCreate(&g_testTaskId02, &stTask2);
if(ret != LOS_OK) {
dprintf("task2 create failed .\n");
return LOS_NOK;
}
/* 任务休眠300Ticks */
LOS_TaskDelay(300);
/* 删除任务1 */
ret = LOS_TaskDelete(g_testTaskId01);
if(ret != LOS_OK) {
dprintf("task1 delete failed .\n");
return LOS_NOK;
}
/* 删除任务2 */
ret = LOS_TaskDelete(g_testTaskId02);
if(ret != LOS_OK) {
dprintf("task2 delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
结果验证
编译运行得到的结果为:
task2 try to get spinlock
task2 got spinlock
task1 try to get spinlock
task2 release spinlock
task1 got spinlock
task1 release spinlock
分级初始化¶
概述¶
基本概念
分级初始化将Kernel的启动过程分成多个阶段,阶段之间存在依赖关系,后初始化的阶段可能会依赖前初始化阶段的接口。对于Kernel启动过程新增的调用接口,不再需要修改启动代码添加函数调用,可通过分级初始化注册宏进行接口注册,Kernel在启动时,按照顺序自动执行所有注册的初始化接口。
运作机制
每一个需要在Kernel初始化时执行的函数,通过使用分级启动宏都会生成一个函数指针变量,指向该函数。
编译生成镜像时,链接脚本将上述的函数指针变量冠段。
Kernel初始化,从冠段地址取出所有函数指针,依次执行所有初始化函数。
开发指导¶
使用场景
分级初始化,可以实现Kernel初始化模块与其他模块的充分解耦。Kernel初始化模块不再感知具体模块的初始化接口,初始化接口也不用关心如何被调用。
功能
LiteOS中的分级初始化模块提供下面几种功能,接口详细信息可以查看API参考。
接口名 |
描述 |
|---|---|
LOS_SYS_INIT |
注册一个初始化函数,在Kernel阶段会运行该函数。 |
层级划分:分级初始化时,下表中越靠前的层级,优先级越高,越先得到执行。
启动层级宏 |
描述 |
|---|---|
SYS_INIT_LEVEL_EARLY |
Kernel启动早期阶段,该阶段仅供用户使用,并且注册到该阶段的初始化函数不可依赖Kernel资源。 |
SYS_INIT_LEVEL_AHEAD |
Kernel启动预备阶段,该阶段仅供系统模块使用,为内核初始化做准备。 |
SYS_INIT_LEVEL_ARCH |
该阶段仅供系统模块使用,架构相关初始化。 |
SYS_INIT_LEVEL_KERNEL |
该阶段仅供系统模块使用,内核基础子模块初始化。 |
SYS_INIT_LEVEL_KERNEL_ADDITION |
该阶段仅供系统模块使用,扩展内核模块初始化。 |
SYS_INIT_LEVEL_COMPONENT |
该阶段仅供系统模块使用,组件相关初始化。 |
SYS_INIT_LEVEL_APP |
应用程序初始化。 |
SYS_INIT_LEVEL_RESERVE |
Kernel启动后期阶段,该阶段供用户使用,注册到该阶段的初始化函数可依赖Kernel资源。 |
优先级划分:同一层级的初始化函数之间,可能存在依赖关系。sync优先级只能用于设置同一层级内的函数优先级。sync值越小,优先级越高,越先执行。
优先级宏 |
描述 |
|---|---|
SYS_INIT_SYNC_0 |
sync 0优先级,在同一层级内最先执行。 |
SYS_INIT_SYNC_1 |
sync 1优先级。 |
SYS_INIT_SYNC_2 |
sync 2优先级。 |
SYS_INIT_SYNC_3 |
sync 3优先级。 |
SYS_INIT_SYNC_4 |
sync 4优先级。 |
开发流程
确定初始化接口的依赖关系,进而确定层级及优先级。
调用LOS_SYS_INIT注册初始化接口。
注意事项¶
分级初始化适用于类型为typedef unsigned int (*SysInitcallFunc)(void)的初始化函数。
由于不会在代码中直接调用初始化函数,一些编译器可能会因此优化掉初始化函数,导致注册失败,建议使用-u链接选项。
同一个层级、sync优先级内,可以注册多个初始化接口,但这些接口之间不能存在依赖关系。
编程实例¶
实例描述
在下面的实例中,描述了分级初始化注册接口的使用方法:
确定初始化接口的依赖关系、层级及优先级。
注册初始化接口。
编程示例
#include "stdio.h"
#include "los_init.h"
#include "los_list.h"
#include "los_hwi.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cpluscplus */
#endif /* __cpluscplus */
UINT32 g_count;
LOS_DL_LIST g_DLlist;
UINT32 InitLevelTest1(VOID)
{
dprintf("InitLevelTest1 init\n");
g_count = 10;
LOS_ListInit(&g_DLlist);
return LOS_OK;
}
/* InitLevelTest1 has no dependence, set SYS_INIT_LEVEL_EARLY level */
LOS_SYS_INIT(InitLevelTest1, SYS_INIT_LEVEL_EARLY, SYS_INIT_SYNC_0);
STATIC VOID HwiUsrIrq(VOID)
{
g_count++;
}
UINT32 InitLevelTest2(VOID)
{
UINT32 ret;
HWI_HANDLE_T irqNum = 26;
HWI_PRIOR_T irqPri = 0x3;
dprintf("InitLevelTest2 init\n");
ret = LOS_HwiCreate(irqNum, irqPri, 0, (HWI_PROC_FUNC)HwiUsrIrq, 0);
if (ret != LOS_OK) {
return LOS_NOK;
}
return ret ;
}
/* InitLevelTest2 rely on hwi module, set SYS_INIT_LEVEL_RESERVE level */
LOS_SYS_INIT(InitLevelTest2, SYS_INIT_LEVEL_RESERVE, SYS_INIT_SYNC_0);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cpluscplus */
#endif /* __cpluscplus */
结果验证
编译运行得到的结果为:
InitLevelTest1 init
InitLevelTest2 init
其他¶
时间管理¶
概述¶
基本概念
时间管理以系统时钟为基础,给应用程序提供所有和时间有关的服务。
系统时钟是由定时器/计数器产生的输出脉冲触发中断产生的,一般定义为整数或长整数。输出脉冲的周期叫做一个“时钟滴答”。系统时钟也称为时标或者Tick。
用户以秒、毫秒为单位计时,而操作系统以Tick为单位计时,当用户需要对系统进行操作时,例如任务挂起、延时等,此时需要时间管理模块对Tick和秒/毫秒进行转换。
LiteOS的时间管理模块提供时间转换、统计、延迟功能。
相关概念
Cycle
系统最小的计时单位。Cycle的时长由系统主时钟频率决定,系统主时钟频率就是每秒钟的Cycle数。
Tick
Tick是操作系统的基本时间单位,由用户配置的每秒Tick数决定。
开发指导¶
使用场景
用户需要了解当前系统运行的时间以及Tick与秒、毫秒之间的转换关系等。
功能
LiteOS的时间管理提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
时间转换 |
LOS_MS2Tick |
采用向上取整的计算方法将毫秒转换成Tick。 |
LOS_Tick2MS |
Tick转化为毫秒。 |
|
时间统计 |
LOS_CyclePerTickGet |
每个Tick多少Cycle数。 |
LOS_TickCountGet |
获取自系统启动以来的Tick数。 |
|
LOS_GetCpuCycle |
获取自系统启动以来的Cycle数。 |
|
LOS_CurrNanosec |
获取自系统启动以来的纳秒数。 |
|
延时管理 |
LOS_Udelay |
以μs为单位的忙等,但可以被优先级更高的任务抢占。 |
LOS_Mdelay |
以ms为单位的忙等,但可以被优先级更高的任务抢占。 |
时间管理错误码
时间转换存在出错的可能性,需要返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_TICK_CFG_INVALID |
0x02000400 |
无效的系统Tick配置。 |
在板级配置适配时配置有效的系统主时钟频率OS_SYS_CLOCK,打开菜单配置有效的LOSCFG_BASE_CORE_TICK_PER_SECOND。 |
开发流程
时间管理的典型开发流程:
根据实际需求,在板级配置适配时确认是否使能LOSCFG_BASE_CORE_TICK_HW_TIME宏选择外部定时器,并配置系统主时钟频率OS_SYS_CLOCK(单位Hz)。OS_SYS_CLOCK的默认值基于硬件平台配置。
打开菜单配置LOSCFG_BASE_CORE_TICK_PER_SECOND,配置项的菜单路径为:Kernel --->Basic Config --->Task。
配置项
含义
取值范围
默认值
依赖
LOSCFG_BASE_CORE_TICK_PER_SECOND
每秒Tick数
(0,1000]
100
无
调用时钟转换/统计接口。
注意事项¶
时间管理不是单独的功能模块,依赖于OS_SYS_CLOCK和LOSCFG_BASE_CORE_TICK_PER_SECOND两个配置选项。
系统的Tick数在关中断的情况下不进行计数,故系统Tick数不能作为准确时间使用。
编程实例¶
实例描述
在下面的例子中,介绍了时间管理的基本方法,包括:
时间转换:将毫秒数转换为Tick数,或将Tick数转换为毫秒数。
时间统计:每Tick的Cycle数、自系统启动以来的Tick数和延迟后的Tick数。
编程示例
前提条件:
使用每秒的Tick数LOSCFG_BASE_CORE_TICK_PER_SECOND的默认值100。
配好OS_SYS_CLOCK系统主时钟频率。
时间转换:
VOID Example_TransformTime(VOID)
{
UINT32 ms;
UINT32 tick;
tick = LOS_MS2Tick(10000); // 10000ms转换为tick
dprintf("tick = %d \n",tick);
ms = LOS_Tick2MS(100); // 100tick转换为ms
dprintf("ms = %d \n",ms);
}
时间统计和时间延迟:
VOID Example_GetTime(VOID)
{
UINT32 cyclePerTick;
UINT64 tickCount;
cyclePerTick = LOS_CyclePerTickGet();
if(0 != cyclePerTick) {
dprintf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
}
tickCount = LOS_TickCountGet();
if(0 != tickCount) {
dprintf("LOS_TickCountGet = %d \n", (UINT32)tickCount);
}
LOS_TaskDelay(200);
tickCount = LOS_TickCountGet();
if(0 != tickCount) {
dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount);
}
}
结果验证
编译运行得到的结果为:
时间转换:
tick = 1000
ms = 1000
时间统计和时间延迟:
LOS_CyclePerTickGet = 495000
LOS_TickCountGet = 1
LOS_TickCountGet after delay = 201
双向链表¶
概述¶
基本概念
双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向前一个节点的指针。其头指针head是唯一确定的。
从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构形式使得双向链表在查找时更加方便,特别是大量数据的遍历。由于双向链表具有对称性,能方便地完成各种插入、删除等操作,但需要注意前后方向的操作。
开发指导¶
功能
LiteOS的双向链表模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
注意事项 |
|---|---|---|---|
初始化链表 |
LOS_ListInit |
将指定节点初始化为双向链表节点。 |
由调用者保证入参合法。 |
LOS_DL_LIST_HEAD |
定义一个节点并初始化为双向链表节点。 |
不涉及。 |
|
增加节点 |
LOS_ListAdd |
将指定节点插入到双向链表头端。 |
由调用者保证入参合法。 |
LOS_ListHeadInsert |
将指定节点插入到双向链表头端,同LOS_ListAdd。 |
由调用者保证入参合法。 |
|
LOS_ListTailInsert |
将指定节点插入到双向链表尾端。 |
由调用者保证入参合法。 |
|
删除节点 |
LOS_ListDelete |
将指定节点从链表中删除。 |
由调用者保证入参合法。 |
LOS_ListDelInit |
将指定节点从链表中删除,并使用该节点初始化链表。 |
由调用者保证入参合法。 |
|
判断双向链表是否为空 |
LOS_ListEmpty |
判断链表是否为空。 |
由调用者保证入参合法。 |
获取节点 |
LOS_DL_LIST_LAST |
获取指定节点的前驱结点。 |
不涉及。 |
LOS_DL_LIST_FIRST |
获取指定节点的后继结点。 |
不涉及。 |
|
获取结构体信息 |
LOS_DL_LIST_ENTRY |
获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称。 |
不涉及。 |
LOS_OFF_SET_OF |
获取指定结构体内的成员相对于结构体起始地址的偏移量。 |
不涉及。 |
|
遍历双向链表 |
LOS_DL_LIST_FOR_EACH |
遍历双向链表。 |
不涉及。 |
LOS_DL_LIST_FOR_EACH_SAFE |
遍历双向链表,并存储当前节点的后继节点用于安全校验。 |
不涉及。 |
|
遍历包含双向链表的结构体 |
LOS_DL_LIST_FOR_EACH_ENTRY |
遍历指定双向链表,获取包含该链表节点的结构体地址。 |
不涉及。 |
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE |
遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址。 |
不涉及。 |
|
LOS_DL_LIST_FOR_EACH_ENTRY_HOOK |
遍历指定双向链表,获取包含该链表节点的结构体地址,并在每次循环中调用钩子函数。 |
不涉及。 |
开发流程
双向链表的典型开发流程:
调用LOS_ListInit/LOS_DL_LIST_HEAD初始双向链表。
调用LOS_ListAdd/LOS_ListHeadInsert向链表头部插入节点。
调用LOS_ListTailInsert向链表尾部插入节点。
调用LOS_ListDelete删除指定节点。
调用LOS_ListEmpty判断链表是否为空。
调用LOS_ListDelInit删除指定节点并以此节点初始化链表。
注意事项¶
需要注意函数入参的合法性由调用者保证。
需要注意节点指针前后方向的操作。
链表操作接口为底层接口,不对入参进行判空,需要使用者确保传参合法。
如果链表节点的内存是动态申请的,删除节点时,要注意释放内存。
编程实例¶
实例描述
本实例实现如下功能:
初始化双向链表。
增加节点。
删除节点。
测试操作是否成功。
编程示例
代码实现如下:
#include "stdio.h"
#include "los_list.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cpluscplus */
#endif /* __cpluscplus */
static UINT32 DLlist_sample(VOID)
{
LOS_DL_LIST DLlist = {NULL,NULL};
LOS_DL_LIST DLlistNode01 = {NULL,NULL};
LOS_DL_LIST DLlistNode02 = {NULL,NULL};
LOS_DL_LIST DLlistNode03 = {NULL,NULL};
dprintf("Initial head\n");
LOS_ListInit(&DLlist);
LOS_ListAdd(&DLlist, &DLlistNode01);
if (DLlistNode01.pstNext == &DLlist && DLlistNode01.pstPrev == &DLlist) {
dprintf("Add DLlistNode01 success \n");
}
LOS_ListTailInsert(&DLlist, &DLlistNode02);
if (DLlistNode02.pstNext == &DLlist && DLlistNode02.pstPrev == &DLlistNode01) {
dprintf("Tail insert DLlistNode02 success \n");
}
LOS_ListHeadInsert(&DLlistNode02, &DLlistNode03);
if (DLlistNode03.pstNext == &DLlist && DLlistNode03.pstPrev == &DLlistNode02) {
dprintf("Head insert DLlistNode03 success \n");
}
LOS_ListDelInit(&DLlistNode03);
LOS_ListDelete(&DLlistNode01);
LOS_ListDelete(&DLlistNode02);
if (LOS_ListEmpty(&DLlist)) {
dprintf("Delete success \n");
}
return LOS_OK;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cpluscplus */
#endif /* __cpluscplus */
结果验证
编译运行得到的结果为:
Initial head
Add DLlistNode01 success
Tail insert DLlistNode02 success
Head insert DLlistNode03 success
Delete success
原子操作¶
概述¶
基本概念
在支持多任务的操作系统中,修改一块内存区域的数据需要“读取-修改-写入”三个步骤。然而同一内存区域的数据可能同时被多个任务访问,如果在修改数据的过程中被其他任务打断,就会造成该操作的执行结果无法预知。
使用开关中断的方法固然可以保证多任务执行结果符合预期,但这种方法显然会影响系统性能。
ARMv6架构引入了LDREX和STREX指令,以支持对共享存储器更缜密的非阻塞同步。由此实现的原子操作能确保对同一数据的“读取-修改-写入”操作在它的执行期间不会被打断,即操作的原子性。
运作机制
LiteOS通过对ARMv6架构中的LDREX和STREX进行封装,向用户提供了一套原子操作接口。
LDREX Rx, [Ry]
读取内存中的值,并标记对该段内存为独占访问:
读取寄存器Ry指向的4Byte内存数据,保存到Rx寄存器中。
对Ry指向的内存区域添加独占访问标记。
STREX Rf, Rx, [Ry]
检查内存是否有独占访问标记,如果有则更新内存值并清空标记,否则不更新内存:
有独占访问标记
将寄存器Rx中的值更新到寄存器Ry指向的内存。
标志寄存器Rf置为0。
没有独占访问标记
不更新内存。
标志寄存器Rf置为1。
判断标志寄存器
标志寄存器为0时,退出循环,原子操作结束。
标志寄存器为1时,继续循环,重新进行原子操作。
开发指导¶
使用场景
有多个任务对同一个内存数据进行加减或交换操作时,使用原子操作保证结果的可预知性。
功能
LiteOS的原子数据包含两种类型Atomic(有符号32位数)与 Atomic64(有符号64位数)。原子操作模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
注意事项 |
|---|---|---|---|
读 |
LOS_AtomicRead |
读取内存数据。 |
由调用者保证入参合法。 |
写 |
LOS_AtomicSet |
写入内存数据。 |
由调用者保证入参合法。 |
加 |
LOS_AtomicAdd |
对内存数据做加法。 |
由调用者保证入参合法。 |
LOS_AtomicSub |
对内存数据做减法。 |
由调用者保证入参合法。 |
|
LOS_AtomicInc |
对内存数据加1。 |
由调用者保证入参合法。 |
|
LOS_AtomicIncRet |
对内存数据加1并返回运算结果。 |
由调用者保证入参合法。 |
|
减 |
LOS_AtomicDec |
对内存数据减1。 |
由调用者保证入参合法。 |
LOS_AtomicDecRet |
对内存数据减1并返回运算结果。 |
由调用者保证入参合法。 |
|
交换 |
LOS_AtomicXchg32bits |
交换内存数据,原内存中的值以返回值的方式返回。 |
由调用者保证入参合法。 |
LOS_AtomicCmpXchg32bits |
比较并交换内存数据,返回比较结果。 |
由调用者保证入参合法。 |
功能分类 |
接口名 |
描述 |
注意事项 |
|---|---|---|---|
读 |
LOS_Atomic64Read |
读取64位内存数据。 |
由调用者保证入参合法。 |
写 |
LOS_Atomic64Set |
写入64位内存数据。 |
由调用者保证入参合法。 |
加 |
LOS_Atomic64Add |
对64位内存数据做加法。 |
由调用者保证入参合法。 |
LOS_Atomic64Sub |
对64位内存数据做减法。 |
由调用者保证入参合法。 |
|
LOS_Atomic64Inc |
对64位内存数据加1。 |
由调用者保证入参合法。 |
|
LOS_Atomic64IncRet |
对64位内存数据加1并返回运算结果。 |
由调用者保证入参合法。 |
|
减 |
LOS_Atomic64Dec |
对64位内存数据减1。 |
由调用者保证入参合法。 |
LOS_Atomic64DecRet |
对64位内存数据减1并返回运算结果。 |
由调用者保证入参合法。 |
|
交换 |
LOS_AtomicXchg64bits |
交换64位内存数据,原内存中的值以返回值的方式返回。 |
由调用者保证入参合法。 |
LOS_AtomicCmpXchg64bits |
比较并交换64位内存数据,返回比较结果。 |
由调用者保证入参合法。 |
须知: 原子操作中,操作数及其结果不能超过函数所支持位数的最大值。
平台差异性
无。
注意事项¶
需注意函数入参的合法性由调用者保证。
目前原子操作接口只支持整型数据。
原子操作只能保证操作的原子性,不能保证操作不会出现翻转溢出。
编程实例¶
实例描述
调用原子操作相关接口,观察结果:
创建两个任务。
任务一用LOS_AtomicInc对全局变量加100次。
任务二用LOS_AtomicDec对全局变量减100次。
子任务结束后在主任务中打印全局变量的值。
编程示例
#include "los_hwi.h"
#include "los_atomic.h"
#include "los_task.h"
UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
Atomic g_sum;
Atomic g_count;
UINT32 Example_Atomic01(VOID)
{
int i = 0;
for(i = 0; i < 100; ++i) {
LOS_AtomicInc(&g_sum);
}
LOS_AtomicInc(&g_count);
return LOS_OK;
}
UINT32 Example_Atomic02(VOID)
{
int i = 0;
for(i = 0; i < 100; ++i) {
LOS_AtomicDec(&g_sum);
}
LOS_AtomicInc(&g_count);
return LOS_OK;
}
UINT32 Example_TaskEntry(VOID)
{
TSK_INIT_PARAM_S stTask1={0};
stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Atomic01;
stTask1.pcName = "TestAtomicTsk1";
stTask1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
stTask1.usTaskPrio = 4;
stTask1.uwResved = LOS_TASK_STATUS_DETACHED;
TSK_INIT_PARAM_S stTask2={0};
stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Atomic02;
stTask2.pcName = "TestAtomicTsk2";
stTask2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
stTask2.usTaskPrio = 4;
stTask2.uwResved = LOS_TASK_STATUS_DETACHED;
LOS_TaskLock();
LOS_TaskCreate(&g_testTaskId01, &stTask1);
LOS_TaskCreate(&g_testTaskId02, &stTask2);
LOS_TaskUnlock();
while(LOS_AtomicRead(&g_count) != 2);
dprintf("g_sum = %d\n", g_sum);
return LOS_OK;
}
结果验证
编译运行得到的结果为:
g_sum = 0
位操作¶
概述¶
基本概念
位操作是指对二进制数的bit位进行操作。程序可以设置某一变量为状态字,状态字中的每一bit位(标志位)可以具有自定义的含义。
开发指导¶
使用场景
系统提供标志位的置1和清0操作,可以改变标志位的内容,同时还提供获取状态字中标志位为1的最高位和最低位的功能。用户也可以对系统的寄存器进行位操作。
功能
LiteOS的位操作模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
置1/清0标志位 |
LOS_BitmapSet |
对状态字的某一标志位进行置1操作。 |
LOS_BitmapClr |
对状态字的某一标志位进行清0操作。 |
|
获取标志位为1的bit位 |
LOS_HighBitGet |
获取状态字中为1的最高位。 |
LOS_LowBitGet |
获取状态字中为1的最低位。 |
注意事项¶
无。
编程实例¶
实例描述
对数据实现位操作,本实例实现如下功能:
某一标志位置1。
获取标志位为1的最高bit位。
某一标志位清0。
获取标志位为1的最低bit位。
编程示例
#include "los_bitmap.h"
#include "los_printf.h"
static UINT32 Bit_Sample(VOID)
{
UINT32 flag = 0x10101010;
UINT16 pos;
dprintf("\nBitmap Sample!\n");
dprintf("The flag is 0x%8x\n", flag);
pos = 8;
LOS_BitmapSet(&flag, pos);
dprintf("LOS_BitmapSet:\t pos : %d, the flag is 0x%0+8x\n", pos, flag);
pos = LOS_HighBitGet(flag);
dprintf("LOS_HighBitGet:\t The highest one bit is %d, the flag is 0x%0+8x\n", pos, flag);
LOS_BitmapClr(&flag, pos);
dprintf("LOS_BitmapClr:\t pos : %d, the flag is 0x%0+8x\n", pos, flag);
pos = LOS_LowBitGet(flag);
dprintf("LOS_LowBitGet:\t The lowest one bit is %d, the flag is 0x%0+8x\n\n", pos, flag);
return LOS_OK;
}
结果验证
编译运行得到结果:
Bitmap Sample!
The flag is 0x10101010
LOS_BitmapSet: pos : 8, the flag is 0x10101110
LOS_HighBitGet:The highest one bit is 28, the flag is 0x10101110
LOS_BitmapClr: pos : 28, the flag is 0x00101110
LOS_LowBitGet: The lowest one bit is 4, the flag is 0x00101110
环形缓冲(ringbuf)¶
概述¶
基本概念
环形缓冲(ringbuf)作为数据的环形缓冲机制,可以实现数据流以先入先出的方式进行缓存。
ringbuf控制块
typedef struct {
UINT32 startIdx; /* ringbuf读下标 */
UINT32 endIdx; /* ringbuf写下标 */
UINT32 size; /* ringbuf总大小,单位为字节 */
UINT32 remain; /* ringbuf剩余可用大小,单位为字节 */
SPIN_LOCK_S lock; /* ringbuf自旋锁 */
RingbufStatus status /* ringbuf状态 */
RingbufFlag flag; /* Ringbuf flag */
CHAR *fifo; /* ringbuf缓冲区起始地址 */
} Ringbuf;
每个ringbuf有两种状态:
RBUF_UNINIT:ringbuf未初始化。
RBUF_INITED:ringbuf已初始化。
每个ringbuf有两种模式:
RBUF_NORMAL:ringbuf正常模式。
RBUF_OVERWRITE:ringbuf可覆盖写模式。
运作机制
图 1 环形缓冲运作示意图

ringbuf控制块中维护着缓冲区的起始地址,和读/写指针的相对偏移。startIdx是读指针相对于ringbuf缓冲区起始地址的偏移,endIdx是写指针的相对偏移。startIdx与endIdx之间为缓冲的数据。ringbuf刚创建时,读写指针偏移均为0。
写入数据到ringbuf时,先判断ringbuf标志。标志为RBUF_NORMAL时,处于正常模式;标志为RBUF_OVERWRITE时,处于覆盖写模式。
正常模式:写入数据到ringbuf时,先判断ringbuf是否还有剩余可用内存。当还有可用内存时才会继续写入,endIdx增长,并减少ringbuf的剩余可用内存;否则丢弃写入数据,可根据LOS_RingbufWrite()的返回值判断写入的数据量。如果ringbuf已经存满数据,因为不会覆盖旧数据,所以无法继续写入。
可覆盖写模式:写入数据到ringbuf时,先判断ringbuf要写入的数据。当要写入数据大于size时,清空ringbuf,仅写入size长度数据;当要写入数据小于size且大于剩余内存remain时,清理一部分最早缓存的数据,再写入数据;当要写入数据小于剩余内存remain时,直接写入;最后均返回实际写入长度。
读取数据时,会按照先入先出的方式,读取最早缓存的数据,startIdx增长,并增加ringbuf的剩余可用内存。读取数据后,因为读指针向后移动,所以无法再次读取相同的数据。
endIdx增长到等于startIdx时,表示ringbuf已经写满,所以无法继续写入,需要读取出数据才能继续写数据到ringbuf。
开发指导¶
使用场景
ringbuf用于数据缓冲,遵循先入先出的原则。
功能
LiteOS中的ringbuf模块提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化/重置环形缓冲 |
LOS_RingbufInit |
初始化环形缓冲区,缓冲区内存、大小和标志通过入参传入。 |
LOS_RingbufReset |
重置缓冲区的状态,读写偏移重置为0,且清除缓冲区的数据。 |
|
读/写环形缓冲 |
LOS_RingbufRead |
从环形缓冲区中读取指定大小的数据到指定地址空间,并返回读取到的数据量。 |
LOS_RingbufPeek |
从环形缓冲区中预读指定大小的数据到指定地址空间,并返回读取到的数据量。 |
|
LOS_RingbufWrite |
写入指定大小的数据到环形缓冲区,并返回成功写入到缓冲区的数据量。 |
|
获取缓冲数据量 |
LOS_RingbufUsedSize |
获取环形缓冲区中已缓存的数据量。 |
开发流程
使用ringbuf模块开发的典型流程如下:
打开菜单,进入Kernel ---> Enable Ringbuf菜单,完成ringbuf模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_RINGBUF
ringbuf裁剪开关
YES/NO
NO
无
为ringbuf申请一块内存;
通过LOS_RingbufInit初始化ringbuf。
写ringbuf。
在合适时机读ringbuf。
注意事项¶
ringbuf写接口行为由LOS_RingbufInit接口在初始化时决定。
通过LOS_RingbufRead接口读取缓冲区的数据后,该数据无法被再次读取。
LOS_RingbufPeek接口仅预读缓冲区数据,未消费该数据,该数据仍然可以被读取。
正常模式下,缓冲区写满时,无法继续写入,请检查LOS_RingbufWrite的返回值以确认实际写入的数据量。
使用完ringbuf后,需要用户自己释放申请的内存。
编程实例¶
实例描述
创建一个ringbuf,两个任务。任务1定时写入数据到ringbuf,任务2在ringbuf数据达到一定量时,取出数据。
锁任务调度,通过LOS_TaskCreate创建任务1和任务2。
申请一块内存用作环形缓冲区。
通过LOS_RingbufInit初始化一个缓冲区。
解锁任务调度。
任务1往环形缓冲区中写入数据。
任务2判断数据达到水线后,读取数据。
编程示例
static Ringbuf g_Ringbuf = {0};
#define READ_TRIGGER_THRESHOLD 32
#define RINGBUF_SIZE 128
VOID WriteTask(VOID)
{
UINT32 i = 0;
UINT32 ret = 0;
CHAR data[READ_TRIGGER_THRESHOLD * 2 + 1] = "LiteOS test data, send in WriteTask, receive & check in ReadTask";
for (;;) {
ret = LOS_RingbufWrite(&g_Ringbuf, data + i, READ_TRIGGER_THRESHOLD / 2);
if (ret != READ_TRIGGER_THRESHOLD / 2) {
PRINT_ERR("real write count is %u\n", ret);
}
i += ret;
if (i >= strlen(data)) {
break;
} else {
LOS_Msleep(1000); /* 1000: sleep 1000ms */
}
}
}
VOID ReadTask(VOID)
{
UINT32 i = 0;
UINT32 ret = 0;
UINT32 count = 0;
CHAR data[READ_TRIGGER_THRESHOLD * 2 + 1] = {0};
for (;;) {
count = LOS_RingbufUsedSize(&g_Ringbuf);
if (count >= READ_TRIGGER_THRESHOLD) {
ret = LOS_RingbufRead(&g_Ringbuf, data + i, count);
i += ret;
PRINTK("read data count %u \n", i);
if (i == READ_TRIGGER_THRESHOLD * 2) {
PRINTK("data receive done: %s\n", data);
break;
}
} else {
PRINTK("info: data count %u < %u\n", count, READ_TRIGGER_THRESHOLD);
}
LOS_Msleep(1000); /* 1000: sleep 1000ms */
}
}
UINT32 SampleTaskCreat(VOID)
{
UINT32 ret = 0;
UINT32 task1, task2;
CHAR *buff = NULL;
TSK_INIT_PARAM_S initParam;
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)WriteTask;
initParam.usTaskPrio = LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO;
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
initParam.pcName = "WriteTask";
#ifdef LOSCFG_KERNEL_SMP
initParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endif
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
LOS_TaskLock();
ret = LOS_TaskCreate(&task1, &initParam);
if(ret != LOS_OK) {
PRINT_ERR("create WriteTask failed, error: %x\n", ret);
return ret;
}
initParam.pcName = "ReadTask";
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ReadTask;
ret = LOS_TaskCreate(&task2, &initParam);
if(ret != LOS_OK) {
PRINT_ERR("create ReadTask failed, error: %x\n", ret);
return ret;
}
buff = (CHAR *)LOS_MemAlloc(m_aucSysMem0, RINGBUF_SIZE);
if (buff == NULL) {
PRINT_ERR("malloc falied");
return LOS_NOK;
}
ret = LOS_RingbufInit(&g_Ringbuf, buff, RINGBUF_SIZE, RBUF_NORMAL);
if (ret != LOS_OK) {
PRINT_ERR("ringbuf init falied");
return LOS_NOK;
}
PRINTK("create the ringbuf success!\n");
LOS_TaskUnlock();
return ret;
}
结果验证
编译运行得到的结果为:
create the ringbuf success!
info: data count 16 < 32
read data count 32
info: data count 16 < 32
read data count 64
data receive done: LiteOS test data, send in WriteTask, receive & check in ReadTask
通用内存访问¶
概述¶
基本概念
当寄存器或内存位于内存空间时,称为IO内存。LiteOS提供了一组通用接口来操作I/O内存。
开发指导¶
使用场景
系统提供读写(1、2、4、8)字节寄存器操作,方便编写驱动代码。
功能
LiteOS为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
读/写64位数据 |
WRITE64(addr,value) |
将64位数据(value)写入64位寄存器(addr)。 |
READ64(addr,value) |
读取64位寄存器(addr)。 |
|
WRITE64_MB(addr,value) |
将64位数据(value)写入64位寄存器(addr),写入前使用数据同步屏障同步。 |
|
READ64_MB(addr,value) |
读取64位寄存器(addr),读取前使用数据同步屏障同步。 |
|
读/写32位数据 |
WRITE32(addr,value) |
将32位数据(value)写入32位寄存器(addr)。 |
READ32(addr,value) |
读取32位寄存器(addr)。 |
|
WRITE32_MB(addr,value) |
将32位数据(value)写入32位寄存器(addr),写入前使用数据同步屏障同步。 |
|
READ32_MB(addr,value) |
读取32位寄存器(addr),读取前使用数据同步屏障同步。 |
|
读/写16位数据 |
WRITE16(addr,value) |
将16位数据(value)写入16位寄存器(addr)。 |
READ16(addr,value) |
读取16位寄存器(addr)。 |
|
WRITE16_MB(addr,value) |
将16位数据(value)写入16位寄存器(addr),写入前使用数据同步屏障同步。 |
|
READ16_MB(addr,value) |
读取16位寄存器(addr),读取前使用数据同步屏障同步。 |
|
读/写8位数据 |
WRITE8(addr,value) |
将8位数据(value)写入8位寄存器(addr)。 |
READ8(addr,value) |
读取8位寄存器(addr)。 |
|
WRITE8_MB(addr,value) |
将8位数据(value)写入8位寄存器(addr),写入前使用数据同步屏障同步。 |
|
READ8_MB(addr,value) |
读取8位寄存器(addr),读取前使用数据同步屏障同步。 |
须知: 如果32位ARM架构写64位寄存器通过str指令访问,会导致高32位寄存器地址和低32位寄存器地址的数据不能同时生效,因此通过strd指令实现。
注意事项¶
无。
扩展内核¶
动态加载¶
概述¶
基本概念
动态加载是一种程序加载技术。
静态链接是在链接阶段将程序各模块文件链接成一个完整的可执行文件,运行时作为整体一次性加载进内存。动态加载允许用户将程序各模块编译成独立的.o文件和.so文件,而不将它们链接成一个可执行文件,在需要使用某模块时再动态地将其加载进内存。
LiteOS支持动态加载OBJ目标文件(.o文件)和SO共享目标文件(.so文件),需要目标文件、系统二进制镜像文件配合使用。
图 1 动态加载示意图

动态加载相关概念
符号表在表现形式上是记录了符号名及其所在内存地址的数组。符号表在动态加载模块初始化时被载入到动态加载模块的符号管理结构中。在加载用户模块进行符号重定位时,动态加载模块通过查找符号管理结构得到相应符号所在地址。
运作机制
对于so文件,可能以ZIP(压缩)或者NOZIP(非压缩)的形式存放在存储介质中,相应的有两种加载策略。
NOZIP策略:普通的so文件可以通过多次lseek与read获取相应的文件信息。首先根据so文件的 SegmentHeader信息,计算出对应的可Load段的大小,然后分配出相应的空间,将相应的可Load段加载到内存并完成动态加载工作。因为每次仅仅读取文件的一小部分,不存在内存浪费问题。
ZIP策略:读取ZIP压缩文件时,必须一次性将文件全部读取到内存中(mem1),但是第一次读取时并不知道对应的so文件中可Load段的大小,所以需要二次分配内存(mem2)。因为mem2中已经具备了本次动态加载所需的所有信息,如果让mem1和mem2并存就会造成内存浪费并导致内存峰值过高。解决办法是分两次读取ZIP文件,第一次读取的目的仅仅是计算最终所需的内存大小(计算完毕,立刻释放相应内存),第二次读取的目的才是根据相应信息完成动态加载工作。可见,ZIP加载策略是以牺牲指令为代价来避免内存峰值过高的一种策略。
开发指导¶
使用场景
静态链接将程序各模块文件链接成一个整体,运行时一次性加载进内存,具有代码装载速度快等优点。但当程序规模较大,模块变更升级较为频繁时,会存在内存和磁盘空间浪费、模块更新困难等问题。
动态加载技术可以较好地解决上述静态链接中存在的问题,在程序需要执行外部模块中的代码时,动态地将外部模块加载进内存,不需要该模块时再卸载,可以实现公共代码的共享以及模块的平滑升级等功能。
功能
LiteOS 的动态加载模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
销毁动态加载 |
LOS_LdDestroy |
销毁动态加载模块。 |
动态加载模块文件 |
LOS_SoLoad |
动态加载一个so模块(依赖文件系统)。 |
LOS_MemLoad |
动态加载一个so或obj模块(不依赖文件系统,从指定内存空间加载)。 |
|
LOS_ObjLoad |
动态加载一个obj模块(依赖文件系统)。 |
|
动态加载模块文件到静态空间 |
LOS_MemLoadStatic |
动态加载一个so或obj模块到指定的静态空间(不依赖文件系统,从指定内存空间加载)。 |
LOS_SoLoadStatic |
动态加载一个so模块(依赖文件系统)到指定的静态空间。 |
|
获取运行视图下模块需要的空间大小 |
LOS_ModuleSizeGet |
计算加载并运行一个so或obj模块所需要的静态空间大小。 |
LOS_DynSizeGetFromFS |
计算加载并运行一个so模块所需要的静态空间大小(依赖文件系统)。 |
|
查找符号地址 |
LOS_FindSymByName |
在模块或系统符号表中查找符号地址。 |
卸载模块 |
LOS_ModuleUnload |
卸载一个模块。 |
设置动态加载的搜索路径/参数/内存池地址 |
LOS_PathAdd |
添加模块的搜索路径。 |
LOS_DynParamReg |
设置so模块的动态加载参数,使用该接口需要打开LOSCFG_DYNLOAD_DYN_FROM_FS宏开关,即只支持从文件系统中加载so模块时才能设置其动态加载参数。 |
|
LOS_DynMemPoolSet |
设置动态加载使用的内存池地址。 |
说明: LOS_DynMemPoolSet接口入参必须是经过LOS_MemInit初始化的内存池地址,即通过LiteOS内存管理算法管理,并且保证该内存池与系统内存池不重合。该接口需在加载.so或者.o文件前使用。
须知: 不同目标平台的内存保护/内存管理单元设计可能存在差异,在使用本章节提供的接口前,请务必阅读目标平台的相关文档以理解平台硬件能力和使用限制。
DYNLOAD错误码
对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_LD_INVALID_ARG |
0x02002600 |
传入的参数中含有NULL,0等非法值。 |
传入正确的参数 |
2 |
LOS_ERRNO_LD_INVALID_ELF |
0x02002601 |
传入的ELF不正确或者是不支持的类型。 |
传入正确的ELF文件 |
开发流程
动态加载主要有以下几个步骤:
准备编译环境
编译待加载的模块文件
编写动态加载的业务代码
编译系统镜像
准备系统环境
准备编译环境
添加.o和.so模块编译选项。
.o模块的编译选项中需要添加“-nostdlib -fno-PIC”选项。
.so模块的编译选项中需要添加“-nostdlib -fPIC -shared”选项。
说明:
以下列出的编译选项,可以根据实际需要自行选择。
-z max-page-size=value
设置.o和.so模块可加载的program segment的对齐参数为value值,添加该选项,可以有效减少各相邻可加载的segment的虚拟地址之间由于对齐需要而产生的空白区域。如果不添加该选项,默认对齐参数为0x10000。
须知:IPC的动态加载需要用户保证所提供的模块文件中所有LD_SHT_PROGBITS、LD_SHT_NOBITS类型的section起始地址都是4字节对齐,否则拒绝加载该模块。
生成.o和.so模块时禁止链接编译器中的标准库(即不允许使用-ldl、-lpthread、-lc等),且务必使用编译选项“-nostdlib”。
.o和.so模块编译选项添加示例如下:
RM = -rm -rf CC = arm-himix100-linux-gcc SRCS = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SRCS)) SOS = $(patsubst %.c,%.so,$(SRCS)) all: $(SOS) $(OBJS): %.o : %.c @$(CC) -nostdlib -c $< -fno-PIC -o $@ $(SOS): %.so : %.c @$(CC) -nostdlib $< -fPIC -shared -o $@ clean: @$(RM) $(SOS) $(OBJS) .PHONY: all clean
修改系统的Makefile。
编译系统镜像的Makefile必须include根目录下的“config.mk”文件,并使用“config.mk”中的“LITEOS_CFLAGS”或“LITEOS_CXXFLAGS”编译选项,示例如下:
LITEOSTOPDIR ?= ../.. SAMPLE_OUT = . include $(LITEOSTOPDIR)/config.mk RM = -rm -rf LITEOS_LIBDEPS := --start-group $(LITEOS_LIBDEP) --end-group SRCS = $(wildcard sample.c) OBJS = $(patsubst %.c,$(SAMPLE_OUT)/%.o,$(SRCS)) all: $(OBJS) clean: @$(RM) *.o sample *.bin *.map *.asm $(OBJS): $(SAMPLE_OUT)/%.o : %.c $(CC) $(LITEOS_CFLAGS) -c $< -o $@ $(LD) $(LITEOS_LDFLAGS) -uinit_jffspar_param --gc-sections -Map=$(SAMPLE_OUT)/sample.map -o $ (SAMPLE_OUT)/sample ./$@ $(LITEOS_LIBDEPS) $(LITEOS_TABLES_LDFLAGS) $(LITEOS_DYNLDFLAGS) $(OBJCOPY) -O binary $(SAMPLE_OUT)/sample $(SAMPLE_OUT)/sample.bin $(OBJDUMP) -d $(SAMPLE_OUT)/sample >$(SAMPLE_OUT)/sample.asm
编译待加载的模块文件
请严格按如下步骤进行编译。以makefile为例介绍。
编译.o和.so模块,并将运行所需的所有.o和.so文件拷贝到同一目录下。
说明:
以下列出的编译选项,可以根据实际需要自行选择。
-z max-page-size=value
设置.o和.so模块可加载的program segment的对齐参数为value值,添加该选项,可以有效减少各相邻可加载的segment的虚拟地址之间由于对齐需要而产生的空白区域。如果不添加该选项,默认对齐参数为0x10000。进入“RTOS_Lite/build/scripts/dynload_tools”目录执行“sym.sh”脚本,示例如下:
LITEOSTOPDIR ?= ../..
说明:sym.sh脚本的入参“/home/wmin/customer/out”,是.o和.so文件所在的目录的绝对路径。“out/dynload_sym”为指定“los_dynload_gsymbol.c”生成目录的绝对路径,建议指定路径为工程的编译生成目录。
注意如果所需加载的.o或.so被更新了,需要重新执行该脚本。该脚本会提取.o和.so文件中的所有系统符号(非用户定义的符号),以便在编译系统镜像时由编译器计算出对应符号的地址。
必须要在“RTOS_Lite/build/scripts/dynload_tools”目录下执行该脚本。
执行完“sym.sh”脚本后,确保“out/dynload_sym”目录下生成了“los_dynload_gsymbol.c”,并且保证该文件在编译阶段参与编译,以及生成的目标文件参与链接。否则,在加载.o和.so过程中会出现符号不能定位的问题。
(可选)进入“RTOS_Lite/build/scripts/dynload_tools”执行litesym生成小型化可加载的文件:
例如需要加载的文件为a.o, 当使能LOSCFG_DYNLOAD_LITE_SYMTAB时,需要将原始文件处理成自定义格式才能正确加载,可以用如下的命令生成。
$ python3 litesym /home/wmin/customer/out/a.o -o /home/wmin/customer/out/a_litesym.o
说明:使能LOSCFG_DYNLOAD_LITE_SYMTAB时,加载的文件必须是经过symtab脚本处理过的,否则加载会失败。
如果不需要使用LOSCFG_DYNLOAD_LITE_SYMTAB,不需要执行此脚本
编写动态加载的业务代码
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Dynamic Load Feature菜单,完成动态加载模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_DYNLOAD
动态加载模块的裁剪开关
YES/NO
YES
LOSCFG_KERNEL_EXTKERNEL
LOSCFG_KERNEL_DYNLOAD_DYN
使能so文件加载
YES/NO
YES
LOSCFG_KERNEL_DYNLOAD
LOSCFG_DYNLOAD_DYN_FROM_FS
使能so文件从文件系统加载
YES/NO
YES
LOSCFG_KERNEL_DYNLOAD_DYN
LOSCFG_DYNLOAD_DYN_FROM_MEM
使能so文件从指定内存中加载
YES/NO
YES
LOSCFG_KERNEL_DYNLOAD_DYN
LOSCFG_KERNEL_DYNLOAD_REL
使能obj文件加载
YES/NO
YES
LOSCFG_KERNEL_DYNLOAD
LOSCFG_DYNLOAD_DYN_FROM_MEM_STATIC
使能so文件从内存中加载到指定内存的功能
YES/NO
NO
LOSCFG_KERNEL_DYNLOAD_DYN
LOSCFG_DYNLOAD_DYN_FROM_FS_STATIC
使能so文件从文件系统中加载到指定内存的功能
YES/NO
NO
LOSCFG_KERNEL_DYNLOAD_DYN
LOSCFG_DYNLOAD_REL_FROM_FS
使能obj文件从文件系统加载
YES/NO
YES
LOSCFG_KERNEL_DYNLOAD_REL
LOSCFG_DYNLOAD_REL_FROM_MEM
使能obj文件从指定内存中加载
YES/NO
YES
LOSCFG_KERNEL_DYNLOAD_REL
LOSCFG_DYNLOAD_REL_FROM_MEM_STATIC
使能obj文件从指定内存中加载到指定静态空间中的功能
YES/NO
NO
LOSCFG_KERNEL_DYNLOAD_REL
LOSCFG_DYNLOAD_LITE_SYMTAB
使能自定义符号表,用于简化符号表建立过程,从而可以减少OS动态加载器大小
YES/NO
NO
LOSCFG_DYNLOAD_REL_FROM_MEM && !LOSCFG_DYNLOAD_REL_FROM_FS &&
!LOSCFG_DYNLOAD_REL_FROM_MEM_STATIC
LOSCFG_KERNEL_DYNLOAD_MODULE_NUM
配置用户能够同时加载的模块数量
1-65535
10
LOSCFG_KERNEL_DYNLOAD
LOSCFG_DYNLOAD_CPP_LIBRARY
支持cpp动态库的动态加载
YES/NO
NO
LOSCFG_KERNEL_EXTKERNEL
须知:
如果同时使能LOSCFG_KERNEL_NX(数据段不可执行,依赖Cortex-A芯片,其在menuconfig中对应的菜单项为:Kernel ---> Enable Data Sec NX Feature)和动态加载模块,则只支持动态加载.so文件,其他形式的加载可能会有异常,同时需要配置动态加载使用的堆大小(位于系统动态内存池的尾部)设置动态加载使用的内存池地址(可选)。
调用LOS_DynMemPoolSet接口可以设置动态加载使用的内存池。如果不设置,默认使用系统内存池。
设置so文件的动态加载策略。
在不同的应用场景下,so文件可能以ZIP或者NOZIP的形式存放于存储介质中。如果需要从文件系统中加载so文件,由于压缩文件与非压缩文件读写操作的差异性,在初始化动态加载模块之前需要指明具体的加载策略。
DYNLOAD_PARAM_S dynloadParam = {ZIP}; // 设置ZIP或NOZIP策略 LOS_DynParamReg(&dynloadParam); // 设置具体的加载策略
说明:
以ZIP格式存储的so文件必须采用ZIP加载策略,而普通的so文件使用上述两种策略都可以加载成功,建议使用NOZIP策略。如果没有设置,默认采用NOZIP加载策略。使用相对路径(可选)。
如果在动态加载模块时想使用相对路径,可以通过LOS_PathAdd接口添加.so和.o文件所在的绝对路径:
ret = LOS_PathAdd("/yaffs/bin/dynload"); if (ret != LOS_OK) { printf("add relative path failed"); return 1; }
添加路径后,调用LOS_SoLoad、LOS_ObjLoad、LOS_MemLoad接口时传入文件名即可,动态加载会在添加的路径下查找指定文件。
说明:只有在调用LOS_PathAdd接口添加路径后,才能在调用动态加载模块接口时使用相对路径。
可以多次调用LOS_PathAdd接口添加多个相对路径。
如果添加的多个路径下有相同文件名的模块,则在加载模块时按照添加的先后依次在所有路径中查找,且只加载第一个查找到的文件。
加载用户模块。
动态加载模块支持加载.o和.so模块。
使用LOS_ObjLoad接口动态加载obj文件(注意只能从文件系统中加载obj模块):
if ((handle = LOS_ObjLoad("/yaffs/bin/dynload/foo.o")) == NULL) { printf("load module ERROR!!!!!!\n"); return 1; }
使用LOS_SoLoad接口动态加载so文件:
if ((handle = LOS_SoLoad("/yaffs/bin/dynload/foo.so")) == NULL) { printf("load module ERROR!!!!!!\n"); return 1; }
说明:
对于so文件的动态加载:如果模块A需要模块B,即模块A依赖模块B,如果明确指明了A.so依赖B.so(编译A.so时将B.so作为编译参数),那么加载A模块时会自动将B模块也加载进来。如果没有明确指明A模块与B模块的依赖关系,那么在加载A模块之前,必须保证B模块已经被成功加载。
对于不支持文件系统的平台,支持直接从指定内存空间动态加载so,可以调用LOS_MemLoad接口实现。
获取用户模块中的符号地址。
在特定用户模块中查找符号、
需要在某个特定用户模块中查找符号地址时,调用LOS_FindSymByName接口,并将LOS_FindSymByName的第一个参数置为需要查找的用户模块的句柄。
if ((magic = LOS_FindSymByName(handle, "os_symbol_table")) == NULL) { printf("symbol not found\n"); return 1; }
在全局符号表中查找符号
需要在全局符号表(即OS模块,包括本模块和所有其他用户模块)中查找某个符号地址时,调用LOS_FindSymByName接口,并将LOS_FindSymByName的第一个参数置NULL。
if ((funTestCase0 = LOS_FindSymByName(NULL, "printf")) == NULL) { printf("symbol not found\n"); return 1; }
使用获取到的符号地址。
LOS_FindSymByName返回符号地址(VOID *指针),对该符号地址转换类型后,可以使用该符号。下面针对数据类型符号和函数类型符号举例说明。
整数类型符号
现有待加载的test.c,有一全局变量UINT32 g_test = 0,可以通过如下代码获取g_test的地址。
const char *g_pscOsOSSymtblFilePath = "/yaffs/bin/dynload/test.so"; UINT32 * g_testPtr = NULL; INT8 *ptr = (INT8 *)NULL; if ((pOSSymtblHandler = LOS_SoLoad(g_pscOsOSSymtblFilePath)) == NULL) { return LOS_NOK; } if ((ptr = LOS_FindSymByName(pOSSymtblHandler, "g_test")) == NULL) { printf("g_uwTest not found\n"); return LOS_NOK; } g_testPtr = (UINT32 *)ptr; /* 强制类型转换成真实的指针类型 */
函数类型符号
foo.c中定义了一个无参的函数test_0和一个有两个参数的函数test_2,编译生成foo.o。
foo.c: int test_0(void) { return 0; } int test_2(int i, int j) { return 0; }
以下代码演示在demo.c中获取foo.o模块中的函数并调用。
demo.c: typedef int (* TST_CASE_FUNC)(); /* 无形参函数指针类型声明 */ typedef int (* TST_CASE_FUNC1)(UINT32); /* 单形参函数指针类型声明 */ typedef int (* TST_CASE_FUNC2)(UINT32, UINT32); /* 双形参函数指针类型声明 */ TST_CASE_FUNC funTestCase0 = NULL; /* 函数指针定义 */ TST_CASE_FUNC2 funTestCase2 = NULL; int ret; handle = LOS_ObjLoad("/yaffs/bin/dynload/foo.o"); funTestCase0 = LOS_FindSymByName(handle, "test_0"); if (funTestCase0 == NULL) { printf("can not find the function name\n"); return 1; } ret = funTestCase0(); funTestCase2 = LOS_FindSymByName(NULL, "test_2"); if (funTestCase2 == NULL){ printf("can not find the function name\n"); return 1; } ret = funTestCase2(42, 57);
卸载模块。
调用LOS_ModuleUnload接口卸载某个模块,将需要卸载的模块句柄作为参数传入该接口。对于已被加载过的obj或so文件的句柄,卸载时统一使用LOS_ModuleUnload接口。
ret = LOS_ModuleUnload(handle); if (ret != LOS_OK) { printf("unload module failed"); return 1; }
销毁动态加载模块。
不再需要动态加载功能时,调用LOS_LdDestroy接口,销毁动态加载模块。
须知:销毁动态加载模块时会自动卸载所有已被加载的模块,销毁后模块不能再使用。
销毁动态加载模块前需确认模块不再使用。
编译系统镜像
在RTOS_Lite源码根目录下执行make,编译系统镜像。
编译完成后,在根目录out/平台名的目录下,可以看到生成的系统镜像“vs_server.bin”文件。
说明: 如果待加载的.o和.so中包含了未定义的外部符号(既没有定义在这些.o和.so文件中,也不是一个合法的系统全局符号),在编译系统镜像文件时会提示相应错误,需排查错误信息,确保系统镜像编译正确。
准备系统环境
.so文件(或.o文件)需要和系统镜像文件配合使用。
如果选择从文件系统加载模块,则模块文件必须放置在文件系统中,例如YAFFS、FAT等文件系统。
如果选择从指定内存空间加载,则忽略下文的2,只需要将模块文件烧写到指定内存空间即可。
建议操作顺序:
烧写系统镜像文件到开发板的flash中。
将.so文件(或.o文件)存储到开发板的flash中,这里分为两种情况:
如果模块文件保存在可热拔插的SD卡设备上,直接将SD卡插到开发板上即可。如果需要更新.so文件(或.o文件),可将SD卡插到电脑上更新。
如果模块文件保存在不可热插拔的存储设备上,可通过如下两种方式更新文件:
将模块文件编译进文件系统镜像,然后烧写文件系统镜像到开发板的flash中。
启动LiteOS系统后,通过tftp命令下载.so文件(或.o文件),示例命令如下:
tftp -g -l /yaffs/bin/dynload/foo.so -r foo.so 10.67.211.235
启动系统进行验证
注意事项¶
编译选项:
.o模块的编译选项中需要添加“-nostdlib -fno-PIC”选项。
.so模块的编译选项中需要添加“-nostdlib -fPIC -shared”选项。
安全性:
对于加载的模块文件,需要提供者保证它的来源可靠并且安全。强烈建议禁止应用系统从SD卡/U盘等外部高风险介质加载文件,如果确实需要,则由提供者/应用层保证文件的可靠性,如果由此产生损失或者问题,华为不承担任何责任。
如果被加载的文件有问题,可能导致包括但不限于设备损坏、数据泄露/被篡改等一系列的问题,对此华为不承担任何责任。
其他限制:
在编译系统镜像之前必须确保所需加载的.o与.so已经就绪,这样才能保证后续在编译系统镜像文件时,这些.o和.so所调用到的外部符号信息已经集成到最终的系统镜像中。
在加载.o和.so模块时,一旦发现该模块引用的外部符号被重复定义在其它没有依赖关系的模块中时,会拒绝对本次引用做重定位,导致加载该模块失败。为了避免使用异常,用户应该事先确保所有待加载的模块内部没有重复定义的符号(变量或函数接口)。
编程实例¶
实例描述
示例中,sample_foo.c中定义了一个无参的函数test_0和一个有两个参数的函数test_2,编译生成foo.o。在sample_Dynamic_loading.c中演示获取foo.o模块中的函数并调用。 sample_Dynamic_loading.c代码实现如下:
编程示例
typedefint (* TST_CASE_FUNC)(VOID); /* 无形参函数指针类型声明 */
typedefint (* TST_CASE_FUNC2)(UINT32, UINT32); /* 双形参函数指针类型声明 */
TST_CASE_FUNC funTestCase0 = NULL; /* 函数指针定义 */
TST_CASE_FUNC2 funTestCase2 = NULL;
int ret;
void *handle = NULL;
handle = LOS_ObjLoad("/jffs0/bin/dynload/dynload_test.o");
funTestCase0 = LOS_FindSymByName(handle, "test_0");
if (funTestCase0 == NULL) {
printf("can not find the function name\n");
return;
}
ret = funTestCase0(); /* 调用该函数指针 */
funTestCase2 = LOS_FindSymByName(NULL, "test_2");
if (funTestCase2 == NULL){
printf("can not find the function name\n");
return;
}
ret = funTestCase2(42, 57); /* 调用该函数指针 */
ret = LOS_ModuleUnload(handle);
if (ret != LOS_OK) {
printf("unload module failed");
return;
}
LOS_LdDestroy();
编译运行得到的结果为:
Huawei LiteOS#
test_0
test_2: 42 57
低功耗框架¶
概述¶
低功耗框架是低功耗模块的统一管理框架,提供对Tickless、系统休眠、休眠唤醒等多种低功耗能力的支持。
低功耗框架默认使能Tickless。此外,由于低功耗特性与具体业务和硬件有较强的相关性,为了更好地适配硬件、扩展功能,低功耗框架开放了相关功能接口方便开发者适配。同时,在默认的低功耗框架中也可以自定义具体策略。
接口名 |
描述 |
|---|---|
LOS_PowerMgrInit |
初始化低功耗框架,入参为框架参数指针,可以自定义低功耗策略及具体休眠动作。注意入参不能为空,否则将注册失败。 |
LOS_LowpowerHookReg |
注册低功耗框架休眠入口函数,系统idle时调用该休眠函数(可不注册,LOS_PowerMgrInit初始化时会注册默认入口,使用系统自带的低功耗框架)。 |
低功耗框架错误码
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_LPM_INIT_NULL |
0x02002100 |
传递给低功耗框架初始化接口的参数为空。 |
确保传入的参数不为空。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为低功耗模块,值为0x21。
Tickless¶
概述¶
基本概念
Tickless机制是一种新的定时机制,它使用动态时钟中断替代周期性时钟定时。
任务调度通常通过时钟中断触发。在周期性定时机制下,每一次时钟中断都会检测当前调度条件是否满足,但能触发任务调度的时钟中断往往只占很小一部分。软件定时器任务必须通过时钟中断触发才能实现,每一次时钟中断也会检测软件定时器是否到期,若到期则调度该定时器的回调函数,而大部分时钟中断发生时并没有软件定时器到期。Tickless机制是通过预期省略不必要的时钟中断,确定下一次“有意义”的时钟中断的产生时刻,而不让“无意义”的时钟中断产生。通过省去CPU空闲时期无意义的时钟中断,可以有效降低系统的功耗。
运作机制
LiteOS的Tickless机制,采用的是“idle模式”,即在CPU空闲时期(idle任务中)开启Tickless机制。通过计算下一次任务调度的时间和下一次软件定时器到期的时间,将两者中的较小值设定为下一次时钟中断到来的时间,从而减少无意义的时钟中断。
开发指导¶
使用场景
系统CPU长时间处于空闲状态,且系统对功耗要求较高。
开发流程
Tickless的典型开发流程:
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Low Power Management Framework ---> Low Power Management Configure 菜单,使能Tickless。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_TICKLESS
Tickless的裁剪开关
YES/NO
YES
LOSCFG_KERNEL_LOWPOWER
当系统长时间不运行业务时,进入idle任务后根据可休眠时间自动进入tickless模式。
平台差异性
无。
注意事项¶
由于Tickless机制与系统时间都通过Tick中断定时器计时,因此在时间精度要求极高的场景下不建议使用。
各平台由于Systick寄存器位数差异,为避免寄存器溢出,在配置tickless休眠时间时,需要根据寄存器可接受最大数值、系统时钟频率、LOSCFG_BASE_CORE_TICK_PER_SECOND计算tickless可正常运行的最大tick数。例如cortex-m4平台由于SysTick->LOAD寄存器为24位,系统时钟频率为120M,配置LOSCFG_BASE_CORE_TICK_PER_SECOND为1000,则配置tickless休眠时间应不超过对应阈值:(2^24 - 1) / (120000000 / 1000) = 139 ticks。
系统休眠¶
概述¶
基本概念
系统休眠是一种兼顾系统性能和能耗的低功耗机制,用户可通过自行配置休眠策略,在待机场景下调整系统时钟频率、调整外设工作模式或下电等,从而节省功耗,提升续航能力。
系统休眠的低功耗管控主要支持:
睡眠模式。
睡眠投票机制。
系统调频。
低功耗流程注册结构体
目前,系统休眠支持浅睡、深睡两种模式。系统休眠默认包含浅睡逻辑,若用户有自定义休眠逻辑或深睡的需求,可以注册低功耗流程结构体struct PowerMgrParameter。
typedef struct {
PowerMgrRunOps runOps; /* power manager run operations */
} PowerMgrParameter;
typedef struct {
#ifdef LOSCFG_KERNEL_DYN_FREQ
UINT32 (*changeFreq)(LosFreqMode freq); /**< Tune system frequency */
#endif
#if defined (LOSCFG_KERNEL_LIGHTSLEEP) || defined (LOSCFG_KERNEL_DEEPSLEEP)
LosSleepMode (*getSleepMode)(VOID); /**< GetSleepMode, provided for special needs before entering sleep */
#endif
#ifdef LOSCFG_KERNEL_LIGHTSLEEP
VOID (*enterLightSleep)(VOID); /**< Enter light sleep */
#endif
#ifdef LOSCFG_KERNEL_DEEPSLEEP
#ifdef LOSCFG_LOWPOWER_SLEEP_USERCONFIG
VOID (*userPreConfig)(VOID); /**< UserPreconfig, provided for special needs before entering sleep */
VOID (*userPostConfig)(VOID); /**< UserPostconfig, provided for special needs after wakeup */
#endif
VOID (*systemWakeup)(VOID); /**< System wakeup */
VOID (*suspendConfig)(VOID); /**< Supend device before entering deep sleep */
VOID (*resumeConfig)(VOID); /**< Resume device after wakeup from deep sleep */
#ifdef LOSCFG_KERNEL_SMP
VOID (*otherCoreResume)(VOID); /**< Other core Resume for multi-core scenes */
#endif
VOID (*enterDeepSleep)(VOID); /**< Enter deep sleep */
VOID (*setWakeUpTimer)(UINT32 timeout); /**< Set wakeup timer */
UINT32 (*withdrawWakeUpTimer)(VOID); /**< Withdraw wakeup timer */
#ifdef LOSCFG_KERNEL_RAM_SAVE
VOID (*contextSave)(VOID); /**< Context save */
VOID (*contextRestore)(VOID); /**< Context restore */
#endif
#endif
#ifdef LOSCFG_LOWPOWER_SLEEP_WFI
VOID (*enterWFI)(VOID); /**< Enter WFI mode */
#endif
} PowerMgrRunOps;
开发指导¶
用户可根据具体业务场景制定休眠策略,定义禁止/允许进入的睡眠模式、选择不同的睡眠模式。由于深睡等模式可能涉及到具体硬件和时钟,所以用户需自行实现相应函数以适配硬件。同时系统休眠模块在流程初始和结束阶段均有预留接口,对于有特殊需求的场景,可以进行睡眠流程的扩展。
系统休眠模块为用户提供如下功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
系统休眠开关 |
LOS_EnableLowPower |
开启系统休眠,仅在debug模式生效。 |
LOS_DisableLowPower |
关闭系统休眠,仅在debug模式生效。 |
|
LOS_GetLowPowerStatus |
获取系统休眠开关状态,仅在debug模式生效。 |
|
系统休眠时间 |
LOS_SleepTicksGet |
获取系统休眠时间。 |
系统投票 |
LOS_PowerMgrSleepLock |
禁止进入指定的睡眠模式。 |
LOS_PowerMgrSleepUnLock |
允许进入指定的睡眠模式。 |
|
系统调频 |
LOS_PowerMgrChangeFreq |
系统调频。 |
默认的系统休眠流程如图1所示。
图 1 系统休眠流程图

在进入系统休眠时,首先进行睡眠前期的配置,根据系统可睡眠时间及用户定义的休眠策略,获取睡眠模式。
若为深睡模式,则会执行深睡前期的配置,例如屏蔽中断、保存设备状态等。随后将挂起设备并设置唤醒源,系统进入深睡模式。
若为浅睡或其他模式,则执行对应流程。通常情况下,Tickless将配合系统休眠浅睡共同使用,以屏蔽Tick中断。用户也可使用自定义的Tick屏蔽和恢复机制。默认浅睡仅进入Tickless+WFI模式。
开发流程
系统休眠的典型开发流程:
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Low Power Management Framework ---> Low Power Management Configure菜单。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_LIGHTSLEEP
系统休眠的浅睡模式裁剪开关
YES/NO
NO
LOSCFG_KERNEL_LOWPOWER
LOSCFG_KERNEL_DEEPSLEEP
系统休眠的深睡模式裁剪开关
YES/NO
NO
LOSCFG_KERNEL_LOWPOWER,且需要硬件平台支持
LOSCFG_KERNEL_WAKEUPTIMER_CALIBRATION
系统休眠深睡模式外部定时器校准tick开关
YES/NO
YES
LOSCFG_KERNEL_DEEPSLEEP && LOSCFG_KERNEL_LOWPOWER,且需要硬件平台支持
LOSCFG_KERNEL_RAM_SAVE
系统休眠时深睡模式下的内存保存开关
YES/NO
NO
LOSCFG_KERNEL_DEEPSLEEP,且需要硬件平台支持
LOSCFG_LOWPOWER_SLEEP_USERCONFIG
系统休眠时前/后处理开关
YES/NO
NO
LOSCFG_KERNEL_DEEPSLEEP
LOSCFG_KERNEL_DYN_FREQ
系统休眠的调频裁剪开关
YES/NO
NO
LOSCFG_KERNEL_LOWPOWER,且需要硬件平台支持
LOSCFG_LOWPOWER_SLEEP_VETO
系统休眠的投票裁剪开关
YES/NO
NO
LOSCFG_KERNEL_LOWPOWER
LOSCFG_LOWPOWER_SLEEP_BY_TICK
系统休眠模式依照就绪时间自动识别开关
YES/NO
YES
LOSCFG_KERNEL_LOWPOWER
LOSCFG_LOWPOWER_SLEEP_WFI
系统休眠WFI模式自定义配置开关
YES/NO
NO
LOSCFG_KERNEL_LOWPOWER
LOSCFG_MAX_SLEEP_TIME
系统休眠最大睡眠阈值配置
INT
10000
LOSCFG_KERNEL_LOWPOWER
LOSCFG_MIN_LIGHT_SLEEP_TIME
系统休眠最小浅睡阈值配置
INT
50
LOSCFG_KERNEL_LIGHTSLEEP
LOSCFG_MIN_DEEP_SLEEP_TIME
系统休眠最小深睡阈值配置
INT
2000
LOSCFG_KERNEL_DEEPSLEEP
LOSCFG_DELTA_TICKS
系统休眠深睡tick补偿配置
INT
1
LOSCFG_KERNEL_DEEPSLEEP
LOSCFG_EXT_TIMER_FREQ
系统休眠深睡外部时钟频率配置
INT
1
LOSCFG_KERNEL_DEEPSLEEP
在Menuconfig中配置系统休眠唤醒策略,包括最小浅睡/深睡阈值、最大睡眠阈值;如果开启深睡模式,根据需要选择是否开启自定义WFI、休眠前后处理、深睡tick补偿以及外部时钟频率等。
适配对应睡眠模式的休眠接口,详见“低功耗流程注册结构体”。
当系统业务繁忙时,调用LOS_PowerMgrSleepLock接口禁止进入指定的睡眠模式(深睡、浅睡);当系统业务空闲时,调用LOS_PowerMgrSleepUnLock接口允许进入指定的睡眠模式。注意这两接口应该是配套使用的。
系统待机时,根据业务投票及系统实际可休眠时长,自动进入相应休眠模式进行休眠。
注意事项¶
开启系统深睡模式时,建议在”低功耗流程注册结构体”的子结构体成员struct PowerMgrRunOps的enterDeepSleep钩子函数中将cache写回内存。
低功耗提供的调频机制,如果在系统启动之后和业务首次调频之前进入空闲状态,系统会默认把频率调到最高。
编程实例¶
实例描述
本实例在cortex-M架构的CPU下实现如下功能:
注册低功耗流程框架,适配深睡、RTC使能等功能接口;
创建用户任务,并在任务中延时5000Ticks,使系统进入深睡模式。
编程示例
前提条件:在menuconfig菜单中完成系统休眠深睡的配置,同时配置用户休眠阈值:LOSCFG_MIN_SLEEP_TIME、LOSCFG_MAX_SLEEP_TIME以及LOSCFG_MIN_DEEP_SLEEP_TIME。
部分代码实现如下:
注册低功耗框架
#ifdef LOSCFG_KERNEL_LOWPOWER #include "los_lowpower.h" #endif const PowerMgrParameter gPowerMgrParameter = { // 用户自定义系统休眠配置,成员函数用户自行按需实现 .runOps = { #ifdefLOSCFG_KERNEL_LIGHTSLEEP .enterLightSleep = EnterLightSleep, // 浅睡动作,此处设置为进入WFI模式 .getSleepMode = GetSleepMode, // 获取用户休眠模式 #endif #ifdefLOSCFG_KERNEL_DEEPSLEEP .suspendConfig = SuspendDevice, // 关闭外设动作 .resumeConfig = ResumeDevice, // 恢复外设 .enterDeepSleep = EnterDeepSleep, // 深睡动作,此处设置为STM32的standby模式 .setWakeUpTimer = SetWakeUpTimer, // 设置唤醒源,此处设置为根据入参(休眠时间)使能外部RTC .withdrawWakeUpTimer = WithdrawWakeUpTimer, // 取消唤醒源,此处设置为停止RTC计时 #endif } }; INT32 main(VOID) { UINT32 ret; #ifdef LOSCFG_KERNEL_LOWPOWER // 注册低功耗流程框架,若仅需默认的Tickless及浅睡模式,可调用LOS_PowerMgrInit(NULL)注册系统默认框架。 LOS_PowerMgrInit(gPowerMgrParameter); #endif HardwareInit(); printf("[main]: Hello, welcome to liteos !\n"); PRINT_RELEASE("********Hello LiteOS ********\n" "\nversion : %s\nopen-version : %s %u.%u.%u\n" "build data : %s %s\n\n" "**********************************\n", HW_LITEOS_VER, HW_LITEOS_SYSNAME, MAJ_V, MIN_V, REL_V, __DATE__, __TIME__); OsSetMainTask(); OsCurrTaskSet(OsGetMainTask()); ret = OsMain(); if (ret != LOS_OK) { return LOS_NOK; } RTC_Start(); g_kernelInit = 1; OsStart(); return LOS_OK; }
创建任务,并在任务入口函数中执行延时操作:
STATIC VOID task_demo1(VOID) { while (1) { osDelay(5000); // 任务延时5000Ticks } }
结果验证
由于设置的深睡阈值为2000,任务延时5000Ticks,所以系统会进入深睡模式:

如上图所示,系统每隔5s将会从standby模式唤醒。
休眠唤醒¶
概述¶
基本概念
休眠唤醒(RunStop)是一套保存系统现场镜像以及从现场镜像中快速恢复运行的机制。
用户在系统运行的某一时刻调用休眠唤醒接口,可以将系统此时的状态快照(CPU现场及内存)保存到Flash上。系统重启后,可以通过快照恢复系统,使系统从快照保存的状态处继续运行。
运作机制
业务模块初始化并保持稳定运行后,调用休眠唤醒接口保存系统快照,这份快照包含了业务模块所需的所有CPU现场及内存状态。系统运行一段时间后,业务模块空闲时,主核主动下电进入低功耗模式。当有业务需要处理时,MCU发送特定数据包唤醒主核上电,硬件状态恢复后,系统从Flash快照处恢复运行,这样系统重新上电后无需重新初始化业务模块,从而实现业务模块的快速唤醒。
开发指导¶
使用场景
如果希望系统运行一段时间后进入低功耗模式,当有业务到来时,系统能够快速恢复到低功耗模式前的状态,从而快速恢复业务处理能力。对于这种应用场景,可以通过休眠唤醒实现。
在某项目中,休眠唤醒被用于实现芯片的休眠唤醒:系统业务运行稳定后,保存当前系统状态的快照。当系统进入空闲状态时,主核主动下电进入低功耗模式,当业务消息到来时MMU对主核上电,系统从快照处快速启动,恢复执行。以WIFI芯片为例,RunStop可以实现主核在低功耗模式下WIFI状态不断连,以及WIFI状态的快速唤醒。
功能
休眠唤醒模块为用户提供如下功能,接口详细信息可以查看API参考。
接口名 |
描述 |
|---|---|
LOS_MakeImage |
将系统状态快照保存到指定介质中 |
开发流程
图 1 休眠唤醒流程图

使能低功耗框架和休眠唤醒。
打开菜单,相关配置项的菜单路径为:Kernel ---> Enable Extend Kernel ---> Enable Low Power Management Framework ---> Low Power Management Configure。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_LOWPOWER
低功耗框架裁剪开关
YES/NO
YES
LOSCFG_KERNEL_EXTKERNEL
LOSCFG_KERNEL_RUNSTOP
休眠唤醒开关
YES/NO
NO
休眠唤醒依赖Flash、Uboot和bestfit内存管理算法。Nand Flash的宏开关为LOSCFG_DRIVERS_MTD_NAND,spi_nor flash的宏开关为LOSCFG_DRIVERS_MTD_SPI_NOR
初始化低功耗框架。
入口函数main中,在OsStart前调用 LOS_PowerMgrInit 初始化低功耗框架为系统默认框架,示例代码如下:
#ifdef LOSCFG_KERNEL_LOWPOWER #include "los_lowpower.h" const PowerMgrRunOps runOps = { .getSleepMode = NULL, }; UINT32 OsPreSystemConfig(VOID) { PowerMgrParameter param; param.runOps = runOps; return LOS_PowerMgrInit(¶m); } #endif LITE_OS_SEC_TEXT_INIT int main(void) { ...... #ifdef LOSCFG_KERNEL_LOWPOWER ret = OsPreSystemConfig(); if (ret != LOS_OK) { return LOS_NOK; } #endif OsStart(); ...... }
编写快照业务代码。
业务代码入口函数为app_init,该函数位于源码根目录的“self_src/targets/board/$(LITEOS_PLATFORM)/os_adapt/os_adapt.c“文件中。
在快照业务代码后调用LOS_MakeImage将系统当前的状态快照保存到指定介质中,并通过MAKE_WOW_IMAGE宏控制非快照业务代码的编译,以实现快照镜像和全部镜像的编译,示例代码如下。
void wakeup_callback(void) { #ifdef LOSCFG_KERNEL_CPUP LOS_CpupReset(); #endif (VOID)LOS_HwiEnable(83); } #define NOR_ERASE_ALIGN_SIZE (64 * 1024) #define NOR_READ_ALIGN_SIZE 1 #define NOR_WRITE_ALIGN_SIZE 1 int flash_read(void *memaddr, size_t start, size_t size) { return spiflash_read(memaddr, start, size); } int flash_write(void *memaddr, size_t start, size_t size) { spiflash_erase(start, size); return spiflash_write(memaddr, start, size); } void app_init(void) { #ifdef LOSCFG_KERNEL_RUNSTOP RUNSTOP_PARAM_S stRunstopParam; // 定义一个LOS_MakeImage的参数变量 #endif #ifdef LOSCFG_DRIVERS_MTD_SPI_NOR (void)spinor_init(); mtd = get_mtd("spinor"); #endif #if defined(LOSCFG_KERNEL_RUNSTOP) && defined(LOSCFG_DRIVERS_MTD_SPI_NOR) memset(&stRunstopParam, 0, sizeof(RUNSTOP_PARAM_S)); /* LOS_MakeImage参数配置 */ stRunstopParam.pfFlashReadFunc = flash_read; // 指定介质读函数 stRunstopParam.pfFlashWriteFunc = flash_write; // 指定介质写函数 stRunstopParam.pfIdleWakeupCallback = idle_wakeup_callback; // 指定从快照恢复后在idle任务中需要执行的回调函数 stRunstopParam.pfImageDoneCallback = NULL; // 指定快照镜像生成后的回调函数,此处为NULL stRunstopParam.pfWakeupCallback = wakeup_callback; // 指定从快照恢复后需要执行的回调函数 stRunstopParam.uwFlashEraseAlignSize = NOR_ERASE_ALIGN_SIZE; // 介质的擦除对齐参数 stRunstopParam.uwFlashWriteAlignSize = NOR_WRITE_ALIGN_SIZE; // 介质的写对齐参数 stRunstopParam.uwFlashReadAlignSize = NOR_READ_ALIGN_SIZE; // 介质的读对齐参数 stRunstopParam.uwImageFlashAddr = 0x100000; // 烧写系统全镜像的介质起始地址 stRunstopParam.uwWowFlashAddr = 0x800000; // 保存快照的介质起始地址 LOS_MakeImage(&stRunstopParam); #endif #ifndef MAKE_WOW_IMAGE #ifdef LOSCFG_KERNEL_RUNSTOP PRINTK("Image length 0x%x\n", OsWowImageSizeGet()); #endif #endif /* MAKE_WOW_IMAGE */ }
配置WOW_SRC变量。
在源码根目录“build/make/wow_scatter.mk”文件中配置WOW_SRC变量,将变量定义为调用休眠唤醒函数的业务源文件路径,如下所示,其中LITEOSTOPDIR指代“RTOS_Lite/self_src”源码目录。
WOW_SRC := $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM)/os_adapt/os_adapt.c编译系统镜像。
在根目录下执行make命令,编译全部业务代码。
RTOS_Lite$ make编译完成后,查看镜像段的排布,以验证快照镜像是否生成成功。进入系统镜像生成目录(例如Hi3556V200平台的镜像生成目录为“out/hi3556v200”,其他类推),可以看到生成的系统ELF文件vs_server,执行“readelf -S vs_server”命令查看section头信息,结果如图2所示。
图 2 系统ELF文件vs_server readelf结果

图2中显示了与休眠唤醒有关的段信息(包括段的名称、起始地址及偏移大小)。其中.wow_rodata为休眠唤醒镜像的只读数据段,.wow_text为代码段,.wow_data为数据段,.wow_bss为bss段。
查看休眠唤醒链接脚本“RTOS_Lite/self_src/tools/scripts/ld/wow.ld”的.text段,可以看到新增了wow.O(*.text*),它实现了将休眠唤醒的快照部分代码相关符号整合到同一个段中,如图3所示。
图 3 链接脚本.text段

将全部镜像烧写到Flash。
进入串口工具界面,可以参考如下命令,将全部镜像烧写到Flash的0x100000地址位。
tftp 0x81000000 vs_server.bin;sf probe 0;sf erase 0x100000 0x300000;sf write 0x81000000 0x100000 0x300000;其中,“vs_server.bin”为系统镜像文件名,先将其烧写到内存中一段高地址位0x81000000。然后烧写到Flash,起始地址为0x100000,烧写长度为0x300000,即烧写的镜像文件大小不能超过3M,请根据实际的镜像大小调整长度值。
说明:
用户可以根据实际环境自主选择镜像烧写工具及烧写方法。加载全部镜像。
可以参考如下命令,从Flash的0x100000地址处读取全部镜像,加载到内存地址0x80200000处。
sf probe 0;sf read 0x80200000 0x100000 0x300000;执行如下命令,从内存地址0x80200000处启动系统。
go 0x80200000;根据系统反馈的快照镜像大小,从快照处恢复运行。
图 4 系统启动时的打印信息

根据系统启动时打印的信息,可以获取休眠唤醒快照镜像大小,如图4红框中所示,本示例中快照大小为0x170000。快照被写到介质的0x800000地址处(从3中可以获得保存快照的介质的起始地址为0x800000)。
重启系统,可以参考如下命令在uboot中执行,从保存快照的介质的0x800000地址处读取0x170000大小的快照镜像,加载到内存地址0x80200000处,然后执行“go 0x80200000”从快照启动系统。
sf probe 0;sf read 0x80200000 0x800000 0x170000; go 0x80200000;
启动效果如图5所示。
图 5 uboot执行命令后的打印信息

系统成功从快照启动。
注意事项¶
用户在编写休眠唤醒业务时,需要保证“#ifndef MAKE_WOW_IMAGE”前涵盖了快照的所有代码和数据,否则从快照恢复后的系统中不会包含所有休眠唤醒的业务。
休眠唤醒依赖于存储介质,当前LiteOS依赖Flash,因此该功能依赖Flash,如果关闭Flash支持,休眠唤醒功能也会关闭。如果需要在其他存储介质上使能休眠唤醒,请参考当前Flash的配置进行适配。
CPU占用率¶
概述¶
基本概念
LiteOS的CPUP(Central Processing Unit Percent,CPU占用率)分为系统CPU占用率、任务CPU占用率和中断CPU占用率。
系统CPU占用率是指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。
任务CPU占用率指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。
中断CPU占用率指周期时间内的中断占用率,用于表示系统一段时间内的中断处理时间。
运作机制
LiteOS的CPUP采用任务级记录的方式,在任务切换时,记录任务的切入时间,在下一次任务切换时,记录任务的切出时间,以此计算出任务本次的运行时间,累加到任务的总占用时间上。
CPU占用率的计算方法:
系统CPU占用率=系统中除idle任务外其他任务运行总时间/系统运行总时间。
任务CPU占用率=任务运行总时间/系统运行总时间。
中断CPU占用率=中断运行总时间/系统运行总时间
开发指导¶
使用场景
通过系统CPU占用率,判断当前系统负载是否超出设计规格。
通过系统中各个任务的CPU占用率,判断各个任务的CPU占用率是否符合设计的预期。
功能
LiteOS的CPU占用率模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
获取系统CPU占用率 |
LOS_HistorySysCpuUsage |
获取系统CPU占用率,不包含idle任务。 |
获取任务CPU占用率 |
LOS_HistoryTaskCpuUsage |
获取指定任务的CPU占用率。 |
LOS_AllCpuUsage |
使能LOSCFG_CPUP_INCLUDE_IRQ且设置入参flag为0时,获取所有中断的CPU占用率。设置入参flag为非0,或者关闭LOSCFG_CPUP_INCLUDE_IRQ后,获取所有任务的CPU占用率,这里的任务也包含了idle任务。 |
|
重置CPU占用率 |
LOS_CpupReset |
重置CPU占用率数据,包含系统和任务的CPU占用率。 |
暂停CPU占用率统计 |
LOS_CpupStop |
暂停CPU占用率统计,包含中断和任务的CPU占用率不再更新。 |
开启CPU占用率统计 |
LOS_CpupStart |
开启CPU占用率统计,包含中断和任务的CPU占用率将先清0,然后以此为起点,重新统计。 |
说明:
通过上述接口获取到的CPU占用率是千分比,所以CPU占用率的有效表示范围为0~1000。系统CPU占用率为1000,表示系统满负荷运转。任务CPU占用率为1000,表示在一段时间内系统一直在运行该任务。
获取CPU占用率有三种模式,通过入参mode设置:
CPUP_LAST_MULIT_RECORD(值为0,原为CPUP_LAST_TEN_SECONDS):表示获取最近多次采样记录内的CPU占用率,默认为10s。
CPUP_LAST_ONE_RECORD(值为1,原为CPUP_LAST_ONE_SECONDS): 表示获取最近1次采样记录的CPU占用率,默认为1s。
CPUP_ALL_TIME(值为0xffff),或除0和1之外的其他值:表示获取自系统启动以来的CPU占用率。
CPUP错误码
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_CPUP_NO_MEMORY |
0x02001E00 |
初始化CPU占用率模块时,内存不足。 |
调整OS_SYS_MEM_SIZE,以确保有足够的内存供CPU占用率模块使用。 |
2 |
LOS_ERRNO_CPUP_TASK_PTR_NULL |
0x02001E01 |
入参存在空指针。 |
传入正确的指针。 |
3 |
LOS_ERRNO_CPUP_NO_INIT |
0x02001E02 |
CPU占用率模块没有初始化。 |
初始化CPU占用率模块后,再使用该模块。 |
4 |
LOS_ERRNO_CPUP_MAXNUM_INVALID |
0x02001E03 |
传入LOS_AllCpuUsage接口的入参(最大线程数或中断数)是一个无效值。 |
传入正确的最大线程数或中断数。 |
5 |
LOS_ERRNO_CPUP_THREAD_NO_CREATED |
0x02001E04 |
获取指定任务的CPU占用率时,发现该任务未创建。 |
系统只能统计已经被创建的任务的CPU占用率,检查传入的任务ID对应的任务是否已经创建。 |
6 |
LOS_ERRNO_CPUP_TSK_ID_INVALID |
0x02001E05 |
调用获取指定任务的CPU占用率的接口时,传入了无效的任务ID。 |
检查传入的任务ID的正确性。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为CPUP模块,值为0x1E。
开发流程
CPU占用率的典型开发流程:
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Cpup菜单,完成CPU占用率模块的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_CPUP
CPUP模块的裁剪开关。
YES/NO
YES
LOSCFG_KERNEL_EXTKERNEL
LOSCFG_CPUP_START_STOP
CPUP动态启停开关。
YES/NO
NO
LOSCFG_KERNEL_CPUP && !LOSCFG_SCHED_LOAD_BALANCE_CPUP
LOSCFG_CPUP_INCLUDE_IRQ
使能该配置项后,可以在LOS_HistoryTaskCpuUsage和LOS_AllCpuUsage接口中获取中断的CPU占用率。关闭该配置项后,只能获取任务的CPU占用率。
YES/NO
YES
LOSCFG_KERNEL_CPUP && LOSCFG_ARCH_INTERRUPT_TAKEOVER
LOSCFG_CPUP_CB_NUM_CONFIGURABLE
使能该配置项,CPUP的中断相关控制块的资源数量可单独配置,不再依赖LOSCFG_PLATFORM_HWI_LIMIT,可优化CPUP的size。
YES/NO
NO
LOSCFG_CPUP_INCLUDE_IRQ
LOSCFG_CPUP_IRQ_CB_NUM
CPUP的中断相关控制块的资源数量,根据实际的中断个数来配置,最大不超过LOSCFG_PLATFORM_HWI_LIMIT。
[1,LOSCFG_PLATFORM_HWI_LIMIT]
LOSCFG_PLATFORM_HWI_LIMIT
LOSCFG_CPUP_CB_NUM_CONFIGURABLE
LOSCFG_CPUP_SAMPLE_PERIOD
CPUP的采样周期,时间单位是tick,表示CPUP软件定时器的周期运行时间间隔。
[10,1000],实际还会限制时间[100ms,10s]
LOSCFG_BASE_CORE_TICK_PER_SECOND
LOSCFG_KERNEL_CPUP && !LOSCFG_SCHED_LOAD_BALANCE_CPUP
LOSCFG_CPUP_HISTORY_RECORD_NUM
CPUP的最大采样记录数目,实际为环形缓冲buffer,表示用户最多可查看最近几次采样记录的CPU占用率信息。
[1,10]
10
LOSCFG_KERNEL_CPUP && !LOSCFG_SCHED_LOAD_BALANCE_CPUP
获取系统CPU使用率LOS_HistorySysCpuUsage。
获取指定任务或中断的CPU使用率LOS_HistoryTaskCpuUsage。
获取所有任务或所有中断的CPU使用率LOS_AllCpuUsage。
平台差异性
无。
注意事项¶
CPU占用率对性能有一定影响,而一般只有在产品开发时需要了解各个任务的占用率,因此建议在发布产品时,关闭CPU占用率。
关闭配置项LOSCFG_CPUP_INCLUDE_IRQ后,系统中的中断耗时会被统计到中断发生的任务中,即被中断打断的任务中。
编程实例¶
实例描述
本实例实现如下功能:
创建一个测试CPUP的任务。
获取系统最近1s内所有任务或中断的CPUP。
获取系统(除idle任务外)最近10s内的总CPU占用率。
获取CPUP测试任务的CPUP。
编程示例
前提条件:打开菜单配置好CPU占用率模块。
代码实现如下:
#include <unistd.h >
#include "los_task.h"
#include "los_cpup.h"
#define MAXTASKNUM 32
UINT32 cpupUse;
UINT32 g_cpuTestTaskId;
VOID Example_CPUP(VOID)
{
printf("entry cpup test example\n");
while(1) {
usleep(100);
}
}
UINT32 Example_CPUP_Test(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S cpupTestTask;
CPUP_INFO_S cpupInfo[MAXTASKNUM];
/* 创建测试CPUP的任务 */
memset(&cpupTestTask, 0, sizeof(TSK_INIT_PARAM_S));
cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_CPUP;
cpupTestTask.pcName = "TestCpupTsk"; /* 测试任务名称 */
cpupTestTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
cpupTestTask.usTaskPrio = 5;
cpupTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_cpuTestTaskId, &cpupTestTask);
if(ret != LOS_OK) {
printf("cpupTestTask create failed.\n");
return LOS_NOK;
}
usleep(100);
/* 系统中运行的任务或者中断数量 */
UINT16 maxNum = MAXTASKNUM;
/* 获取系统所有任务或中断最近1s的CPU占用率 */
cpupUse = LOS_AllCpuUsage(maxNum, cpupInfo, CPUP_LAST_ONE_SECONDS, 0);
printf("the system cpu usage in last 1s: %d\n", cpupUse);
/* 获取最近10s内系统(除idle任务外)总CPU占用率 */
cpupUse = LOS_HistorySysCpuUsage(CPUP_LAST_TEN_SECONDS);
printf("the history system cpu usage in last 10s: %d\n", cpupUse);
/* 获取指定任务在最近1s的CPU占用率,该测试例程中指定的任务为上面创建的CPUP测试任务 */
cpupUse = LOS_HistoryTaskCpuUsage(g_cpuTestTaskId, CPUP_LAST_ONE_SECONDS);
printf("cpu usage of the cpupTestTask in last 1s:\n TaskID: %d\n usage: %d\n", g_cpuTestTaskId, cpupUse);
return LOS_OK;
}
结果验证
编译运行得到的结果为:
the system cpu usage in last 1s: 15
the history system cpu usage in last 10s: 3
cpu usage of the cpupTestTask in last 1s:
TaskID: 10
usage: 0
Trace¶
概述¶
基本概念
Trace实时记录系统行为,类似系统“录像”功能。在系统发生异常后,能辅助用户查看历史事件,定位问题。
运作机制
LiteOS的Trace采用静态代码打桩和缓冲区记录方式,在桩被执行时,获取事件发生的上下文,并写入到缓冲区。
在插桩函数的入参中需要提供监测的事件类型、事件操作的主体对象和事件的参数,这些信息会被写入缓冲区,同时缓冲区中也会记录事件发生的时间、系统中的任务信息等事件上下文信息。
开发指导¶
使用场景
通过Trace了解系统运转的路径,理解系统运行机制。
通过Trace分析系统发生异常前的操作,定位死机问题。
功能
LiteOS的Trace模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
配置Trace缓冲区 |
LOS_TraceInit |
配置Trace缓冲区的地址和大小。 |
开启/停止Trace事件记录 |
LOS_TraceStart |
开启事件记录。 |
LOS_TraceStop |
停止事件记录。 |
|
操作Trace记录的数据 |
LOS_TraceRecordDump |
输出Trace缓冲区数据。 |
LOS_TraceRecordGet |
获取Trace缓冲区的首地址。 |
|
LOS_TraceReset |
清除Trace缓冲区中的事件。 |
|
过滤Trace记录的模块 |
LOS_TraceEventMaskSet |
设置事件掩码,仅记录某些模块的事件。 |
屏蔽特定中断号事件 |
LOS_TraceHwiFilterHookReg |
注册过滤特定中断号事件的钩子函数。 |
插桩函数 |
LOS_TRACE_EASY |
简易插桩。 |
LOS_TRACE |
标准插桩。 |
说明:
LOS_TRACE_EASY(TYPE, IDENTITY, params...) 简易插桩。
一句话插桩,用户在目标源代码中插入该接口即可。
TYPE有效取值范围为[0, 0xF],表示不同的事件类型。
IDENTITY类型UINTPTR,表示事件操作的主体对象。
Params类型UINTPTR,表示事件的参数。 示例:
LOS_TRACE_EASY(1, userId0, userParam1, userParam2); LOS_TRACE_EASY(2, userId0); LOS_TRACE_EASY(1, userId1, userParam1); LOS_TRACE_EASY(2, userId1); LOS_TRACE_EASY(3, userParma1, userParam2, userParam3); // parmas 可复用IDENTITY字段(当IDENTITY不必要时)
LOS_TRACE(TYPE, IDENTITY, params...) 标准插桩。
相比简易插桩,支持动态过滤事件和参数裁剪,但使用上需要用户按规则来扩展。
TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。
IDENTITY和Params的类型及含义同简易插桩。 示例:
1.在enum LOS_TRACE_MASK中定义事件掩码,即模块级别的事件类型。定义规范为TRACE_#MOD#_FLAG,#MOD#表示模块名,例如: TRACE_FS_FLAG = 0x2000 2.在enum LOS_TRACE_TYPE中定义具体事件类型。定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER,例如: FS_READ = TRACE_FS_FLAG | 0; // 读文件 FS_WRITE = TRACE_FS_FLAG | 1; // 写文件 3.定义事件参数。定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... 其中的#TYPE#就是上面2中的#TYPE#,例如: #define FS_READ_PARAMS(fp, fd, flag, size) fp, fd, flag, size 宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪; 当定义为空时,表示不监测该类型事件: #define FS_READ_PARAMS(fp, fd, flag, size) // 不监测文件读事件 4.在适当位置插入代码桩。定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...)) LOS_TRACE(FS_READ, fp, fd, flag, size); // 读文件的代码桩,#TYPE#之后的入参就是上面3中的FS_READ_PARAMS函数的入参。
LiteOS预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见“kernel_lite/include/los_trace.h”。
Trace Mask事件过滤接口LOS_TraceEventMaskSet(UINT32 mask),其入参mask仅高28位生效(对应LOS_TRACE_MASK中某模块的使能位),仅用于模块的过滤,暂不支持针对某个特定事件的细粒度过滤。例如:LOS_TraceEventMaskSet(0x202),则实际设置生效的mask为0x200(TRACE_QUE_FLAG),QUE模块的所有事件均会被采集。一般建议使用的方法为: LOS_TraceEventMaskSet(TRACE_EVENT_FLAG | TRACE_MUX_FLAG | TRACE_SEM_FLAG | TRACE_QUE_FLAG);
如果仅需要过滤简易插桩事件,通过设置Trace Mask为TRACE_MAX_FLAG即可。
Trace缓冲区大小有限,事件写满之后会覆盖写,用户可通过LOS_TraceRecordDump中打印的CurEvtIndex识别最新事件。
Trace错误码
为快速定位可能导致Trace操作失败的原因,初始化Trace和启动Trace均需要返回对应的错误码。其他无返回值的接口如停止Trace、清除与Dump Trace 数据,错误原因均为Trace状态不合法,系统会直接打印错误原因。
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_TRACE_ERROR_STATUS |
0x02001400 |
初始化Trace或启动Trace时状态不正确。 |
|
2 |
LOS_ERRNO_TRACE_NO_MEMORY |
0x02001401 |
初始化Trace时,缓冲区申请失败。 |
解决方案有两个:
|
3 |
LOS_ERRNO_TRACE_BUF_TOO_SMALL |
0x02001402 |
初始化Trace时,缓冲区size设置过小。 |
配置LOSCFG_TRACE_BUFFER_SIZE增大Trace缓冲区的大小,menuconfig菜单项为:Kernel ---> Enable Extend Kernel ---> Enable Trace Feature ---> Trace work mode (Offline mode) ---> Trace record buffer size。 |
4 |
LOS_ERRNO_TRACE_BUF_IS_NULL |
0x02001403 |
在离线模式下初始化Trace,且动态内存关闭,指定缓冲区为NULL。 |
有如下解决方案:
|
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为Trace模块,值为0x14。
开发流程
Trace的典型开发流程:
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Trace Feature菜单,完成Trace的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_TRACE
Trace模块的裁剪开关。
YES/NO
NO
LOSCFG_KERNEL_EXTKERNEL
LOSCFG_RECORDER_MODE_OFFLINE
Trace工作模式为离线模式。
YES/NO
YES
LOSCFG_KERNEL_TRACE
LOSCFG_RECORDER_MODE_ONLINE
Trace工作模式为在线模式。
YES/NO
NO
LOSCFG_KERNEL_TRACE
LOSCFG_TRACE_CLIENT_INTERACT
使能与Trace IDE(LiteOS Studio)的交互,包括数据可视化和流程控制。
YES/NO
NO
LOSCFG_KERNEL_TRACE
LOSCFG_TRACE_PIPELINE_SERIAL
选择串口作为IDE数据可视化通道。
YES/NO
YES
LOSCFG_TRACE_CLIENT_INTERACT
LOSCFG_TRACE_CONTROL_VIA_SHELL
选择shell作为IDE流程控制的方式。
YES/NO
YES
LOSCFG_TRACE_CLIENT_INTERACT && LOSCFG_SHELL
LOSCFG_TRACE_CONTROL_AGENT
选择agent作为IDE流程控制的方式。
YES/NO
NO
LOSCFG_TRACE_CLIENT_INTERACT
LOSCFG_TRACE_NO_CONTROL
不启用IDE的流程控制。
YES/NO
NO
LOSCFG_TRACE_CLIENT_INTERACT
LOSCFG_TRACE_MSG_EXTEND
记录系统更多的扩展信息。
YES/NO
NO
LOSCFG_KERNEL_TRACE
LOSCFG_TRACE_FRAME_CORE_MSG
记录CPUID、中断状态、锁任务状态。
YES/NO
NO
LOSCFG_TRACE_MSG_EXTEND
LOSCFG_TRACE_FRAME_EVENT_COUNT
记录事件的次序编号。
YES/NO
NO
LOSCFG_TRACE_MSG_EXTEND
LOSCFG_TRACE_FRAME_MAX_PARAMS
配置记录事件的最大参数个数。
INT
3
LOSCFG_KERNEL_TRACE
LOSCFG_TRACE_BUFFER_SIZE
配置Trace的缓冲区大小。
INT
2048
LOSCFG_RECORDER_MODE_OFFLINE
(可选)预置事件参数和事件桩(或使用系统默认的事件参数配置和事件桩)。
(可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。
(可选)调用LOS_TraceEventMaskSet设置需要监测的事件掩码(系统默认的事件掩码仅使能中断与任务切换),事件掩码参见“los_trace.h”中的LOS_TRACE_MASK定义。
在需要记录事件的代码起始点调用LOS_TraceStart。
在需要记录事件的代码结束点调用LOS_TraceStop。
调用LOS_TraceRecordDump输出缓冲区数据(函数的入参为布尔型,FALSE表示格式化输出,TRUE表示输出到Windows客户端)。
平台差异性
无。
注意事项¶
由于Trace会影响系统性能,同时考虑到一般只有在产品开发时才需要了解系统发生的事件,因此建议在产品发布时关闭Trace。
编程实例¶
实例描述
本实例实现如下功能:
创建一个用于Trace测试的任务。
设置事件掩码。
启动Trace。
停止Trace。
格式化输出Trace数据。
编程示例
前提条件:在menuconfig菜单中完成Trace模块的配置。
代码实现如下:
#include "los_trace.h"
UINT32 g_traceTestTaskId;
VOID Example_Trace(VOID)
{
UINT32 ret;
LOS_TaskDelay(10);
/* 开启trace */
ret = LOS_TraceStart();
if (ret != LOS_OK) {
dprintf("trace start error\n");
return;
}
/* 触发任务切换的事件 */
LOS_TaskDelay(1);
LOS_TaskDelay(1);
LOS_TaskDelay(1);
/* 停止trace */
LOS_TraceStop();
LOS_TraceRecordDump(FALSE);
}
UINT32 Example_Trace_test(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S traceTestTask;
/* 创建用于trace测试的任务 */
memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */
traceTestTask.uwStackSize = 0x800;
traceTestTask.usTaskPrio = 5;
traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask);
if(ret != LOS_OK){
dprintf("TraceTestTask create failed .\n");
return LOS_NOK;
}
/* 系统默认情况下已启动trace, 因此可先关闭trace,然后清除缓存区后,再重启trace */
LOS_TraceStop();
LOS_TraceReset();
/* 开启任务模块事件记录 */
LOS_TraceEventMaskSet(TRACE_TASK_FLAG);
return LOS_OK;
}
结果验证
编译运行得到的结果为:
*******TraceInfo begin*******
clockFreq = 50000000
CurEvtIndex = 7
Index Time(cycles) EventType CurTask Identity params
0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 0x0
1 0x366d74ae 0x45 0x0 0x1 0x0 0x8 0x1f
2 0x36940da6 0x45 0x1 0xc 0x1f 0x4 0x9
3 0x3694337c 0x45 0xc 0x1 0x9 0x8 0x1f
4 0x36eea56e 0x45 0x1 0xc 0x1f 0x4 0x9
5 0x36eec810 0x45 0xc 0x1 0x9 0x8 0x1f
6 0x3706f804 0x45 0x1 0x0 0x1f 0x4 0x0
7 0x37070e59 0x45 0x0 0x1 0x0 0x8 0x1f
*******TraceInfo end*******
输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。
Time(cycles):表示事件发生的时间,值是时钟的cycle数。
EventType:表示的具体事件可查阅头文件“los_trace.h”中的enum LOS_TRACE_TYPE。
CurTask:表示当前运行在哪个任务中,值为taskId。
Identity:表示事件操作的主体对象,可查阅头文件“los_trace.h”中的#TYPE#_PARAMS。
params:表示的事件参数可查阅头文件“los_trace.h“中的#TYPE#_PARAMS。
下面以序号为0的输出项为例,进行说明。
Index Time(cycles) EventType CurTask Identity params
0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 0x0
Time(cycles)可换算成时间,换算公式为cycles/clockFreq,单位为s。
0x45为TASK_SWITCH事件,即任务切换事件,当前运行任务的taskId为0x1。
Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义:
#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \ taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus
因为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ...,所以Identity为taskId(0x0),第一个参数为oldPriority(0x1f)...
说明:
params的个数由menuconfig中Enable Trace Feature ---> Record max params配置,默认为3,超出的参数不会被记录,用户应自行合理配置该值。
综上所述,任务由0x1切换到0x0,0x1任务的优先级为0x1f,状态为0x4,0x0任务的优先级为0x0。
Perf¶
概述¶
基本概念
Perf为性能分析工具,依赖PMU(Performance Monitoring Unit)对采样事件进行计数和上下文采集,统计出其算法的热点(hot spot)分布和热路径(hot path)。
运作机制
基于事件采样原理,以性能事件为基础,当事件发生时,相应的事件计数器溢出发生中断,在中断处理函数中记录事件信息,包括当前的PC、当前运行的任务ID以及调用栈等信息。
开发指导¶
使用场景
通过Perf查找性能瓶颈和定位热点代码,更高效地优化系统性能。
功能
LiteOS的Perf模块为用户提供下面几种功能,接口详细信息可参见API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化Perf |
LOS_PerfInit |
初始化PMU以及采样缓冲区。 |
开启/停止Perf采样 |
LOS_PerfStart |
开启采样。 |
LOS_PerfStop |
停止采样。 |
|
配置Perf采样事件 |
LOS_PerfConfig |
配置采样事件的类型、周期等。 |
读取采样数据 |
LOS_PerfDataRead |
读取采样数据到指定地址。 |
注册采样数据缓冲区的钩子函数 |
LOS_PerfNotifyHookReg |
注册缓冲区水线到达的处理钩子。 |
LOS_PerfFlushHookReg |
注册缓冲区刷cache的钩子。 |
|
去初始化Perf |
LOS_PerfDeinit |
去初始化PMU。 |
须知: 采样数据缓冲区为环形buffer,buffer中读过的区域可以覆盖写,未被读过的区域不能被覆盖写。
Perf错误码
为快速定位可能导致Perf操作失败的错误原因,Perf初始化和Perf采样配置接口均需要返回对应的错误码。具体描述如下表所示:
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_PERF_STATUS_INVALID |
0x02002000 |
初始化Perf或配置采样事件时的状态不正确。 |
禁止重复初始化Perf,只能在Perf处于停止状态时才能配置采样事件。 |
2 |
LOS_ERRNO_PERF_HW_INIT_ERROR |
0x02002001 |
初始化Perf时硬件PMU初始化失败。 |
确认平台是否具备硬件PMU单元,以及PMU的中断号是否正确。 |
3 |
LOS_ERRNO_PERF_TIMED_INIT_ERROR |
0x02002002 |
初始化Perf时高精度Hrtimer PMU初始化失败。 |
确认平台是否具备高精度定时器单元。 |
4 |
LOS_ERRNO_PERF_SW_INIT_ERROR |
0x02002003 |
初始化Perf时软件PMU初始化失败。 |
确认是否开启软件PMU功能。 |
5 |
LOS_ERRNO_PERF_BUF_ERROR |
0x02002004 |
初始化Perf时buffer初始化失败。 |
配置的Perf buffer size过大或过小,可通过修改“los_config.h”中LOS_PERF_BUFFER_SIZE解决。 |
6 |
LOS_ERRNO_PERF_INVALID_PMU |
0x02002005 |
配置采样事件的PMU未注册。 |
各类PMU可通过menuconfig进行选配, 确保对应的PMU已开启。 |
7 |
LOS_ERRNO_PERF_PMU_CONFIG_ERROR |
0x02002006 |
配置采样事件的事件参数不合法。 |
检查LOS_PerfConfig中事件类型、事件ID、事件周期是否合法。 事件类型见enum PerfEventType,包括:
|
8 |
LOS_ERRNO_PERF_CONFIG_NULL |
0x02002007 |
配置采样事件的入参为空指针。 |
LOS_PerfConfig入参不能为空指针。 |
9 |
LOS_ERRNO_PERF_CONFIG_TASK_FILTER_ERROR |
0x02002008 |
配置任务白名单失败。 |
检查拷贝白名单时memcpy_s入参是否正确。。 |
10 |
LOS_ERRNO_PERF_INIT_NO_MEMERY |
0x02002009 |
初始化Perf时内存不足。 |
检查当前系统内存剩余或者关闭分任务统计功能以减少内存使用。。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为Perf模块,值为0x20。
开发流程
Perf的典型开发流程:
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Perf Feature菜单,完成Perf的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_PERF
Perf模块的裁剪开关
YES/NO
NO
LOSCFG_KERNEL_EXTKERNEL
LOSCFG_PERF_CALC_TIME_BY_TICK
Perf模块使用Tick作为计时单位
YES/NO
YES
LOSCFG_KERNEL_PERF
LOSCFG_PERF_CALC_TIME_BY_CYCLE
Perf模块使用系统Cycle作为计时单位
YES/NO
NO
LOSCFG_KERNEL_PERF
LOSCFG_PERF_HW_PMU
使能硬件PMU采样事件
YES/NO
NO
LOSCFG_KERNEL_PERF&&
LOSCFG_ARCH_PMU
LOSCFG_PERF_TIMED_PMU
使能高精度周期PMU采样事件
YES/NO
NO
LOSCFG_KERNEL_PERF && LOSCFG_COMPAT_LINUX_HRTIMER
LOSCFG_PERF_SW_PMU
使能软件PMU采样事件
YES/NO
YES
LOSCFG_KERNEL_PERF
LOSCFG_KERNEL_PERF_PER_TASK
Perf分任务统计模式开关
YES/NO
NO
LOSCFG_KERNEL_PERF
LOSCFG_KERNEL_PERF_SEPARATED_IRQ
Perf独立中断统计模式开关
YES/NO
NO
LOSCFG_KERNEL_PERF && LOSCFG_ARCH_INTERRUPT_TAKEOVER
调用LOS_PerfConfig配置需要采样的事件。
Perf提供2种模式的配置, 及3大类型的事件配置:
2种模式:计数模式(仅统计事件发生次数)、采样模式(收集上下文如任务ID、PC、backtrace等)。
3种事件类型:CPU硬件事件(cycle、branch、icache、dcache等)、OS软件事件(task switch、mux pend、irq等)、高精度周期事件(cpu clock)。
在需要采样的代码起始点调用LOS_PerfStart。
在需要采样的代码结束点调用LOS_PerfStop。
调用输出缓冲区数据的接口LOS_PerfDataRead读取采样数据,并使用LiteOS Studio工具解析(单击调测工具里的性能分析tab页签)。
平台差异性
针对类型为硬件事件的采样,其依赖PMU硬件单元,且具有平台差异性。目前LiteOS Perf支持armv7 、armv8、risc-v、xea2 与LingLong架构的PMU。
注意事项¶
Perf用于性能调测,对系统性能有一定影响,建议在产品发布时,关闭Perf模块的裁剪开关LOSCFG_KERNEL_PERF。
编程实例¶
实例描述
本实例实现如下功能:
配置采样事件。
启动Perf。
执行需要统计的算法。
停止Perf。
输出统计结果。
编程示例
前提条件:在menuconfig菜单中完成Perf模块的配置。
打开菜单,进入Kernel ---> Enable Extend Kernel ---> Enable Perf Feature,然后选中“Enable Hardware Pmu Events for Sampling, Enable Perf Per-Task mode”和“Enable Perf Separated IRQ mode”,使能PMU对硬件事件监测,并使能perf分任务统计以及中断独立统计。
代码实现如下:
#include "los_perf.h"
#include "los_memory.h"
#define TEST_PERF_BUFFER_SIZE 1024
#define OUTPUT_BUFFER_SIZE 0x200
#ifndef LOSCFG_KERNEL_MEM_ALLOC
STATIC CHAR g_buffer[TEST_PERF_BUFFER_SIZE] = {0};
#endif
STATIC VOID OsPrintBuff(const CHAR *buf, UINT32 num)
{
UINT32 i;
dprintf("num: ");
for (i = 0; i < num; i++) {
dprintf(" %02u", i);
}
dprintf("\n");
dprintf("hex: ");
for (i = 0; i < num; i++) {
dprintf(" %02x", buf[i]);
}
dprintf("\n");
}
VOID PerfTestHWEvent(VOID)
{
UINT32 ret;
CHAR buf[OUTPUT_BUFFER_SIZE] = {0};
UINT32 len;
CHAR *buffer = NULL;
PerfConfigAttr attr1 = {
.eventsCfg = {
.type = PERF_EVENT_TYPE_HW,
.events = {
[0] = {PERF_COUNT_HW_CPU_CYCLES, 0xFFFF},
[1] = {PERF_COUNT_HW_BRANCH_INSTRUCTIONS, 0xFFFFFF00},
},
.eventsNr = 2,
.predivided = 1, /* cycle counter increase every 64 cycles */
},
.taskIds = {0},
.taskIdsNr = 0,
.needSample = 0,
.sampleType = PERF_RECORD_IP | PERF_RECORD_CALLCHAIN,
};
#ifdef LOSCFG_KERNEL_MEM_ALLOC
buffer = (CHAR *)LOS_MemAlloc(m_aucSysMem1, TEST_PERF_BUFFER_SIZE);
#else
buffer = g_buffer;
#endif
ret = LOS_PerfInit(buffer, TEST_PERF_BUFFER_SIZE);
if (ret != LOS_OK) {
PRINT_ERR("perf init error %u\n", ret);
return;
}
ret = LOS_PerfConfig(&attr1);
if (ret != LOS_OK) {
PRINT_ERR("perf config error %u\n", ret);
return;
}
PRINTK("------count mode------\n");
LOS_PerfStart(0);
test(); /* code snippets concerned by users */
LOS_PerfStop();
PRINTK("--------sample mode------ \n");
attr1.needSample = 1;
LOS_PerfConfig(&attr1);
LOS_PerfStart(2);
test(); /* code snippets concerned by users */
LOS_PerfStop();
len = LOS_PerfDataRead(buf, OUTPUT_BUFFER_SIZE); /* get sample data */
OsPrintBuff(buf, len); /* print data */
memset(buf, 0, sizeof(buf));
#ifdef LOSCFG_KERNEL_MEM_ALLOC
(VOID)LOS_MemFree(m_aucSysMem1, buffer);
#endif
LOS_PerfDeinit();
}
结果验证
编译运行得到的结果为:
------count mode------
[EMG] perf count [tid 0 / Swt_Task]:
[EMG] [cycles] eventType: 0xff count: 0
[EMG] [branches] eventType: 0xc count: 0
[EMG] perf count [tid 1 / IdleCore000]:
[EMG] [cycles] eventType: 0xff count: 0
[EMG] [branches] eventType: 0xc count: 0
[EMG] perf count [tid 2 / app_Task]:
[EMG] [cycles] eventType: 0xff count: 40043481344
[EMG] [branches] eventType: 0xc count: 10002153167
[EMG] perf count [tid 3 / Swt_Task]:
[EMG] [cycles] eventType: 0xff count: 0
[EMG] [branches] eventType: 0xc count: 0
[EMG] perf count [tid 4 / IdleCore000]:
[EMG] [cycles] eventType: 0xff count: 100718720
[EMG] [branches] eventType: 0xc count: 5182967
[EMG] perf count [tid 5 / SerialShellTask]:
[EMG] [cycles] eventType: 0xff count: 0
[EMG] [branches] eventType: 0xc count: 0
[EMG] perf count [tid 6 / SerialEntryTask]:
[EMG] [cycles] eventType: 0xff count: 0
[EMG] [branches] eventType: 0xc count: 0
[EMG] perf count [irq@core 0]:
[EMG] [cycles] eventType: 0xff count: 891017
[EMG] [branches] eventType: 0xc count: 2948096
[EMG] perf count [irq@core 1]:
[EMG] [cycles] eventType: 0xff count: 889686
[EMG] [branches] eventType: 0xc count: 2948154
[EMG] time used: 44668000(us)
--------sample mode------
[EMG] time used: 44673000(us)
num: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 ...
hex: 00 ef ef ef 00 00 00 00 14 00 00 00 60 00 00 00 02 00 00 00 90 2d 20 80 02 00 00 00 d4 5f 20 80 54 55 21 80 90 2d 20 80 02 ...
针对计数模式,系统在perf stop后会打印如下信息:
当开启分任务统计功能后,会先打印每个任务的头信息,其后的事件计数为此任务的计数
perf count [tid 2 / app_Task]任务ID:2,任务名称:app_Task。
当关闭分任务统计功能后,会先打印每个核的头信息,其后的事件计数为此核的计数
perf count [core 0]核ID:core 0。
当开启中断独立统计功能后,会先打印每个核中断的头信息,其后的事件计数为此核中断的计数
perf count [irq@core 0]核ID:core 0。
事件计数的信息:
[cycles] eventType: 0xff count: 40043481344事件名称:cycles、事件类型:0xff、事件发生的次数:40043481344。
说明:
当采样事件为硬件PMU事件时,打印的事件类型为实际的硬件事件ID,非enum PmuHwId中定义的抽象类型。硬件事件ID详见各架构手册规定。针对采样模式,系统会将采样数据记录到用户指定的内存中,即LOS_PerfInit的入参。用户可以自行打印该内存的地址,然后通过JTAG口导出该片内存,再使用LiteOS Studio线下工具解析。用户也可以通过LOS_PerfDataRead将数据读到指定地址,进行查看或进一步处理。示例中OsPrintBuff为测试接口,其按字节打印Read到的采样数据,num表示第几个字节, hex表示该字节中的数值。
须知:在RISC-V架构中,使用采样模式需要在定时中断处理函数中调用OsPerfCheckEventCount()函数。一般使用Tick中断处理函数。
Perf独立中断统计模式下,PMU中断不会被单独统计。另外,由于Timed PMU同样依赖于中断工作,计数可能与预期不一致。如果需要使用Timed PMU,请关闭Perf独立中断。
C++支持¶
概述¶
基本概念
C++作为目前使用最广泛的编程语言之一,支持类、封装、重载等特性,是在C语言基础上开发的一种面向对象的编程语言。
运作机制
STL(Standard Template Library)标准模板库,是一些“容器”的集合,也是算法和其他一些组件的集合。其目的是标准化组件,使用标准化组件后可以不用重新开发,直接使用现成的组件。
开发指导¶
功能
功能分类 |
接口名 |
描述 |
|---|---|---|
使用C++特性的前置条件 |
LOS_CppSystemInit |
初始化C++构造函数 |
说明: 该函数有3个入参:
第一个参数:init_array段的起始地址。
第二个参数:init_array段的结束地址。
第三个参数:标记调用C++特性时的场景,包括BEFORE_SCATTER(在分散加载快速启动阶段使用C++特性)、AFTER_SCATTER(在分散加载非快速启动阶段使用C++特性)、NO_SCATTER(在非分散加载特性中使用C++特性,或者在分散加载中不使用C++特性)。
开发流程
打开菜单,选择Kernel ---> Enable Extend Kernel ---> C++ Support,使能C++。
配置项
含义
取值范围
默认值
依赖
LOSCFG_KERNEL_CPPSUPPORT
C++特性的裁剪开关
YES/NO
YES
LOSCFG_KERNEL_EXTKERNEL
使用C++特性之前,需要调用函数LOS_CppSystemInit,初始化C++构造函数。
由于在分散加载应用场景下,C++初始化中涉及到的相关代码、数据段加载的时机会有所不同,所以在开启和不开启分散加载这两种情况下,C++初始化函数LOS_CppSystemInit的调用有所不同。其中构造函数的初始化存在init_array这个段中,段区间通过变量__init_array_start、__init_array_end传递。
不开启分散加载特性
在不开启分散加载特性的情况下,需要在调用C++代码前,以NO_SCATTER(表示并未开启分散加载特性)参数调用LOS_CppSystemInit:
LOS_CppSystemInit((unsigned long)&__init_array_start, (unsigned long)&__init_array_end, NO_SCATTER);开启分散加载特性
如果需要在分散加载的快速启动阶段调用相关C++代码:
在分散加载的快速启动阶段调用C++代码之前,以BEFORE_SCATTER(表示在分散加载快速启动阶段调用)参数调用LOS_CppSystemInit:
LOS_CppSystemInit((unsigned long)&__init_array_start, (unsigned long)&__init_array_end, BEFORE_SCATTER);在分散加载的非快速启动阶段,再以AFTER_SCATTER(表示在分散加载非快速启动阶段调用)参数调用LOS_CppSystemInit:
LOS_CppSystemInit((unsigned long)&__init_array_start, (unsigned long)&__init_array_end, AFTER_SCATTER);如果无需在分散加载的快速启动阶段调用相关C++代码:
以NO_SCATTER参数调用一次LOS_CppSystemInit:
LOS_CppSystemInit((unsigned long)&__init_array_start, (unsigned long)&__init_array_end, NO_SCATTER);
C函数与C++函数混合调用。
在C++中调用C程序的函数,代码需加入C++包含的宏:
#ifdef __cplusplus #if __cplusplus extern "C" { #endif /* __cplusplus */ #endif /* __cplusplus */ /* code */ ... #ifdef __cplusplus #if __cplusplus } #endif /* __cplusplus */ #endif /* __cplusplus */
注意事项¶
LiteOS的C++功能需要编译器适配才能支持,编译器编译链接C++代码时需要使用LiteOS提供的C库。
C++的cout函数依赖console功能。
编程实例¶
实例描述
本实例实现了在LiteOS上运行C++的代码。
编写C++代码。
在运行C++代码之前,先在app_init函数里初始化C++构造函数。此处未开启分散加载特性,所以只需以NO_SCATTER参数调用一次LOS_CppSystemInit即可。
在LOS_CppSystemInit之后调用编写好的C++代码。
编程示例
前提条件:打开菜单使能C++支持。
C++代码实现如下:
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
using namespace std;
class TestClass {
public:
TestClass(int arg);
~TestClass(void);
void PrintTest(void);
void StringTest(void);
void MapTest(void);
private:
int intTest;
string stringTest;
map<string, int> mapTest;
};
TestClass::TestClass(int arg)
{
cout << "TestClass is constructed here, arg = " << arg << endl;
intTest = arg;
}
TestClass::~TestClass(void)
{
cout << "TestClass is destructed" << endl;
}
void TestClass::PrintTest(void)
{
cout << __FUNCTION__ << " enter" << endl;
cout << " intTest = " << this->intTest << endl;
}
void TestClass::StringTest(void)
{
cout << __FUNCTION__ << " enter" << endl;
string a("Lite");
string b("OS");
string c("LiteOS");
if (a != b) {
cout << " " << a << " != " << b << endl;
}
a += b;
if (a == c) {
cout << " " << a << " == " << c << endl;
}
}
void TestClass::MapTest(void)
{
cout << __FUNCTION__ << " enter" << endl;
mapTest.insert(pair<string, int>("Huawei", 1));
mapTest.insert(pair<string, int>("LiteOS", 2));
mapTest.insert(pair<string, int>("Open", 3));
mapTest.insert(pair<string, int>("Source", 4));
cout << " show map key&value" << endl;
for (auto &it : mapTest) {
cout << " " << it.first << " " << it.second << endl;
}
mapTest["LiteOS"] = 8; /* 8: new value */
cout << " change value of \"LiteOS\" key" << endl;
for (auto &it : mapTest) {
cout << " " << it.first << " " << it.second << endl;
}
}
void CppTestEntry(void)
{
cout << "LiteOS cpp sample start" << endl;
TestClass test(123);
test.PrintTest();
test.StringTest();
test.MapTest();
cout << "LiteOS cpp sample stop" << endl;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
app_init中初始化C++构造函数后,调用C++代码,实现如下:
void app_init(void)
{
......
/* 初始化C++构造函数 */
LOS_CppSystemInit((UINT32)&__init_array_start, (UINT32)&__init_array_end, NO_SCATTER);
/* 调用C++代码 */
CppTestEntry();
......
}
结果验证
运行CppTestEntry()函数,结果如下:
LiteOS cpp sample start
TestClass is constructed here, arg = 123
PrintTest enter
intTest = 123
StringTest enter
Lite != OS
LiteOS == LiteOS
MapTest enter
show map key&value
Huawei 1
LiteOS 2
Open 3
Source 4
change value of "LiteOS" key
Huawei 1
LiteOS 8
Open 3
Source 4
LiteOS cpp sample stop
TestClass is destructed
控制台¶
概述¶
基本概念
控制台是用于输入/输出系统信息的设备,可以向用户发送文本输出,并从用户接收文本输入。
开发指导¶
功能
LiteOS的控制台模块为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化 |
system_console_init |
初始化控制台。 |
virtual_serial_init |
初始化虚拟串口。 |
|
去初始化 |
system_console_deinit |
去初始化控制台。 |
virtual_serial_deinit |
去初始化虚拟串口。 |
注意事项¶
控制台功能依赖于文件系统。
控制台输出任务的优先级为30。
异常输出不经过控制台。
文件系统¶
功能概述¶
本章以Hi3556V200为例,介绍LiteOS目前支持的文件系统,包括VFS、FAT、YAFFS2和LITTLEFS。
各个文件系统功能概述
文件系统 |
功能特点概述 |
|---|---|
VFS |
VFS是Virtual File System(虚拟文件系统)的缩写。通过采用标准的Unix系统接口,VFS可以访问位于不同物理介质上的不同文件系统,为各类文件系统提供一个统一的操作方式。 |
FAT |
FAT文件系统是File Allocation Table(文件配置表)的简称,有FAT12、FAT16、FAT32。在可移动存储介质(U盘、SD卡、移动硬盘等)上使用FAT文件系统,可以使设备在Windows、Linux等桌面系统之间保持很好的兼容性。 |
YAFFS2 |
YAFFS是Yet Another Flash File System的简称,是一种针对Nand Flash的开源嵌入式文件系统,适用于大容量的存储设备,使得Nand Flash具有高效性和健壮性。 YAFFS2为文件系统提供了损耗平衡和掉电保护,保证在对文件系统修改的过程中,不会因发生意外而造成数据受损。LiteOS的YAFFS2支持多分区。 |
LITTLEFS |
LITTLEFS(Little File System)是一种基于块的故障保护文件系统,使用小日志存储元数据,使用较大的写时复制(COW)结构存储文件数据,主要应用于NOR Flash闪存的文件管理。 LITTLEFS为文件系统提供了磨损平衡和掉电保护,保证在对文件系统修改的过程中,不会因发生意外而造成数据受损。 |
VFS¶
概述¶
基本概念
VFS是Virtual File System(虚拟文件系统)的缩写,是一个异构文件系统之上的软件粘合层,为用户提供统一的Unix文件操作接口。
由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统则需要使用不同的非标准接口。但是,通过在系统中添加VFS层来提供统一的抽象接口,便可以屏蔽底层异构类型的文件系统差异,使得访问文件系统时无需关注底层的存储介质和文件系统类型,从而提高开发效率。
LiteOS中,VFS框架通过在内存中的树结构实现,树的每个结点都是一个inode结构体。设备注册和文件系统挂载后会根据路径在树中生成相应的结点。
VFS的两大主要功能如下:
查找节点。
统一调用(标准)。
运作机制
通过VFS层,可以使用标准的Unix文件操作函数(如open、read、write等)实现对不同介质上不同文件系统的访问。
VFS框架内存中的inode的关键在于u和i_private字段,一个是函数方法结构体的指针,一个是数据指针。inode树结点有三种类型:
虚拟结点:作为VFS框架的虚拟文件,保持树的连续性,如/bin、/bin/vs、/dev。
设备结点:/dev目录下,对应一个设备,如/dev/mmc0。
挂载点:调用mount函数后生成,如/bin/vs/sd、/ramfs、/yaffs。
图 1 VFS树节点结构

系统初始化过程中会调用los_vfs_init(),将“/”作为root_inode。
文件描述符
本设计中文件描述符分两种,采用动态内存数组的形式来对普通文件描述符进行管理,采用全局数组管理网络描述符,这两个数组在内存上并不连续。
File描述符,即普通文件描述符,其中0保留作为系统stdin标准输入、1保留作为系统stdout标准输出、2保留作为系统stderr标准错误输出。用户可以分配的文件描述符从3开始到CONFIG_NFILE_DESCRIPTORS-1。
Socket描述符,该描述符的分配从CONFIG_NFILE_DESCRIPTORS开始。
开发指导¶
接口说明
头文件 |
接口名 |
描述 |
|---|---|---|
open_source/incubator-nuttx/liteos/fs/include/fs/fs.h |
register_driver |
注册字符驱动到文件系统。 |
unregister_driver |
取消字符驱动注册。 |
|
register_blockdriver |
注册块设备驱动到文件系统。 |
|
unregister_blockdriver |
取消块设备驱动注册。 |
|
open_blockdriver |
打开块设备。 |
|
close_blockdriver |
关闭块设备。 |
|
find_blockdriver |
查找块设备。 |
|
fs/include/los_fs.h |
chattr |
改变文件系统中的文件属性,目前仅FAT文件系统支持,修改后的文件属性目前支持4种:F_RDO(只读)、F_HID(隐藏)、F_SYS(系统文件)、F_ARC(存档)。 |
ls |
列出目录内容。 |
|
rindex |
从指定字符串的右面开始搜索第一次出现指定字符的位置。 |
说明: 关于文件属性:
当前只支持修改FAT文件系统的文件属性,其他文件系统对只读等属性有各自的处理方式。
F_RDO(只读)、F_HID(隐藏)、F_SYS(系统文件)和F_ARC(存档)这4种属性并不冲突,可以任意修改。
只读属性文件/目录不允许被删除。
只读属性文件/目录允许重命名。
只读文件不允许以O_CREAT、O_TRUNC,以及有含有写权限的方式打开。
在LiteOS中隐藏属性文件可见,在Windows中不可见(不显示隐藏文件属性的情况下)。
在LiteOS中文件加上隐藏属性,在Windows中只能通过命令行找到(在显示/不显示隐藏文件属性的情况下,该文件在Windows中都不可见)。
开发流程
推荐驱动开发人员使用VFS框架来注册/卸载设备,在应用层使用open()、read()操作字符设备文件来调用驱动。
打开菜单,选择FileSystem ---> Enable VFS,使能VFS。
配置项
含义
取值范围
默认值
依赖
LOSCFG_FS_VFS
VFS框架的裁剪开关
YES/NO
YES
无
调用register_driver()、register_blockdriver()接口生成设备结点,调用mount()接口生成文件系统挂载结点。生成设备节点的开发流程详见适配文件操作的“开发指导”。
注意事项¶
VFS下所有文件系统创建的目录名和文件名长度最多为255个字符,若超过则创建失败。
VFS层支持的文件和目录最大全路径长度为259个字符,若超过,则创建失败
所有文件系统中的文件名和目录名中只仅支持“-” 与“_”两种特殊字符,使用其他特殊字符可能造成不可预知的后果,请谨慎为之。
VFS下的所有文件系统,在创建模式或只写模式打开文件,此时文件权限为open接口mode入参值默认为0666,因VFS下的文件系统对文件权限模式支持不全, 推荐使用0666入参。
VFS在优化迭代过程中,对部分POSIX接口(rename、dup、opendir等)的异常错误分支的errno返回值会有更改,以便更贴近于POSIX标准,业务在使用errno返回值时请谨慎处理。
对于块设备节点(例如“/dev/mmc0”),如果块设备驱动的bpos实现unlink回调函数且返回值为OK,则误调用unlink(“/dev/mmc0”)会导致块设备节点被删除和节点消失,并影响正常业务功能;如果想保持块设备节点常驻,不实现unlink回调即可。
ls命令会显示块设备节点文件的size,size值为块设备的容量值,如果不想显示对应的设备容量,块设备驱动的bpos不实现geometry回调函数即可。
VFS可以直接递归创建多级目录。
设备分为字符设备和块设备,为了设备上的数据安全,需挂载相应文件系统后通过文件系统接口操作数据。
los_vfs_init() 只能调用一次,多次调用将会造成文件系统异常。
open打开一个文件时,参数O_RDWR、O_WRONLY、O_RDONLY互斥,只能出现一个,若出现2个或以上作为open的参数,会出现不可预知的错误,禁止使用。
不建议open一个目录直接获取目录数据。如果要获取目录信息,可以调用opendir接口打开目录。
open()、read()、write()等为非线程安全接口,在多线程场景下,建议使用fopen()、fread()、fwrite()等接口。
挂载文件系统请严格按照手册进行,错误挂载可能损坏设备及系统。
挂载点必须为空目录,不能重复挂载至同一挂载点或挂载至其他挂载点下的目录或文件。
不要在休眠唤醒或者分散加载阶段挂载一个不存在的文件系统。
在unregister_driver卸载设备之前,要确保字符设备已经正确的close,即open和close的次数要对应,否则会卸载失败。
在umount之前,需确保此文件系统中的所有目录及文件全部关闭,否则umount会失败。如果强制umount,可能导致包括但不限于文件系统损坏、设备损坏等问题,对此华为不承担任何责任。
SD卡移除前,需确保所有目录及文件已经全部关闭,并进行umount操作。如果强制拔卡,可能导致包括但不限于SD数据丢失、SD卡损坏等问题,对此华为不承担任何责任。
FAT¶
概述¶
基本概念
FAT文件系统是File Allocation Table(文件配置表)的简称,FAT文件系统有FAT12、FAT16、FAT32。FAT文件系统将硬盘分为MBR区、DBR区、FAT区、DIR区、DATA区等5个区域。
FAT文件系统支持多种介质,特别是在可移动存储介质(U盘、SD卡、移动硬盘等)上广泛使用。而且,FAT文件系统可以使嵌入式设备和Windows、Linux等桌面系统保持很好的兼容性,方便用户管理操作文件。
LiteOS的FAT文件系统的特点有:
代码量和资源占用小、可裁切、支持多种物理介质。
与Windows、Linux等系统保持兼容。
支持多设备、多分区识别等功能。
支持硬盘多分区,可以在主分区以及逻辑分区上进行文件操作。
支持识别出硬盘上其他类型的文件系统,比如NTFS。
支持基于FAT32 的虚拟分区特性。
当开启虚拟分区特性时,用户可通过调用虚拟分区的设置接口los_set_virpartparam(),对特定的物理分区配置虚拟分区的数量、空间百分比大小、虚拟分区的入口名等,并会根据设置的入口名,在根目录建立对应的文件夹作为虚拟分区入口。
在对应的虚拟分区内部进行操作,即视为在对应的虚拟分区内进行操作。
若所有虚拟分区的空间百分比总和未达到100%,则允许在虚拟分区入口目录外进行写入操作,此时所有虚拟分区入口外部的总可写入空间,即为百分比总和的剩余空间。若所有虚拟分区的空间百分比达到了100%,则在虚拟分区入口目录外部将会以空间不足的理由,拒绝写入操作。
开发指导¶
FAT文件系统的接口说明
头文件 |
接口名 |
描述 |
|---|---|---|
fs/include/los_fs.h |
getlabel |
获取盘符 |
set_label |
设置盘符 |
|
format |
格式化磁盘 |
|
los_set_systime_status |
设置FAT文件当前系统时间有效或无效 |
|
LOS_SetSyncThreadPrio |
设置FAT文件系统缓存异步刷新任务的优先级 |
|
LOS_SetSyncThreadInterval |
设置FAT文件系统缓存异步刷新的时间间隔 |
|
LOS_GetSyncThreadInterval |
获取FAT文件系统缓存异步刷新的时间间隔 |
|
LOS_SetDirtyRatioThreshold |
设置FAT文件系统缓存异步刷新的脏数据块比例阈值 |
|
LOS_GetDirtyRatioThreshold |
获取FAT文件系统缓存异步刷新的脏数据块比例阈值 |
|
LOS_SetBlockExpireInterval |
设置FAT文件系统块的最大过期时间间隔 |
|
LOS_GetBlockExpireInterval |
获取FAT文件系统块的最大过期时间间隔 |
|
LOS_FatSetMemPool |
设置FAT文件系统独立内存池 |
|
LOS_BcacheSetMemPool |
设置FAT文件系统缓存独立内存池 |
|
vfs_files.h |
LOS_PartitionFormat |
格式化磁盘,仅部分平台定制接口 |
LOS_DiskPartition |
按比例创建磁盘分区,仅部分平台定制接口 |
说明:
使能FAT文件系统缓存(Cache)异步刷新后,系统会创建一个任务刷Cache,线程被触发唤醒进行脏数据刷新的条件如下:
周期性5s唤醒。
脏数据停留时间超过3s。
脏数据量超过50%时,触发任何一个条件,线程都会立刻把脏数据写回磁盘。
在“open_source/FatFs/source/ffconf.h”文件中可以配置FAT文件系统,其中FF_FS_LOCK为最多支持同时打开的文件(文件夹)数。
虚拟分区的接口说明
头文件 |
接口名 |
接口功能描述 |
|---|---|---|
fs/include/los_fs.h |
los_set_virpartparam |
设置虚拟分区的参数 |
virstatfs |
获取指定虚拟分区的状态 |
虚拟分区的调配结构体定义如下所示:
#define _MAX_ENTRYLENGTH 16
#define _MAX_VIRVOLUMES 5
typedef struct virtual_partition_info
{
char *devpartpath;
int virpartnum;
double virpartpercent[_MAX_VIRVOLUMES];
char virpartname[_MAX_VIRVOLUMES][_MAX_ENTRYLENGTH + 1];
} virpartinfo;
成员 |
描述 |
注意事项 |
|---|---|---|
devpartpath |
设备节点路径 |
所选定的设备节点的名称。必须是/dev下的设备节点路径,上限是DISK_NAME。 |
virpartnum |
虚拟分区数量 |
最小值为1,最大值为_MAX_VIRVOLUMES,即为5。 |
virpartpercent |
虚拟分区百分比 |
总和必须大于0.00,小于等于1.00。每个成员的值必须大于0.00,小于1.00。 |
virpartname |
虚拟分区入口名 |
入口名有效字符最多为_MAX_ENTRYLENGTH ,即16个字符,不得为空。传入的集合不可以有重名项。入口名不得含有非法名称字符。 |
须知:
在LiteOS中,虚拟分区特性仅支持特定的单个物理分区进行,可通过调配虚拟分区参数接口设置设备节点。
设置指定物理设备节点后,会对设置进行上锁,不允许再次更改设置。挂载时就会采用设置的参数进行配置。当卸载后,才会对设置进行解锁。
卸载后,需要通过调配虚拟分区参数接口重新设置虚拟分区参数。
当插入的物理分区中已经设置了虚拟分区,但是其配置参数与当前系统通过调配接口所设置的参数不一致时,则将按照已有的配置参数对此分区进行管理。
虚拟分区涉及的接口行为变化
开启虚拟分区特性后,部分接口的行为会在虚拟分区下有部分变化。下述这些接口的行为变化只对于已成功应用了虚拟分区特性的物理分区,对于未能应用虚拟分区的基本FAT文件系统的物理分区,以及其他的文件系统,这些接口行为不涉及变化。
下述的接口只是在执行功能上的行为针对虚拟分区有所变化,函数的调用方式、参数等均不受影响。
序号 |
接口名 |
行为变化 |
返回值与错误码说明 |
|---|---|---|---|
1 |
mount |
挂载了基本FAT分区后,会尝试识别当前物理分区是否已经应用过虚拟分区,并尝试对该物理分区按照其配置的信息加载虚拟分区,检查并尝试修复虚拟分区入口;若当前物理分区未应用过虚拟分区并符合启动虚拟分区的条件,则会自动对该物理分区应用虚拟分区,并建立虚拟分区。 |
返回值为-1表示基本FAT分区挂载失败,此时会触发libc错误码指示发生了何种错误; 返回值为0表示基本FAT分区挂载成功,此时错误码若为0表示虚拟分区挂载成功,若错误码为VIRERR_MODIFIED、VIRERR_CHAIN_ERR、VIRERR_OCCUPIED、VIRERR_NOTCLEAR、VIRERR_NOTFIT、VIRERR_NOPARAM表示虚拟分区挂载失败 |
2 |
format |
当FAT分区已挂载后,若格式化成为FAT32文件系统,则会在格式化完成后对该物理分区应用虚拟分区;若非FAT32文件系统,则会完成格式化操作,并清除该物理分区的虚拟分区;若FAT分区未挂载,无论何种文件系统,则均会完成格式化操作后清除该物理分区的虚拟分区。 |
返回值为-1表示基本FAT分区格式化失败,此时会触发libc错误码指示发生了何种错误; 返回值为0表示基本FAT分区格式化完成。若此时分区并未挂载,则错误码则会为VIRERR_NOTMOUNT;若格式化的分区非调配接口所设定设备节点路径,或者调配接口并未设置参数,则此时会指示VIRERR_NOPARAM。若分区已挂载,错误码为0表示应用或重新应用虚拟分区成功,或者错误码可能为VIRERR_NOTFIT。 |
3 |
open |
请参阅虚拟分区的兼容性的说明。 |
无 |
4 |
mkdir |
请参阅虚拟分区的兼容性的说明。 |
无 |
5 |
rmdir |
当物理分区已经成功应用虚拟分区时,不允许删除虚拟分区入口目录。 |
无 |
6 |
remove |
当物理分区已经成功应用虚拟分区时,不允许删除虚拟分区入口目录。 |
无 |
7 |
rename |
当物理分区已经成功应用虚拟分区时,不允许重命名虚拟分区入口目录。 |
无 |
虚拟分区的错误码
应用虚拟分区发生错误,只会发生于挂载时或者格式化时。当基本FAT文件系统的对应操作完成后,进行虚拟分区的配置与应用。此时若发生某种问题或者触发某种条件,虚拟分区会将错误码传递给上层,以分辨应用虚拟分区时发生了什么情况。同时该分区将会以基本FAT文件系统进行管理,而不会在此基础上采用虚拟分区管理。
说明: 对于挂载和格式化操作的返回值,指示的是基本FAT文件系统的成功或失败。
当接口的返回值为0时,表示对应的基本FAT文件系统操作成功,此时可通过访问errno获取虚拟分区的错误码,以检查应用虚拟分区过程中是否有异常产生。
当接口的返回值为-1时,表示于对应的基本FAT文件系统操作失败,此时的errno为libc的错误码,表示基本FAT操作失败的原因,不再表示虚拟分区的提示信息。
序号 |
定义 |
实际数值 |
描述 |
触发情况 |
|---|---|---|---|---|
1 |
VIRERR_MODIFIED |
0x10000001 |
虚拟分区保留扇区信息遭到破坏 |
是否被重新分区、是否调整过分区大小、或者SD卡是否曾遭到破坏。 |
2 |
VIRERR_CHAIN_ERR |
0x10000002 |
虚拟分区入口目录已被移出所在的虚拟分区 |
虚拟分区入口目录在其他平台上被删除后重建,有一定概率会触发该问题。 |
3 |
VIRERR_OCCUPIED |
0x10000003 |
虚拟分区入口被同名文件占据 |
物理分区根目录有同名文件,阻碍了虚拟分区入口的创建。 |
4 |
VIRERR_NOTCLEAR |
0x10000004 |
物理分区非空 |
在对新物理分区应用虚拟分区时,分区非空。 |
5 |
VIRERR_NOTFIT |
0x10000005 |
物理分区非FAT32文件系统 |
物理分区是否为FAT32文件系统。 |
6 |
VIRERR_NOTMOUNT |
0x10000006 |
设备未挂载 |
执行格式化时设备未挂载。 |
7 |
VIRERR_INTER_ERR |
0x10000007 |
意外错误 |
应用虚拟分区时发生意外错误。 |
8 |
VIRERR_NOPARAM |
0x10000008 |
未对虚拟分区进行全局配置 |
在执行挂载/格式化操作之前,需要先调用调配接口。 |
9 |
VIRERR_PARMLOCKED |
0x10000009 |
设置已上锁 |
设置已上锁,不允许再次设置。 |
10 |
VIRERR_PARMNUMERR |
0x1000000A |
设置接口VirPartNum字段错误 |
检查VirPartNum的设定值是否正确。 |
11 |
VIRERR_PARMPERCENTERR |
0x1000000B |
设置接口VirPartPercent字段错误 |
检查VirPartPercent字段的值是否正确。 |
12 |
VIRERR_PARMNAMEERR |
0x1000000C |
设置接口VirPartName字段错误 |
检查VirPartName字段的设定是否正确。 |
13 |
VIRERR_PARMDEVERR |
0x1000000D |
设置接口DevPartPath字段错误 |
检查DevPartPath字段是否正确。 |
虚拟分区的兼容性
虚拟分区特性是LiteOS独有的基于FAT的文件管理模式。在其他平台上对已经应用过虚拟分区的物理分区进行操作时,务必了解以下兼容性特征,以免使用时发生问题。
重新对存储介质分区、调整存储介质的大小等操作,会直接导致物理分区的虚拟分区特性失效。
在其他平台上对已经应用虚拟分区的物理分区建立文件夹时,需要注意:
若建立的文件夹在虚拟分区入口外,且全部虚拟分区的百分比总和为100%,则下次在LiteOS中在该文件夹内不允许创建新的文件或者文件夹。
在其他平台上对已经应用虚拟分区的物理分区写文件时,需要注意:
若写入的文件在虚拟分区入口外,且全部虚拟分区的百分比总和为100%,则下次在LiteOS中这些文件只允许以只读方式访问。
若写入的文件在虚拟分区入口内,而且文件整体的簇链均在对应的虚拟分区的簇链管理范围内,则下次在LiteOS中这些文件会以正常权限访问。
若写入的文件在虚拟分区入口内,而且文件整体的簇链有一部分或者整体不在虚拟分区的管理范围内,则下次在LiteOS中这些文件只允许以只读权限访问。
在其他平台上删除虚拟分区入口目录,会导致对应的虚拟分区意外丢失。下次在LiteOS中进行挂载时,会尝试重建丢失的虚拟分区。
若删除后用户创建一个同名的文件夹,且文件夹的整体簇链均落在了对应的虚拟分区的管理范围内,则下次在LiteOS中进行挂载时,该虚拟分区入口将被正常识别。
若删除后用户创建一个同名的文件夹,且整体簇链一部分或者整体落在了对应虚拟分区的管辖范围外,则下次在LiteOS中进行挂载时,会导致虚拟分区入口校验失败,拒绝此次的虚拟分区应用。
在其他平台上重命名虚拟分区入口目录,会导致对应的虚拟分区意外丢失。下次在LiteOS中进行挂载时,会尝试将丢失的虚拟分区重建。原有的文件不会丢失,但这些文件将不会再受到虚拟分区特性的管理,只允许以只读权限访问。
开发流程
使用FAT功能,涉及以下几个步骤,其中对虚拟分区的操作为可选部分,只有在FAT使用虚拟分区功能的情况下才涉及,请根据实际业务情况选择(各步骤详细操作见下述分解):
打开菜单,进入FileSystem ---> Enable FAT菜单,完成FAT文件系统的配置。
配置项
含义
取值范围
默认值
依赖
LOSCFG_FS_FAT
使能FAT文件系统的开关
YES/NO
YES
LOSCFG_FS_VFS && LOSCFG_DRIVER_DISK
LOSCFG_FS_FAT_CACHE
使能FAT文件系统缓存
YES/NO
YES
LOSCFG_FS_FAT
LOSCFG_FS_FAT_CACHE_SYNC_THREAD
使能FAT文件系统缓存异步刷新
YES/NO
YES
LOSCFG_FS_FAT_CACHE
LOSCFG_FS_FAT_CACHE_PROC
使能FAT文件系统缓存统计
YES/NO
YES
LOSCFG_FS_FAT_CACHE
LOSCFG_FS_FAT_CACHE_INDEPENDENT_MEMORY_POOL
使能FAT文件系统缓存独立内存池
YES/NO
NO
LOSCFG_FS_FAT_CACHE
LOSCFG_FS_FAT_CHINESE
使能FAT文件系统支持中文
YES/NO
YES
LOSCFG_FS_FAT
LOSCFG_FS_FAT_CHINESE_GB2312
使用GB2312中文编码方式
YES/NO
YES
LOSCFG_FS_FAT_CHINESE
LOSCFG_FS_FAT_CHINESE_UTF_8
使用UTF-8中文编码方式
YES/NO
NO
LOSCFG_FS_FAT_CHINESE
LOSCFG_FS_FAT_DCACHE
使能FAT文件系统目录项缓存
YES/NO
YES
LOSCFG_FS_FAT
LOSCFG_FS_FAT_TRIM
使能FAT文件系统介质TRIM
YES/NO
YES
LOSCFG_FS_FAT
LOSCFG_FS_FAT_UNI_TRIM
使能FAT文件系统介质合并TRIM
YES/NO
YES
LOSCFG_FS_FAT
LOSCFG_FS_FAT_DIRECT_TRIM
使能FAT文件系统介质直接TRIM
YES/NO
NO
LOSCFG_FS_FAT
LOSCFG_FS_FAT_VIRTUAL_PARTITION
使能FAT文件系统支持虚拟分区
YES/NO
NO
LOSCFG_FS_FAT
LOSCFG_FS_FAT_FTL
使能FAT文件系统运行在flash上
YES/NO
NO
LOSCFG_FS_FAT &&
LOSCFG_DRIVERS_MTD
LOSCFG_FS_FAT_FTL_COMPRESS
使能FAT文件系统运行在flash上读写压缩数据。
YES/NO
NO
LOSCFG_FS_FAT_FTL
LOSCFG_FS_FAT_INDEPENDENT_MEMORY_POOL
使能FAT文件系统独立内存池
YES/NO
NO
LOSCFG_FS_FAT
LOSCFG_FS_FAT_STACK_LFN
LFN 512字节使用栈内存
YES/NO
YES
LOSCFG_FS_FAT
LOSCFG_FS_FAT_HEAP_LFN
LFN 512字节使用堆内存
YES/NO
NO
LOSCFG_FS_FAT
说明:当虚拟分区特性启用后,整个LiteOS的FAT文件系统将优先采用虚拟分区特性进行管理,虚拟分区不支持FAT目录项缓存。
当启用FAT目录项缓存后,会消耗一部分内存用来缓存FAT文件系统的目录项,从而减少部分接口的再次访问耗时。
当启用FAT Trim功能后,可以有效减缓磁盘的write amplification行为,提升数据写入速率的稳定性。
当启用FTL功能后,FAT文件系统可以在Nor Flash上使用,使用add_mtd_partition添加分区生成spinorblk0ftlp0。
当使用format格式化设备后,mount即可得到一个干净的FAT文件系统。
制作基于FTL的FAT文件系统镜像(可选)
FTL支持文件系统镜像的预制作,使用mkftl2image目录下的工具源码进行make编译,生成“mkfs.ftl”来进行制作,当修改压缩选项时,需重新生成“mkfs.ftl”。命令格式如下:mkfs.ftl [inDir] [partitionSize] [blockSize] [pageSize] [outFile]。
./mkfs.ftl appfs 0x400000 0x10000 0x200 appfs.ftl
参数 |
含义 |
|---|---|
inDir |
要制作成镜像的源目录 |
partitionSize |
分区大小 |
blockSize |
Flash器件的块大小 |
pageSize |
Flash器件的页大小 |
outFile |
输出的文件系统镜像文件 |
识别设备
在“open_source/FatFs/source/ffconf.h”文件中可以使能多分区和多设备。使能多分区后,可以操作设备上的多个分区,否则只能操作第一个分区。使能多设备后,可以将多个设备挂载成FAT文件系统。具体配置项为:
配置FF_MULTI_PARTITION为1,使能多分区功能。
配置FF_VOLUMES大于1时,使能多设备功能。
系统自动识别插入的SD卡,自动注册设备节点,如下所示。其中的mmcblk0和mmcblk1为卡0和卡1,是独立的主设备,mmcblk0p0和mmcblk0p1为卡0的两个分区,mmcblk1p0为卡1的分区,这些分区可作为分区设备使用。存在分区设备的情况下,禁止使用主设备。
LiteOS# ls
Directory /dev:
acodec 0
adec 0
aenc 0
ai 0
ao 0
console 0
fb0 0
hi_gpio 0
hi_mipi 0
hi_rtc 0
hi_tde 0
i2c-0 0
i2c-1 0
i2c-2 0
isp_dev 0
lcd 0
logmpp 0
mem 0
mmcblk0 0
mmcblk0p0 0
mmcblk0p1 0
mmcblk1 0
mmcblk1p0 0
可以在Shell中使用partinfo命令查看设备的分区信息:
LiteOS # partinfo /dev/sdap0
part info :
disk id : 3
part_id in system: 0
part no in disk : 0
part no in mbr : 1
part filesystem : 0C
part dev name : sdap0
part sec start : 2048
part sec count : 167794688
挂载FAT文件系统
调用mount()函数实现设备节点的挂载。
ret = mount("/dev/mmcblk0p0", "/bin/vs/sd", "vfat", 0, NULL);
if (ret) {
dprintf("mount fat filesystem err %d\n", ret);
}
说明: mount()函数有五个参数:
第一个参数表示设备节点。
第二个参数表示挂载点。
第三个参数表示文件系统类型。
第四个参数表示挂载标志,默认为0。
最后一个参数表示私有数据,默认为NULL。
除了在源码中调用mount()函数,也可以在Shell中使用mount命令实现挂载,前面3个参数同mount()函数,最后两个参数不需要设置。得到如下回应信息,表明挂载成功:
LiteOS# mount /dev/mmcblk0p0 /bin/vs/sd vfat
mount ok
卸载FAT文件系统
调用umount()函数卸载分区,只需要给出正确的挂载点即可。也可以在Shell中使用umount命令实现,得到如下回应信息,表明卸载成功:
LiteOS# umount /bin/vs/sd
umount ok
检查虚拟分区的可操作条件(可选)
对于未在LiteOS中应用过虚拟分区特性的存储设备(如SD卡、U盘、移动硬盘等)物理分区,需要保证:
该物理分区为FAT32分区。
该物理分区下无任何文件。
对于曾经在LiteOS中已经应用过虚拟分区特性的存储设备物理分区,需要保证:
该物理分区下的虚拟分区入口目录没有被删除、重命名或者其他形式的破坏。
该物理分区的保留扇区内数据完整。
若有任一条件不满足,则无法对该物理分区应用虚拟分区特性,只能应用常规FAT文件系统。
调配虚拟分区(可选)
虚拟分区特性针对的是某个特定场景下的物理分区,需要通过调配接口los_set_virpartparam()指定分区设备,并设置该分区的配置参数。调配虚拟分区参数接口的主要目的是允许用户建立符合场景的虚拟分区。
系统开机后,若想应用虚拟分区特性,需要先调用los_set_virpartparam(),对当前系统的虚拟分区数量、对应虚拟分区的关键参数进行配置。若未配置,则将导致后续虚拟分区应用失败。
对未在LiteOS中应用过虚拟分区特性的存储设备物理分区,在进行虚拟分区应用之前,可以通过调配接口设置需要的参数。正常情况下,当成功调配后,对该物理分区执行挂载操作,即可完成虚拟分区的应用。
若操作的物理分区已经应用过虚拟分区特性,只要该分区已设置的虚拟分区校验正确,则可忽略调配接口设置的参数,而无条件遵循该分区内部的参数对其管理。
挂载虚拟分区(可选)
在完成挂载FAT文件系统后会自动对已经挂载的分区进行虚拟分区的应用。若虚拟分区应用成功,则会启动虚拟分区对该物理分区进行管理;若虚拟分区应用失败,则会以普通FAT文件系统对该物理分区进行管理,并传出特定的错误码表明在应用虚拟分区时发生了何种错误。
虚拟分区成功挂载后,会在分区根目录下建立对应数量以及对应名称的目录,即作为虚拟分区入口。在虚拟分区入口内部创建文件,即相当于在对应的虚拟分区内创建文件。每个虚拟分区入口内的最大可用空间,即为设置的虚拟分区百分比大小。
卸载虚拟分区(可选)
卸载虚拟分区可以认为是挂载的逆过程,不再进行重复。
创建虚拟分区(可选)
当对一个新物理分区应用虚拟分区时,执行挂载操作即可完成虚拟分区的创建,并且可以在LiteOS中进行重复使用。创建的虚拟分区的基本参数,以调配接口所设置的参数为准。
某些情况下,需要对某个分区重新应用虚拟分区时,可在已经挂载的分区上格式化该磁盘为FAT32文件系统。在格式化操作完成后,则会对该物理分区重新应用虚拟分区。
须知: 执行格式化操作会导致用户数据丢失,在执行此操作时请小心。
删除虚拟分区(可选)
当某些情况下,需要撤销对物理分区使用虚拟分区特性,可以在未挂载的分区上进行格式化,即可清除该分区上的虚拟分区信息。
须知: 执行格式化操作会导致用户数据丢失,在执行此操作时请小心。
注意事项¶
FAT文件系统中,单个文件不能大于4GB。
带绝对路径的文件名,除去挂载点之后的长度总和不能大于252Byte。
目前只支持FAT32格式,如果使用其他文件格式,需要先将介质格式化为FAT32再对接到LiteOS。
FAT文件系统支持的操作有:open,close,read,write,seek,sync,opendir,closedir,readdir,rewinddir,statfs,virstatfs,unlink,mkdir,rmdir,rename,stat,lstat,stat64,utime,chattr,seek64,getlabel,set_label,format,fallocate,fallocate64,truncate,truncate64,fscheck。
以可写方式打开一个文件后,未close前再次打开会失败。同时打开同一文件,必须全部以只读方式打开。长时间打开一个文件,没有close时数据会丢失,必须close才能保存。
open打开一个文件,参数有O_TRUNC时,会将文件中的内容清空。
若open未使用mode入参,则无法通过mode入参来控制文件权限。
目前FAT文件系统不建议open带O_DIRECTORY属性的操作,打开目录请使用opendir()。
FAT文件系统的读写指针没有分离,所以以O_APPEND(追加写)方式打开文件后,读指针也在文件尾,读文件前需要用户手动置位。
FAT文件系统的stat及lstat函数获取的文件时间只是文件的修改时间,暂不支持获取文件创建时间和最后访问时间。微软FAT协议不支持1980年以前的时间。
在format操作之前,若FAT文件系统已挂载,需确保所有目录及文件已经全部关闭,否则format会失败。
FAT支持挂载只读属性:
当mount函数的入参为MS_RDONLY时,FAT将开启只读属性,所有带写入操作的接口(format接口除外),如write、mkdir、unlink,以及通过非O_RDONLY属性打开的文件,将均被拒绝,并传出EACCESS错误码。
当mount函数的入参为MS_NOSYNC时,FAT不会主动将Cache的内容写回存储器件。FAT的如下接口(open、close、 unlink、rename、mkdir、rmdir、chattr、truncate)不会自动进行sync操作,虽然可以提升速度,但是需要上层主动调用sync来同步数据,否则下电可能会导致数据丢失。
当前Cache的默认大小为16个块,每个块256个扇区。可以在“fs/include/vfs_config.h”文件中修改Cache的块数量CONFIG_FS_FAT_BLOCK_NUMS 和每个块的扇区数CONFIG_FS_FAT_SECTOR_PER_BLOCK,注意CONFIG_FS_FAT_SECTOR_PER_BLOCK必须为32的倍数,且不能大于2048。
当有两张SD卡插槽时,卡0和卡1不固定,先插入的为卡0,后插入的为卡1。
为避免SD卡使用异常和内存泄漏,在SD卡使用过程中拔卡,必须先关闭正处于打开状态的文件和目录,然后umount挂载节点。
YAFFS2¶
概述¶
基本概念
YAFFS是Yet Another Flash File System的简称,是一种开源的、针对Nand Flash的嵌入式文件系统。在YAFFS中,最小的存储单位为page。
目前YAFFS文件系统有YAFFS和YAFFS2两个版本,主要区别在于page读写size的大小,YAFFS2可支持到2K per page,远高于YAFFS的512 bytes,因此YAFFS2能够更好地支持大容量的Nand Flash芯片。此外YAFFS2还有64bytes的SPARE区域,用于存储坏块信息、ECC校验等。
YAFFS2专为Nand Flash设计,适用于大容量的存储设备,同时也使得Nand Flash具有高效性和健壮性。
YAFFS2实现对2K per page的读写支持,同时在内存空间占用、垃圾回收速度、读写速度等方面均有大幅提升。
YAFFS2为文件系统提供了损耗平衡和掉电保护,保证在修改文件系统中的数据时,即使发生意外也不损坏数据。
LiteOS的YAFFS2文件系统支持多分区,采用双向链表结构实现。
开发指导¶
接口说明
头文件 |
接口名 |
描述 |
|---|---|---|
fs/include/mtd_partition.h |
add_mtd_partition |
添加YAFFS2分区,该函数会自动为设备节点命名,对于YAFFS2,其命名规则是“/dev/nandblk”加上分区号。 |
delete_mtd_partition |
删除已经卸载的分区。 |
说明:
add_mtd_partition添加YAFFS2分区时,系统会自动对起始地址和分区大小根据设备block大小进行对齐。该函数有四个参数:
第一个参数表示介质,支持“nand”和“spinor”。YAFFS2分区在“nand”上使用,littlefs在“spinor”上使用。
第二个参数表示分区的起始地址,以16进制的形式传入。
第三个参数表示分区大小,以16进制的形式传入。
最后一个参数表示分区号,有效值为0~19。
delete_mtd_partition函数有两个参数:
第一个参数是分区号。
第二个参数为介质类型,该函数与add_mtd_partition()函数对应。
开发流程
使用LiteOS的YAFFS2文件系统,涉及以下几个步骤(各步骤详细操作见下述分解):
打开菜单,选择FileSystem ---> Enable YAFFS2,使能YAFFS2文件系统。
配置项
含义
取值范围
默认值
依赖
LOSCFG_FS_YAFFS
使能YAFFS2的开关
YES/NO
YES
LOSCFG_FS_VFS && LOSCFG_DRIVERS_MTD_NAND
制作YAFFS2文件系统镜像并烧录(可选)
目前支持使用mkyaffs2image工具制作文件系统镜像,可以参考如下命令制作镜像:
./mkyaffs2image rootfs rootfs.yaffs2 1 2
参数 |
含义 |
|---|---|
rootfs |
要制作成镜像的源目录 |
rootfs.yaffs2 |
镜像名称 |
1 |
Nand Flash器件的页大小,1表示2KB pagesize |
2 |
Nand Flash器件的ECC校验类型,2表示4bit/512 |
说明: 用户可根据实际情况修改命令中参数值。
制作好镜像后,就可以烧录此镜像了。
调用add_mtd_partition创建分区
创建分区的示例代码如下:
if (ret = add_mtd_partition("nand", 0x900000, 0x200000, 0) < 0) {
dprintf("add yaffs partition 0 failed, return %d\n", ret);
}
if (ret = add_mtd_partition("nand", 0xb00000, 0x200000, 1) < 0) {
dprintf("add yaffs partition 1 failed, return %d\n", ret);
}
创建成功后,在Shell中可以使用“partition nand”命令查看Nand Flash的分区信息:
LiteOS# partition nand
nand partition num:0, dev name:/dev/nandblk0, mountpt:(null), startaddr:0x0900000,length:0x0200000
nand partition num:1, dev name:/dev/nandblk1, mountpt:(null), startaddr:0x0b00000,length:0x0200000
调用mount函数挂载分区
调用mount()函数实现设备节点的挂载。
ret = mount("/dev/nandblk0", "/yaffs0", "yaffs", 0, NULL);
if (ret) {
dprintf("mount yaffs err %d\n", ret);
}
说明: mount()函数有五个参数:
第一个参数表示设备节点,这个参数需要和add_mtd_partition()函数对应起来。
第二个参数表示挂载点。
第三个参数表示文件系统类型。
第四个参数表示挂载标志,默认为0。
最后一个参数表示私有数据,默认为NULL。
除了在源码中调用mount()函数,也可以在Shell中使用mount命令实现挂载,前面3个参数同mount()函数,最后两个参数不需要给出,回显“mount ok”表明挂载成功:
LiteOS# mount /dev/nandblk1 /yaffs1 yaffs
mount ok
挂载成功后,在Shell中可以使用partition nand命令看到设备节点的挂载点信息:
LiteOS# partition nand
nand partition num:0, dev name:/dev/nandblk0, mountpt:/yaffs0, startaddr:0x0900000,length:0x0200000
nand partition num:1, dev name:/dev/nandblk1, mountpt:/yaffs1, startaddr:0x0b00000,length:0x0200000
调用umount函数卸载分区
调用umount()函数卸载分区,只需要正确给出挂载点即可。这一操作也可以在Shell中使用umount命令实现,回显“umount ok”表明命令执行成功:
LiteOS# umount /yaffs1
umount ok
LiteOS# umount /yaffs0
umount ok
卸载成功后,在Shell中可以使用“partition nand”命令看到设备节点的挂载点变为空。
LiteOS# partition nand
nand partition num:0, dev name:/dev/nandblk0, mountpt:(null), startaddr:0x0900000,length:0x0200000
nand partition num:1, dev name:/dev/nandblk1, mountpt:(null), startaddr:0x0b00000,length:0x0200000
调用delete_mtd_partition函数删除分区
调用delete_mtd_partition删除分区前要卸载分区。
ret = delete_mtd_partition(1, "nand");
if (ret != 0) {
printf("delte yaffs error\n");
} else {
printf("delete yaffs ok\n");
}
注意事项¶
对于YAFFS2的多分区特性,分区起始地址可以灵活配置,但分区间地址不能重叠,用户需要根据Flash使用情况,合理配置空间,防止与其它文件系统或其它不适合挂载YAFFS2文件系统的空间发生冲突。
最小可添加的分区为一个block大小,但是最小可挂载的分区为9个block大小(由YAFFS2特性决定),要注意区分这两个概念。
建议分区使用前先进行擦除操作。
LiteOS的YAFFS2文件系统只允许连续打开20个目录。
YAFFS2文件系统支持的操作有:open,close,read,write,seek,sync,dup,opendir,closedir,readdir,rewinddir,statfs,unlink,mkdir,rmdir,rename,stat,stat64,seek64。
open打开一个文件,参数有O_TRUNC时,必须同时拥有写的权限,才会将文件中的内容清空。
YAFFS2当前不支持获取文件的atime、mtime、ctime,使用stat接口获取出来的值为0。
目前YAFFS2不建议open带 O_DIRECTORY属性的操作,打开目录请使用opendir()。
YAFFS2支持挂载只读属性,当mount函数的入参为MS_RDONLY时,YAFFS2将开启只读属性,所有带写操作的接口,如write、mkdir、unlink,以及通过非O_RDONLY属性打开的文件,将均被拒绝,并传出EACCES错误码。
open打开一个文件,o_flags必须带有O_RDONLY、O_WRONLY、O_RDWR中的一种属性,o_flags设置的读写权限必须与创建文件时设置的mode匹配。
LITTLEFS¶
概述¶
基本概念
LITTLEFS是LITTLE File System(微型文件系统)的缩写,是一种专门为微控制器设计的故障保护文件系统,主要应用于NOR Flash闪存,其特点包括:
可读写。
提供损耗平衡。
提供掉电安全保护,在修改文件系统中的数据时,即使发生意外也不损坏数据。
内存和代码很精简,占用很少的RAM和FLASH。
LITTLEFS是基于块的文件系统,磁盘被分成大小均匀的块阵列,作为存储的逻辑单元,这些块在使用时由公共块分配器进行分配。LITTLEFS使用小日志存储元数据,并使用较大的写时复制(COW)结构存储文件数据。
LiteOS的LITTLEFS主要应用于NOR Flash闪存的文件管理,且支持多分区。
开发指导¶
接口说明
头文件 |
接口名 |
描述 |
|---|---|---|
fs/include/mtd_partition.h |
add_mtd_partition |
添加文件系统分区,该函数会自动为设备节点命名,对于LITTLEFS,其命名规则是“/dev/spinorblk0”。 |
delete_mtd_partition |
删除已经卸载的分区。 |
|
open_source/musl/include/sys/mount.h |
mount |
挂载文件系统。 |
umount |
卸载文件系统。 |
|
umount2 |
强制卸载文件系统,部分场景下存在内存泄露,需要重启后继续使用文件系统。 |
开发流程
使能LITTLEFS
打开菜单,选择FileSystem ---> Enable LITTLEFS,使能LITTLEFS文件系统。
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_FS_LITTLEFS |
使能LITTLEFS的开关 |
YES/NO |
YES |
LOSCFG_FS_VFS && LOSCFG_DRIVERS_MTD_SPI_NOR |
添加LITTLEFS分区
调用add_mtd_partition()函数添加LITTLEFS分区。
if (add_mtd_partition("spinor", 0x100000, 0x500000, 0) != 0) {
dprintf("add spinor partition failed\n");
}
说明: add_mtd_partition()添加LITTLEFS分区时,系统会自动根据设备block大小将分区起始地址和大小对齐。 该函数有四个参数:
第一个参数表示介质,支持“nand”和“spinor”。LITTLEFS分区在“spinor”上使用。
第二个参数表示分区的起始地址,以16进制的形式传入。
第三个参数表示分区大小,以16进制的形式传入。
最后一个参数表示分区号,有效值为0~19。
成功后,可以在Shell中使用“partition spinor”命令查看spinor flash分区信息,更多请详见“partition”。
LiteOS# partition spinor
spinor partition num:0, dev name:/dev/spinorblk0, mountpt:(null), startaddr:0x0100000, length:0x0500000
挂载LITTLEFS分区
调用mount()函数实现设备节点的挂载。
ret = mount("/dev/spinorblk0", "/littlefs0", "littlefs", 0, NULL);
if (ret != 0) {
dprintf("ERROR:mount littlefs failed\n");
}
说明: mount()函数有五个参数:
第一个参数表示设备节点,这个参数须与add_mtd_partition()函数对应。
第二个参数表示挂载点。
第三个参数表示文件系统类型。
第四个参数表示挂载标志,默认为0。
最后一个参数表示私有数据,默认为NULL。
调用mount()函数是否成功,可以在Shell中执行“ls”查看当前根目录下的目录。
LiteOS# ls
Directory /:
bin <DIR>
dev <DIR>
littlefs0 <DIR>
挂载成功后,可以对NOR Flash进行读写操作。
卸载LITTLEFS分区
调用umount()函数卸载分区,只需要给出正确的挂载点即可。
ret = umount("/littlefs0");
if (ret != 0) {
dprintf("umount littlefs err\n");
}
说明: umount()函数只有一个“表示挂载点”的参数。
删除LITTLEFS分区
调用delete_mtd_partition()删除已经卸载的分区。
delete_mtd_partition(0, "spinor");
说明: delete_mtd_partition()函数有两个参数:
第一个参数是分区号。
第二个参数为介质类型,该参数与add_mtd_partition()函数对应。
注意事项¶
如果以O_TRUNC方式open一个文件时,会将文件中的内容清空。
若open未使用mode入参,则无法通过mode入参来控制文件权限。
LITTLEFS文件系统的读写指针分离,以O_APPEND(追加写)方式打开文件后,读指针在文件头,写指针在文件尾。
LITTLEFS文件系统用于NOR Flash,最终调用NOR Flash驱动接口,因此使用LITTLEFS文件系统之前需要保证存在NOR Flash闪存,且驱动已初始化成功(spinor_init()返回0)。
目前LITTLEFS文件系统支持的操作有:open,close,read,write,seek,opendir,closedir,readdir,rewinddir,unlink,mkdir,rmdir,rename,stat,stat64,seek64。
当前LITTLEFS读写cache的默认大小为1024Byte,可以在“fs/littlefs/include/vfs_littlefs.h”文件中修改LFS_CACHE_SIZE的大小,注意必须为LFS_MIN_CACHE_SIZE的倍数,且不能大于65536Byte。
打开文件并写入数据,若在未close的情况下rename,数据会丢失。
目前access操作只能判断文件是否存在,不能判断文件是否具有指定的权限。
目前stat操作只能获取文件名、大小、文件类型(文件或目录),无法获取文件权限。
目前LITTLEFS不建议open带O_DIRECTORY属性的操作,打开目录请使用opendir()。
若同时打开同一文件,必须全部以只读方式打开。若以可写方式同时打开一个文件并写入数据,必须close才能保存,且只保存最后一次close的数据。
LITTLEFS支持挂载只读属性,当mount函数的入参mountflags为MS_RDONLY时,LITTLEFS将开启只读属性,所有带有写操作的接口,如write、mkdir、unlink,以及通过非O_RDONLY属性打开的文件,均将被拒绝,并传出EACCESS错误码。
兼容接口¶
概述¶
图 1 兼容接口示意图

LiteOS提供了一套自有OS接口,同时也支持musl libc库、C++标准库、CMSIS接口和Linux接口。上层APP建议调用musl libc库等兼容接口,不建议直接调用LiteOS的自有OS接口。下面章节将列出LiteOS支持的兼容接口。
须知: LitesOS适配的libc库函数,遵循开源musl libc的实现形式,musl开源代码遵循标准C规范ISO/IEC 9899:1999和POSIX标准1003.1™-2008:作为最简单的底层库函数,内部不对指针类型参数做非空判断,由使用者保证入参的合法性。 其中LiteOS实现的获取时间类接口都是自系统起调度算起,与接口标准定义不一致,使用时需特别注意。
POSIX接口¶
POSIX支持接口¶
LiteOS提供一套POSIX适配接口,具体的规格参见下表。
头文件 |
接口名 |
类型 |
说明 |
|---|---|---|---|
ctype.h |
isalnum_l |
数据判断 |
判断入参是否为字母或数字 |
ctype.h |
isalpha_l |
数据判断 |
判断入参是否为字母 |
ctype.h |
isascii |
数据判断 |
判断入参是否为ascii码 |
ctype.h |
isblank |
数据判断 |
判断入参是否为空格或tab |
ctype.h |
isblank_l |
数据判断 |
判断入参是否为空格或tab |
ctype.h |
iscntrl_l |
数据判断 |
判断入参是否为控制字符 |
ctype.h |
isdigit_l |
数据判断 |
判断入参是否为十进制数 |
ctype.h |
isgraph_l |
数据判断 |
判断入参是否有图形表示 |
ctype.h |
islower_l |
数据判断 |
判断入参是否为小写字母 |
ctype.h |
isprint_l |
数据判断 |
判断入参是否可以打印 |
ctype.h |
ispunct_l |
数据判断 |
判断入参是否为标点符号 |
ctype.h |
isspace_l |
数据判断 |
判断入参是否为空格 |
ctype.h |
isupper_l |
数据判断 |
判断入参是否为大写字母 |
ctype.h |
isxdigit_l |
数据判断 |
判断入参是否为十六进制数 |
ctype.h |
toascii |
数据转换函数 |
将字符转换成ascii码 |
ctype.h |
tolower_l |
数据转换函数 |
将大写字母转换成小写字母 |
ctype.h |
toupper_l |
数据转换函数 |
将小写字母转换成大写字母 |
dirent.h |
alphasort |
目录操作 |
依字母顺序排序目录结构 |
dirent.h |
closedir |
目录操作 |
关闭目录 |
dirent.h |
opendir |
目录操作 |
打开目录 |
dirent.h |
readdir |
目录操作 |
读取目录 |
dirent.h |
rewinddir |
目录操作 |
重设读取目录的位置为开头位置 |
dirent.h |
scandir |
目录操作 |
扫描目录完成条件过滤 |
dirent.h |
seekdir |
目录操作 |
设置下回读取目录的位置 |
dirent.h |
telldir |
目录操作 |
取得目录流的读取位置 |
dlfcn.h |
dlclose |
动态加载 |
卸载一个模块 |
dlfcn.h |
dlopen |
动态加载 |
动态加载一个so模块 |
dlfcn.h |
dlsym |
动态加载 |
在模块或者系统符号表中查找符号 |
fcntl.h |
creat |
文件操作函数 |
创建一个文件 |
fcntl.h |
fcntl |
文件操作函数 |
文件描述词操作 |
fcntl.h |
open |
文件操作函数 |
打开文件 |
langinfo.h |
nl_langinfo |
系统函数 |
返回指定的本地信息 |
langinfo.h |
nl_langinfo_l |
系统函数 |
返回指定的本地信息 |
libgen.h |
basename |
文件路径 |
返回文件名 |
mqueue.h |
mq_close |
消息队列 |
关闭消息队列 |
mqueue.h |
mq_getattr |
消息队列 |
获取消息队列属性 |
mqueue.h |
mq_open |
消息队列 |
打开消息队列 |
mqueue.h |
mq_receive |
消息队列 |
接受一个消息队列中的消息 |
mqueue.h |
mq_send |
消息队列 |
发送一个消息到消息队列 |
mqueue.h |
mq_setattr |
消息队列 |
设置消息队列属性 |
mqueue.h |
mq_timedreceive |
消息队列 |
定时接收消息 |
mqueue.h |
mq_timedsend |
消息队列 |
定时发送消息 |
mqueue.h |
mq_unlink |
消息队列 |
移除消息队列 |
net/if.h |
if_freenameindex |
网络函数 |
通过if_nameindex()获取完毕接口名称与索引后,调用该函数以释放动态分配的内存区域 |
net/if.h |
if_indextoname |
数据转换函数 |
将网卡序号转为网卡名 |
net/if.h |
if_nameindex |
网络函数 |
返回动态分配的struct if_nameindex结构数组,数组中的每一个元素分别对应一个本地网络接口 |
net/if.h |
if_nametoindex |
数据转换函数 |
将网卡名转为网卡序号 |
netdb.h |
freeaddrinfo |
网络函数 |
释放getaddrinfo申请的内存空间 |
netdb.h |
getaddrinfo |
网络函数 |
将主机名和服务名转换到socket地址 |
netdb.h |
getnameinfo |
网络函数 |
将socket地址转换到主机名和服务名 |
poll.h |
poll |
文件操作函数 |
监视文件描述符 |
pthread.h |
pthread_attr_destroy |
pthread |
删除线程的属性 |
pthread.h |
pthread_attr_getdetachstate |
pthread |
获取脱离状态的属性 |
pthread.h |
pthread_attr_getinheritsched |
pthread |
获取任务调度方式 |
pthread.h |
pthread_attr_getschedparam |
pthread |
获取任务调度参数 |
pthread.h |
pthread_attr_getschedpolicy |
pthread |
获取任务调度策略属性,目前仅支持SCHED_RR调度策略,不支持SCHED_OTHER、SCHED_FIFO |
pthread.h |
pthread_attr_getscope |
pthread |
获取任务范围属性,任务使用范围目前只支持PTHREAD_SCOPE_SYSTEM,不支持PTHREAD_SCOPE_PROCESS |
pthread.h |
pthread_attr_getstackaddr |
pthread |
获取任务堆栈的起始地址 |
pthread.h |
pthread_attr_getstacksize |
pthread |
获取任务属性堆栈大小 |
pthread.h |
pthread_attr_init |
pthread |
初始化任务属性 |
pthread.h |
pthread_attr_setdetachstate |
pthread |
设置任务属性分离状态 |
pthread.h |
pthread_attr_setinheritsched |
pthread |
设置任务调度方式 |
pthread.h |
pthread_attr_setschedparam |
pthread |
设置任务调度参数,注意在LiteOS中,任务优先级的值越小,任务在系统中的优先级就越高,与标准库函数相反,且优先级仅支持0~31。注意:需要将pthread_attr_t任务属性的inheritsched字段设置为PTHREAD_EXPLICIT_SCHED,否则设置的任务调度优先级将不会生效,系统默认设置为PTHREAD_INHERIT_SCHED |
pthread.h |
pthread_attr_setschedpolicy |
pthread |
设置任务调度策略属性,目前仅支持SCHED_RR调度策略,不支持SCHED_OTHER、SCHED_FIFO |
pthread.h |
pthread_attr_setscope |
pthread |
设置任务范围属性,任务使用范围目前只支持PTHREAD_SCOPE_SYSTEM,不支持PTHREAD_SCOPE_PROCESS |
pthread.h |
pthread_attr_setstackaddr |
pthread |
设置任务堆栈的起始地址 |
pthread.h |
pthread_attr_setstacksize |
pthread |
设置任务属性堆栈大小 |
pthread.h |
pthread_cancel |
pthread |
取消任务,仅支持先设置PTHREAD_CANCEL_ASYNCHRONOUS状态,再调用pthread_cancel取消任务 |
pthread.h |
pthread_cond_broadcast |
pthread |
唤醒所有被阻塞在条件变量上的线程 |
pthread.h |
pthread_cond_destroy |
pthread |
释放条件变量 |
pthread.h |
pthread_cond_init |
pthread |
初始化条件变量 |
pthread.h |
pthread_cond_signal |
pthread |
释放被阻塞在条件变量上的一个线程 |
pthread.h |
pthread_cond_timedwait |
pthread |
超时时限内等待一个条件变量,当超时等待时间为相对时间,LiteOS不能处理早已超时的情况 |
pthread.h |
pthread_cond_wait |
pthread |
等待一个条件变量 |
pthread.h |
pthread_condattr_destroy |
pthread |
删除存储并使属性对象无效 |
pthread.h |
pthread_condattr_getclock |
pthread |
获取任务时钟 |
pthread.h |
pthread_condattr_getpshared |
pthread |
获取条件变量属性,目前只支持获取PTHREAD_PROCESS_PRIVATE条件变量属性 |
pthread.h |
pthread_condattr_init |
pthread |
初始化条件变量属性 |
pthread.h |
pthread_condattr_setclock |
pthread |
设置任务时钟,只支持CLOCK_MONOTONIC或CLOCK_REALTIME模式 |
pthread.h |
pthread_condattr_setpshared |
pthread |
设置条件变量属性,只支持PTHREAD_PROCESS_PRIVATE属性 |
pthread.h |
pthread_create |
pthread |
创建任务 |
pthread.h |
pthread_detach |
pthread |
分离任务 |
pthread.h |
pthread_equal |
pthread |
判断是否为同一任务 |
pthread.h |
pthread_exit |
pthread |
任务退出 |
pthread.h |
pthread_getschedparam |
pthread |
获取任务优先级及调度参数,目前仅支持SCHED_RR调度策略,不支持SCHED_OTHER、SCHED_FIFO |
pthread.h |
pthread_getspecific |
pthread |
获取调用线程的键绑定 |
pthread.h |
pthread_join |
pthread |
阻塞任务 |
pthread.h |
pthread_key_create |
pthread |
分配用于标识进程中线程特定数据的键 |
pthread.h |
pthread_key_delete |
pthread |
销毁现有线程特定数据键 |
pthread.h |
pthread_mutex_destroy |
pthread |
删除互斥锁 |
pthread.h |
pthread_mutex_getprioceiling |
pthread |
获取互斥锁的优先级上限 |
pthread.h |
pthread_mutex_init |
pthread |
初始化互斥锁 |
pthread.h |
pthread_mutex_lock |
pthread |
申请互斥锁(阻塞操作) |
pthread.h |
pthread_mutex_setprioceiling |
pthread |
设置互斥锁的优先级上限 |
pthread.h |
pthread_mutex_timedlock |
pthread |
申请互斥锁(只在设定时间内阻塞) |
pthread.h |
pthread_mutex_trylock |
pthread |
尝试申请互斥锁(非阻塞) |
pthread.h |
pthread_mutex_unlock |
pthread |
释放互斥锁 |
pthread.h |
pthread_mutexattr_destroy |
pthread |
销毁互斥锁属性对象 |
pthread.h |
pthread_mutexattr_getprioceiling |
pthread |
获取互斥锁属性的优先级上限 |
pthread.h |
pthread_mutexattr_getprotocol |
pthread |
获取互斥锁属性的协议属性 |
pthread.h |
pthread_mutexattr_gettype |
pthread |
获取互斥锁的类型属性 |
pthread.h |
pthread_mutexattr_init |
pthread |
初始化互斥锁属性对象 |
pthread.h |
pthread_mutexattr_setprioceiling |
pthread |
设置互斥锁属性的优先级上限 |
pthread.h |
pthread_mutexattr_setprotocol |
pthread |
设置互斥锁属性的协议属性 |
pthread.h |
pthread_mutexattr_settype |
pthread |
设置互斥锁的类型属性 |
pthread.h |
pthread_once |
pthread |
一次性操作任务 |
pthread.h |
pthread_self |
pthread |
获取任务ID |
pthread.h |
pthread_setcancelstate |
pthread |
任务cancel功能开关 |
pthread.h |
pthread_setcanceltype |
pthread |
设置任务cancel类型 |
pthread.h |
pthread_setschedparam |
pthread |
设置任务优先级及调度策略,目前仅支持SCHED_RR调度策略,不支持SCHED_OTHER和SCHED_FIFO,任务优先级仅支持0~31 |
pthread.h |
pthread_setschedprio |
pthread |
设置任务优先级,任务优先级仅支持0~31 |
pthread.h |
pthread_setspecific |
pthread |
设置线程数据 |
pthread.h |
pthread_testcancel |
pthread |
cancel任务 |
sched.h |
sched_get_priority_max |
调度函数 |
获取系统支持的最大的优先级值 |
sched.h |
sched_get_priority_min |
调度函数 |
获取系统支持的最小的优先级值 |
sched.h |
sched_yield |
调度函数 |
使当前线程放弃占用CPU |
semaphore.h |
sem_destroy |
信号量 |
销毁无名信号量 |
semaphore.h |
sem_getvalue |
信号量 |
获取指定信号量的值 |
semaphore.h |
sem_init |
信号量 |
初始化无名信号量 |
semaphore.h |
sem_post |
信号量 |
释放一个指定的无名信号量 |
semaphore.h |
sem_timedwait |
信号量 |
申请一个超时等待的无名信号量,当超时等待时间为相对时间时,LiteOS不能处理早已超时的情况 |
semaphore.h |
sem_trywait |
信号量 |
尝试申请一个无名信号量 |
semaphore.h |
sem_wait |
信号量 |
申请等待一个无名信号量 |
stdio.h |
dprintf |
标准IO操作 |
打印函数 |
stdio.h |
fdopen |
文件操作函数 |
取一个现存的文件描述符,并使一个标准的I/O流与该描述符相结合 |
stdio.h |
fileno |
文件操作函数 |
取得参数stream指定的文件流所使用的文件描述符,如果文件流已经关闭,则返回-1,并设置错误码 |
stdio.h |
fseeko |
文件操作函数 |
移动文件流的读写位置 |
stdio.h |
ftello |
文件操作函数 |
获取文件流的读取位置 |
stdlib.h |
initstate |
随机数函数 |
初始化随机数生成状态 |
stdlib.h |
lrand48 |
随机数函数 |
取得一个正的长整型的随机数 |
stdlib.h |
mkstemp |
文件处理函数 |
建立唯一的临时文件 |
stdlib.h |
nrand48 |
随机数函数 |
产生正随机数 |
stdlib.h |
posix_memalign |
内存操作 |
对齐申请内存,返回内存指针 |
stdlib.h |
random |
随机数函数 |
产生0~RAND_MAX范围内的随机数 |
stdlib.h |
realpath |
文件处理函数 |
相对路径转换成绝对路径,入参resolved_path的长度必须不小于PATH_MAX |
stdlib.h |
seed48 |
随机数函数 |
设置48位随机数种子 |
stdlib.h |
setstate |
随机数函数 |
设置随机数状态 |
stdlib.h |
srand48 |
随机数函数 |
设置48位随机数种子 |
stdlib.h |
srandom |
随机数函数 |
产生随机数种子 |
string.h |
stpcpy |
字符串处理函数 |
赋值字符串到数组 |
string.h |
stpncpy |
字符串处理函数 |
赋值字符串到数组 |
string.h |
strcoll_l |
字符串处理函数 |
根据 LC_COLLATE 比较字符串 |
string.h |
strdup |
字符串处理函数 |
拷贝字符串到新申请内存中 |
string.h |
strerror_l |
字符串处理函数 |
返回一个指向错误消息字符串的指针 |
string.h |
strerror_r |
字符串处理函数 |
返回一个指向错误消息字符串的指针(可重入) |
string.h |
strnlen |
字符串处理函数 |
返回字符串长度 |
string.h |
strtok_r |
字符串处理函数 |
分割字符串 |
string.h |
strxfrm_l |
字符串处理函数 |
根据LC_COLLATE来转换字符串src,若传入n的长度小于字符串src的长度,则不作转换,直接返回字符串src的长度 |
strings.h |
ffs |
位操作函数 |
查找一个整数中的第一个置位值 |
strings.h |
strcasecmp |
字符串处理函数 |
字符串比较 |
strings.h |
strcasecmp_l |
字符串处理函数 |
字符串比较 |
strings.h |
strncasecmp |
字符串处理函数 |
字符串比较 |
strings.h |
strncasecmp_l |
字符串处理函数 |
字符串比较 |
sys/ioctl.h |
ioctl |
文件操作函数 |
执行指定操作 |
sys/select.h |
select |
文件操作函数 |
监视文件描述符,用户需要保证待监测的文件描述符为小于FD_SETSIZE的正值,否则可能产生不确定的行为 |
sys/stat.h |
fstat |
文件操作接口 |
获取文件属性 |
sys/stat.h |
lstat |
文件操作接口 |
获取文件属性 |
sys/stat.h |
mkdir |
目录操作接口 |
创建目录 |
sys/stat.h |
stat |
文件操作接口 |
获取文件属性 |
sys/time.h |
gettimeofday |
时间函数 |
获取当前时间,以秒和微秒的格式返回 |
sys/times.h |
times |
时间函数 |
获取cpu tick到buff中 |
sys/uio.h |
readv |
文件操作函数 |
读文件 |
sys/uio.h |
writev |
文件操作函数 |
写文件 |
sys/utsname.h |
uname |
系统函数 |
获取系统信息,可配置LOSCFG_LIB_VENDORNAME选项改变返回值的nodename成员,不返回硬件标识符和域名信息 |
syslog.h |
syslog |
系统函数 |
输出系统日志,不支持第一个入参按等级输出日志 |
time.h |
asctime_r |
时间函数 |
将时间和日期以字符串形式表示 |
time.h |
clock_getres |
时间函数 |
获取对应时钟类型能够提供的时间精确度 |
time.h |
clock_gettime |
时间函数 |
获取指定时钟的时间 |
time.h |
clock_settime |
时间函数 |
设置指定时钟的时间 |
time.h |
clock_nanosleep |
时间函数 |
具有可指定时钟的高分辨率睡眠(但是目前只支持CLOCK_REALTIME) |
time.h |
ctime_r |
时间函数 |
将时间和日期以字符串形式表示 |
time.h |
gmtime_r |
时间函数 |
取得目前的时间和日期 |
time.h |
localtime_r |
时间函数 |
取得当地目前的时间和日期 |
time.h |
nanosleep |
时间函数 |
进程以纳秒为单位休眠,但目前只支持tick(默认10ms)休眠,第二个参数不支持,且传参所设置的秒数不能大于4292秒 |
time.h |
strftime_l |
时间函数 |
格式化日期和时间,格式化字符不支持%z或%Z的格式化转换 |
time.h |
strptime |
时间函数 |
按照特定时间格式将字符串转换为时间类型 |
time.h |
timer_create |
时间函数 |
创建定时器,只支持SIGEV_THREAD在线程内处理(LiteOS有两种实现方式,在线程或者在中断中处理timer的回调函数,具体采用哪种方式由宏开关LOSCFG_BASE_CORE_SWTMR_IN_ISR控制);clock_id只支持CLOCK_REALTIME |
time.h |
timer_delete |
时间函数 |
删除定时器 |
time.h |
timer_getoverrun |
时间函数 |
获取定时器超时次数 |
time.h |
timer_gettime |
时间函数 |
获得一个定时器剩余时间 |
time.h |
timer_settime |
时间函数 |
初始化或者撤销定时器,it_interval参数为0时,定时器使用完毕同样需要手动删除 |
unistd.h |
access |
文件操作函数 |
判断文件权限 |
unistd.h |
chdir |
目录操作接口 |
改变当前工作目录 |
unistd.h |
close |
文件操作接口 |
关闭文件 |
unistd.h |
dup |
文件操作函数 |
复制文件 |
unistd.h |
dup2 |
文件操作函数 |
复制文件 |
unistd.h |
fsync |
文件操作函数 |
文件同步 |
unistd.h |
ftruncate |
文件操作函数 |
改变文件大小 |
unistd.h |
getcwd |
目录操作接口 |
获取当前工作目录 |
unistd.h |
getopt |
系统函数 |
分析命令行参数 |
unistd.h |
getpid |
系统函数 |
获取任务ID |
unistd.h |
isatty |
文件操作函数 |
判断文件是否为终端设备 |
unistd.h |
lseek |
文件操作函数 |
改变文件读写指针 |
unistd.h |
pread |
文件操作函数 |
读文件 |
unistd.h |
pwrite |
文件操作函数 |
写文件 |
unistd.h |
read |
文件操作函数 |
读文件 |
unistd.h |
rmdir |
目录操作接口 |
删除目录 |
unistd.h |
sleep |
时间函数 |
以秒为单位阻塞进程 |
unistd.h |
sync |
文件操作函数 |
文件同步 |
unistd.h |
sysconf |
系统函数 |
获取系统参数 |
unistd.h |
unlink |
文件操作函数 |
删除文件 |
unistd.h |
write |
文件操作函数 |
写文件 |
utime.h |
utime |
文件操作函数 |
修改文件时间 |
wchar.h |
mbsnrtowcs |
宽字符处理函数 |
多字节序列转换为宽字符(有最大长度限制) |
wchar.h |
wcscoll_l |
宽字符处理函数 |
采用目前区域的字符排列次序来比较宽字符串 |
wchar.h |
wcsnlen |
宽字符处理函数 |
返回多字节字符的长度(有最大长度限制) |
wchar.h |
wcsnrtombs |
转换函数 |
把数组中存储的编码转换为多字节字符(有最大长度限制) |
wchar.h |
wcsxfrm_l |
宽字符处理函数 |
根据程序当前的区域选项中的LC_COLLATE来转换宽字符串的前n个字符 |
wctype.h |
iswalnum_l |
宽字符处理函数 |
判断宽字符是否字母或数字 |
wctype.h |
iswalpha_l |
宽字符处理函数 |
判断宽字符是否为英文字母 |
wctype.h |
iswblank |
宽字符处理函数 |
判断宽字符是否为TAB或者空格 |
wctype.h |
iswblank_l |
宽字符处理函数 |
判断宽字符是否为TAB或者空格 |
wctype.h |
iswcntrl_l |
宽字符处理函数 |
判断宽字符是否为控制字符 |
wctype.h |
iswctype_l |
宽字符处理函数 |
判断是否为宽字符类型 |
wctype.h |
iswdigit_l |
宽字符处理函数 |
判断宽字符是否为阿拉伯字符0到9 |
wctype.h |
iswgraph_l |
宽字符处理函数 |
判断宽字符是否为除空格外的可打印字符 |
wctype.h |
iswlower_l |
宽字符处理函数 |
判断宽字符是否为小写英文字母 |
wctype.h |
iswprint_l |
宽字符处理函数 |
判断宽字符是否为可打印字符 |
wctype.h |
iswpunct_l |
宽字符处理函数 |
判断宽字符是否为标点符号或特殊符号 |
wctype.h |
iswspace_l |
宽字符处理函数 |
判断宽字符是否为空格字符 |
wctype.h |
iswupper_l |
宽字符处理函数 |
判断宽字符是否为大写英文字母 |
wctype.h |
iswxdigit_l |
宽字符处理函数 |
判断宽字符是否为16进制数字 |
wctype.h |
towlower_l |
宽字符处理函数 |
将宽字符转换成小写字母 |
wctype.h |
towupper_l |
宽字符处理函数 |
将宽字符转换成大写字母 |
wctype.h |
wctype_l |
宽字符处理函数 |
判断是否为宽字符类型 |
POSIX NP支持接口¶
LiteOS还提供了一套POSIX NP(nonportable)适配接口,以支持部分SMP(多核)的NP接口。NP接口为非POSIX标准接口,作为POSIX接口的补充。以下接口只有在开启多核模式下可以操作亲和性,在单核模式下直接返回ENOERR。
文件名 |
接口名 |
说明 |
|---|---|---|
pthread.h |
pthread_attr_setaffinity_np |
设置attr亲和性属性 |
pthread.h |
pthread_attr_getaffinity_np |
获取attr亲和性属性 |
pthread.h |
pthread_setaffinity_np |
设置pthread任务的亲和性 |
pthread.h |
pthread_getaffinity_np |
获取pthread任务的亲和性 |
pthread.h |
pthread_setname_np |
设置线程名称 |
pthread.h |
pthread_getname_np |
获取线程名称 |
POSIX不支持接口¶
LiteOS的POSIX接口中,有一些未支持,具体参见下表。
头文件 |
接口名 |
|---|---|
aio.h |
aio_cancel |
aio.h |
aio_error |
aio.h |
aio_fsync |
aio.h |
aio_read |
aio.h |
aio_return |
aio.h |
aio_suspend |
aio.h |
aio_write |
aio.h |
lio_listio |
dirent.h |
dirfd |
dirent.h |
fdopendir |
dirent.h |
readdir_r |
dlfcn.h |
dlerror |
fcntl.h |
openat |
fcntl.h |
posix_fadvise |
fcntl.h |
posix_fallocate |
fmtmsg.h |
fmtmsg |
fnmatch.h |
fnmatch |
ftw.h |
ftw |
ftw.h |
nftw |
glob.h |
glob |
glob.h |
globfree |
grp.h |
endgrent |
grp.h |
getgrent |
grp.h |
getgrgid |
grp.h |
getgrgid_r |
grp.h |
getgrnam |
grp.h |
getgrnam_r |
grp.h |
setgrent |
iconv.h |
iconv |
iconv.h |
iconv_close |
iconv.h |
iconv_open |
inttypes.h |
imaxabs |
inttypes.h |
imaxdiv |
libgen.h |
dirname |
locale.h |
duplocale |
locale.h |
freelocale |
locale.h |
newlocale |
locale.h |
uselocale |
monetary.h |
strfmon |
monetary.h |
strfmon_l |
mqueue.h |
mq_notify |
netdb.h |
endhostent |
netdb.h |
endnetent |
netdb.h |
endprotoent |
netdb.h |
endservent |
netdb.h |
gai_strerror |
netdb.h |
gethostent |
netdb.h |
getnetbyaddr |
netdb.h |
getnetbyname |
netdb.h |
getnetent |
netdb.h |
getprotobyname |
netdb.h |
getprotobynumber |
netdb.h |
getprotoent |
netdb.h |
getservbyname |
netdb.h |
getservbyport |
netdb.h |
getservent |
netdb.h |
sethostent |
netdb.h |
setnetent |
netdb.h |
setprotoent |
netdb.h |
setservent |
nl_types.h |
catclose |
nl_types.h |
catgets |
nl_types.h |
catopen |
pthread.h |
pthread_atfork |
pthread.h |
pthread_attr_getguardsize |
pthread.h |
pthread_attr_getstack |
pthread.h |
pthread_attr_setguardsize |
pthread.h |
pthread_attr_setstack |
pthread.h |
pthread_barrier_destroy |
pthread.h |
pthread_barrier_init |
pthread.h |
pthread_barrier_wait |
pthread.h |
pthread_barrierattr_destroy |
pthread.h |
pthread_barrierattr_getpshared |
pthread.h |
pthread_barrierattr_init |
pthread.h |
pthread_barrierattr_setpshared |
pthread.h |
pthread_getattr_default_np |
pthread.h |
pthread_getattr_np |
pthread.h |
pthread_getconcurrency |
pthread.h |
pthread_getcpuclockid |
pthread.h |
pthread_getname_np |
pthread.h |
pthread_mutex_consistent |
pthread.h |
pthread_mutexattr_getpshared |
pthread.h |
pthread_mutexattr_getrobust |
pthread.h |
pthread_mutexattr_setpshared |
pthread.h |
pthread_mutexattr_setrobust |
pthread.h |
pthread_rwlock_destroy |
pthread.h |
pthread_rwlock_init |
pthread.h |
pthread_rwlock_rdlock |
pthread.h |
pthread_rwlock_timedrdlock |
pthread.h |
pthread_rwlock_timedwrlock |
pthread.h |
pthread_rwlock_tryrdlock |
pthread.h |
pthread_rwlock_trywrlock |
pthread.h |
pthread_rwlock_unlock |
pthread.h |
pthread_rwlock_wrlock |
pthread.h |
pthread_rwlockattr_destroy |
pthread.h |
pthread_rwlockattr_getpshared |
pthread.h |
pthread_rwlockattr_init |
pthread.h |
pthread_rwlockattr_setpshared |
pthread.h |
pthread_setattr_default_np |
pthread.h |
pthread_setconcurrency |
pthread.h |
pthread_setname_np |
pthread.h |
pthread_spin_destroy |
pthread.h |
pthread_spin_init |
pthread.h |
pthread_spin_lock |
pthread.h |
pthread_spin_trylock |
pthread.h |
pthread_spin_unlock |
pthread.h |
pthread_timedjoin_np |
pthread.h |
pthread_tryjoin_np |
pwd.h |
endpwent |
pwd.h |
getpwent |
pwd.h |
getpwnam |
pwd.h |
getpwnam_r |
pwd.h |
getpwuid |
pwd.h |
getpwuid_r |
pwd.h |
setpwent |
regex.h |
regcomp |
regex.h |
regerror |
regex.h |
regexec |
regex.h |
regfree |
setjmp.h |
siglongjmp |
setjmp.h |
sigsetjmp |
sched.h |
sched_getparam |
sched.h |
sched_getscheduler |
sched.h |
sched_rr_get_interval |
sched.h |
sched_setparam |
sched.h |
sched_setscheduler |
search.h |
hcreate |
search.h |
hdestroy |
search.h |
hsearch |
search.h |
insque |
search.h |
lfind |
search.h |
lsearch |
search.h |
remque |
search.h |
tdelete |
search.h |
tfind |
search.h |
tsearch |
search.h |
twalk |
semaphore.h |
sem_close |
semaphore.h |
sem_open |
semaphore.h |
sem_unlink |
signal.h |
kill |
signal.h |
killpg |
signal.h |
psiginfo |
signal.h |
psignal |
signal.h |
pthread_kill |
signal.h |
pthread_sigmask |
signal.h |
sigaction |
signal.h |
sigaddset |
signal.h |
sigaltstack |
signal.h |
sigdelset |
signal.h |
sigemptyset |
signal.h |
sigfillset |
signal.h |
sighold |
signal.h |
sigignore |
signal.h |
siginterrupt |
signal.h |
sigismember |
signal.h |
sigpause |
signal.h |
sigpending |
signal.h |
sigprocmask |
signal.h |
sigqueue |
signal.h |
sigrelse |
signal.h |
sigsuspend |
signal.h |
sigtimedwait |
signal.h |
sigwait |
signal.h |
sigwaitinfo |
spawn.h |
posix_spawn |
spawn.h |
posix_spawn_file_actions_addclose |
spawn.h |
posix_spawn_file_actions_adddup2 |
spawn.h |
posix_spawn_file_actions_addopen |
spawn.h |
posix_spawn_file_actions_destroy |
spawn.h |
posix_spawn_file_actions_init |
spawn.h |
posix_spawnattr_destroy |
spawn.h |
posix_spawnattr_getflags |
spawn.h |
posix_spawnattr_getpgroup |
spawn.h |
posix_spawnattr_getschedparam |
spawn.h |
posix_spawnattr_getschedpolicy |
spawn.h |
posix_spawnattr_getsigdefault |
spawn.h |
posix_spawnattr_getsigmask |
spawn.h |
posix_spawnattr_init |
spawn.h |
posix_spawnattr_setflags |
spawn.h |
posix_spawnattr_setpgroup |
spawn.h |
posix_spawnattr_setschedparam |
spawn.h |
posix_spawnattr_setschedpolicy |
spawn.h |
posix_spawnattr_setsigdefault |
spawn.h |
posix_spawnattr_setsigmask |
spawn.h |
posix_spawnp |
stdio.h |
ctermid |
stdio.h |
flockfile |
stdio.h |
fmemopen |
stdio.h |
ftrylockfile |
stdio.h |
funlockfile |
stdio.h |
getc_unlocked |
stdio.h |
getchar_unlocked |
stdio.h |
getdelim |
stdio.h |
getline |
stdio.h |
open_memstream |
stdio.h |
pclose |
stdio.h |
popen |
stdio.h |
putc_unlocked |
stdio.h |
putchar_unlocked |
stdio.h |
renameat |
stdio.h |
tempnam |
stdlib.h |
_Exit |
stdlib.h |
a64l |
stdlib.h |
drand48 |
stdlib.h |
erand48 |
stdlib.h |
exit |
stdlib.h |
getsubopt |
stdlib.h |
grantpt |
stdlib.h |
jrand48 |
stdlib.h |
l64a |
stdlib.h |
lcong48 |
stdlib.h |
mkdtemp |
stdlib.h |
mrand48 |
stdlib.h |
posix_openpt |
stdlib.h |
ptsname |
stdlib.h |
putenv |
stdlib.h |
rand_r |
stdlib.h |
setenv |
stdlib.h |
setkey |
stdlib.h |
unlockpt |
stdlib.h |
unsetenv |
string.h |
memccpy |
string.h |
strndup |
string.h |
strsignal |
stropts.h |
isastream |
sys/ipc.h |
ftok |
sys/mman.h |
mlock |
sys/mman.h |
mlockall |
sys/mman.h |
mmap |
sys/mman.h |
mprotect |
sys/mman.h |
msync |
sys/mman.h |
munlock |
sys/mman.h |
munlockall |
sys/mman.h |
munmap |
sys/mman.h |
posix_madvise |
sys/mman.h |
shm_open |
sys/mman.h |
shm_unlink |
sys/msg.h |
msgctl |
sys/msg.h |
msgget |
sys/msg.h |
msgrcv |
sys/msg.h |
msgsnd |
sys/resource.h |
getpriority |
sys/resource.h |
getrlimit |
sys/resource.h |
getrusage |
sys/resource.h |
setpriority |
sys/resource.h |
setrlimit |
sys/select.h |
pselect |
sys/sem.h |
semctl |
sys/sem.h |
semget |
sys/sem.h |
semop |
sys/shm.h |
shmat |
sys/shm.h |
shmctl |
sys/shm.h |
shmdt |
sys/shm.h |
shmget |
sys/socket.h |
sockatmark |
sys/socket.h |
socketpair |
sys/stat.h |
chmod |
sys/stat.h |
fchmod |
sys/stat.h |
fchmodat |
sys/stat.h |
fstatat |
sys/stat.h |
futimens |
sys/stat.h |
mkdirat |
sys/stat.h |
mkfifo |
sys/stat.h |
mkfifoat |
sys/stat.h |
mknod |
sys/stat.h |
mknodat |
sys/stat.h |
umask |
sys/stat.h |
utimensat |
sys/time.h |
getitimer |
sys/time.h |
setitimer |
sys/time.h |
utimes |
sys/wait.h |
wait |
sys/wait.h |
waitid |
sys/wait.h |
waitpid |
syslog.h |
closelog |
syslog.h |
openlog |
syslog.h |
setlogmask |
termios.h |
cfgetispeed |
termios.h |
cfgetospeed |
termios.h |
cfsetispeed |
termios.h |
cfsetospeed |
termios.h |
tcdrain |
termios.h |
tcflow |
termios.h |
tcflush |
termios.h |
tcgetattr |
termios.h |
tcgetsid |
termios.h |
tcsendbreak |
termios.h |
tcsetattr |
time.h |
clock_getcpuclockid |
time.h |
getdate |
time.h |
tzset |
ulimit.h |
ulimit |
unistd |
_exit |
unistd.h |
alarm |
unistd.h |
chown |
unistd.h |
confstr |
unistd.h |
crypt |
unistd.h |
ctermid |
unistd.h |
encrypt |
unistd.h |
execl |
unistd.h |
execle |
unistd.h |
execlp |
unistd.h |
execv |
unistd.h |
execve |
unistd.h |
execvp |
unistd.h |
faccessat |
unistd.h |
fchdir |
unistd.h |
fchown |
unistd.h |
fchownat |
unistd.h |
fdatasync |
unistd.h |
fexecve |
unistd.h |
fork |
unistd.h |
fpathconf |
unistd.h |
getegid |
unistd.h |
geteuid |
unistd.h |
getgid |
unistd.h |
getgroups |
unistd.h |
gethostid |
unistd.h |
gethostname |
unistd.h |
getlogin |
unistd.h |
getlogin_r |
unistd.h |
getpgid |
unistd.h |
getpgrp |
unistd.h |
getppid |
unistd.h |
getsid |
unistd.h |
getuid |
unistd.h |
lchown |
unistd.h |
link |
unistd.h |
linkat |
unistd.h |
nice |
unistd.h |
pathconf |
unistd.h |
pause |
unistd.h |
pipe |
unistd.h |
readlink |
unistd.h |
readlinkat |
unistd.h |
setegid |
unistd.h |
seteuid |
unistd.h |
setgid |
unistd.h |
setpgid |
unistd.h |
setpgrp |
unistd.h |
setregid |
unistd.h |
setreuid |
unistd.h |
setsid |
unistd.h |
setuid |
unistd.h |
swab |
unistd.h |
symlink |
unistd.h |
symlinkat |
unistd.h |
tcgetpgrp |
unistd.h |
tcsetpgrp |
unistd.h |
truncate |
unistd.h |
ttyname |
unistd.h |
ttyname_r |
unistd.h |
unlinkat |
utmpx.h |
endutxent |
utmpx.h |
getutxent |
utmpx.h |
getutxid |
utmpx.h |
getutxline |
utmpx.h |
pututxline |
utmpx.h |
setutxent |
wchar.h |
open_wmemstream |
wchar.h |
wcpcpy |
wchar.h |
wcpncpy |
wchar.h |
wcscasecmp |
wchar.h |
wcscasecmp_l |
wchar.h |
wcsdup |
wchar.h |
wcsncasecmp |
wchar.h |
wcsncasecmp_l |
wchar.h |
wcswidth |
wchar.h |
wcwidth |
wctype.h |
towctrans_l |
wctype.h |
wctrans_l |
wordexp.h |
wordexp |
wordexp.h |
wordfree |
Libc/Libm/Libmat接口¶
Libc支持接口¶
LiteOS支持部分Libc接口,具体如下表所示。
头文件 |
接口名 |
类型 |
说明 |
|---|---|---|---|
arpa/inet.h |
inet_aton |
网络函数 |
转换字符串IP为数字格式,并存储在传参地址中 |
arpa/inet.h |
htonl |
网络函数 |
将主机数转换成无符号长整型的网络字节顺序 |
arpa/inet.h |
htons |
网络函数 |
将整型变量从主机字节顺序转变成网络字节顺序 |
arpa/inet.h |
ntohl |
网络函数 |
将一个无符号长整形数从网络字节顺序转换为主机字节顺序 |
arpa/inet.h |
ntohs |
网络函数 |
将一个16位数由网络字节顺序转换为主机字节顺序 |
assert.h |
assert |
诊断宏函数 |
如果它的条件返回错误,则终止程序执行,用于调测 |
ctype.h |
isalnum |
数据判断 |
判断入参是否为字母或数字 |
ctype.h |
isalpha |
数据判断 |
判断入参是否为字母 |
ctype.h |
iscntrl |
数据判断 |
判断入参是否为控制字符 |
ctype.h |
isdigit |
数据判断 |
判断入参是否为十进制数 |
ctype.h |
isgraph |
数据判断 |
判断入参是否有图形表示 |
ctype.h |
islower |
数据判断 |
判断入参是否为小写字母 |
ctype.h |
isprint |
数据判断 |
判断入参是否可以打印 |
ctype.h |
ispunct |
数据判断 |
判断入参是否为标点符号 |
ctype.h |
isspace |
数据判断 |
判断入参是否为空格 |
ctype.h |
isupper |
数据判断 |
判断入参是否为大写字母 |
ctype.h |
isxdigit |
数据判断 |
判断入参是否为十六进制数 |
ctype.h |
tolower |
数据转换函数 |
将大写字母转换成小写字母 |
ctype.h |
toupper |
数据转换函数 |
将小写字母转换成大写字母 |
err.h |
err |
错误提示 |
输出错误信息 |
err.h |
errx |
错误提示 |
输出错误信息 |
err.h |
verr |
错误提示 |
输出错误信息 |
err.h |
verrx |
错误提示 |
输出错误信息 |
err.h |
vwarn |
错误提示 |
输出警告信息 |
err.h |
vwarnx |
错误提示 |
输出警告信息 |
err.h |
warn |
错误提示 |
输出警告信息 |
err.h |
warnx |
错误提示 |
输出警告信息 |
errno.h |
errno |
错误信息 |
错误类型 |
errno.h |
get_errno |
错误信息 |
获取错误类型 |
errno.h |
set_errno |
错误信息 |
设置错误类型 |
fcntl.h |
creat64 |
文件操作函数 |
创建一个文件(64位) |
fcntl.h |
fallcoate |
文件操作函数 |
为文件预分配物理空间 |
fcntl.h |
fallcoate64 |
文件操作函数 |
为文件预分配物理空间(64位) |
fcntl.h |
open64 |
文件操作函数 |
打开文件(64位) |
ifaddrs.h |
freeifaddrs |
网络函数 |
释放本地IP地址 |
ifaddrs.h |
getifaddrs |
网络函数 |
获取本地IP地址 |
inttypes.h |
strtoimax |
数据转换函数 |
解析字符串中的整数值 |
inttypes.h |
strtoumax |
数据转换函数 |
解析字符串中的整数值 |
inttypes.h |
wcstoimax |
数据转换函数 |
解析字符串中的整数值 |
inttypes.h |
wcstoumax |
数据转换函数 |
解析字符串中的整数值 |
locale.h |
setlocale |
系统函数 |
设置或读取地域化信息。读取地域信息时返回C.UTF-8,因为LiteOS不支持国际化,所以设置地域信息无效。 |
malloc.h |
calloc |
内存操作 |
申请内存,返回内存指针 |
malloc.h |
free |
内存操作 |
释放原先分配的动态内存 |
malloc.h |
malloc |
内存操作 |
申请内存,返回内存指针 |
malloc.h |
memalign |
内存操作 |
对齐申请内存,返回内存指针 |
malloc.h |
realloc |
内存操作 |
调整已经申请的内存,返回内存指针 |
malloc.h |
zalloc |
内存操作 |
申请内存并清零内存区域,返回内存指针 |
netdb.h |
gethostbyname |
网络函数 |
获取对应于给定主机名的主机信息 |
netdb.h |
gethostbyname_r |
网络函数 |
获取对应于给定主机名的主机信息,可重入 |
poll.h |
notify_poll |
文件操作函数 |
监视文件描述符 |
poll.h |
notify_poll_with_key |
文件操作函数 |
监视文件描述符 |
resolv.h |
dn_comp |
网络函数 |
压缩域名 |
sched.h |
sched_getaffinity |
调度函数 |
获取指定任务对的CPU亲和性 |
sched.h |
sched_setaffinity |
调度函数 |
设置指定任务对的CPU亲和性 |
setjmp.h |
longjmp |
系统函数 |
长跳转 |
setjmp.h |
setjmp |
系统函数 |
承担非局部标号和goto作用 |
stdio.h |
asprintf |
格式化字符串函数 |
格式化字符串的复制 |
stdio.h |
clearerr |
错误处理函数 |
清除错误标志(加锁) |
stdio.h |
clearerr_unlocked |
错误处理函数 |
清除错误标志(不加锁) |
stdio.h |
fclose |
文件操作函数 |
关闭文件,文件关闭后再操作,将操作失败,并且设置错误码 |
stdio.h |
feof |
文件操作函数 |
检查文件流是否读到了文件尾 |
stdio.h |
feof_unlocked |
文件操作函数 |
检查文件流是否读到了文件尾(不加锁) |
stdio.h |
ferror |
文件操作函数 |
检查文件流是否有错误 |
stdio.h |
ferror_unlocked |
文件操作函数 |
检查文件流是否有错误(不加锁) |
stdio.h |
fflush |
文件操作函数 |
更新缓冲区 |
stdio.h |
fflush_unlocked |
文件操作函数 |
更新缓冲区(不加锁) |
stdio.h |
fgetc |
文件操作函数 |
从文件中读取一个字符 |
stdio.h |
fgetpos |
文件操作函数 |
取得文件流的读取位置 |
stdio.h |
fgets |
文件操作函数 |
从文件中读取一个字符串 |
stdio.h |
fgets_unlocked |
文件操作函数 |
从文件中读取一个字符串(不加锁) |
stdio.h |
fileno_unlocked |
文件操作函数 |
取得参数stream指定的文件流所使用的文件描述符(不加锁), 如果文件流已经关闭,则返回-1,并设置错误码 |
stdio.h |
fopen |
文件操作函数 |
打开文件,不支持以rx模式打开一个文件,否则返回NULL |
stdio.h |
fopen64 |
文件操作函数 |
打开文件(64位) |
stdio.h |
fprintf |
文件操作函数 |
格式化输出数据至文件 |
stdio.h |
fputc |
文件操作函数 |
将字符写入文件流中 |
stdio.h |
fputs |
文件操作函数 |
将字符串写入文件流中 |
stdio.h |
fputs_unlocked |
文件操作函数 |
将字符串写入文件流中(不加锁) |
stdio.h |
fread |
文件操作函数 |
从文件流读取数据,读取文件的size未做对齐处理,需用户自行保证size对齐 |
stdio.h |
fread_unlocked |
文件操作函数 |
从文件流读取数据(不加锁) |
stdio.h |
freopen |
文件操作函数 |
关闭已打开的文件,按照传入的模式打开新文件后将新文件流关联到旧文件流 |
stdio.h |
freopen64 |
文件操作函数 |
关闭已打开的文件,按照传入的模式打开新文件后将新文件流关联到旧文件(64位) |
stdio.h |
fscanf |
文件操作函数 |
读取格式化输入 |
stdio.h |
fseek |
文件操作函数 |
移动文件流的读取位置 |
stdio.h |
fseeko64 |
文件操作函数 |
移动文件流的读写位置(64位) |
stdio.h |
fsetpos |
文件操作函数 |
移动文件流的读取位置 |
stdio.h |
fsetpos64 |
文件操作函数 |
移动文件流的读取位置(64位) |
stdio.h |
ftell |
文件操作函数 |
获取文件流的读取位置 |
stdio.h |
ftello64 |
文件操作函数 |
获取文件流的读取位置(64位) |
stdio.h |
fwrite |
文件操作函数 |
将数据写入文件流,写入文件的size未做对齐处理,需用户自行保证size对齐,有入参合法性校验 |
stdio.h |
fwrite_unlocked |
文件操作函数 |
将数据写入文件流(不加锁) |
stdio.h |
getc |
标准IO操作 |
由文件中读取一个字符 |
stdio.h |
getchar |
标准IO操作 |
由文件中读取一个字符 |
stdio.h |
gets |
标准IO操作 |
由文件中读取一个字符串 |
stdio.h |
perror |
标准IO操作 |
向标准错误流输出错误信息 |
stdio.h |
printf |
标准IO操作 |
格式化输出数据 |
stdio.h |
putc |
标准IO操作 |
将一指定字符写入文件 |
stdio.h |
putchar |
标准IO操作 |
将一指定字符写标准输出流 |
stdio.h |
puts |
标准IO操作 |
将指定的字符串写到标准输出流 |
stdio.h |
putw |
标准IO操作 |
将一个整数写入文件 |
stdio.h |
remove |
文件操作函数 |
删除 |
stdio.h |
rename |
文件操作函数 |
重命名 |
stdio.h |
rewind |
文件操作函数 |
移动文件流的读取位置到起始位置 |
stdio.h |
scanf |
格式化输入输出函数 |
格式化字符串输入 |
stdio.h |
setbuf |
文件操作函数 |
设置文件流的缓冲区 |
stdio.h |
setbuffer |
文件操作函数 |
设置文件流的缓冲区 |
stdio.h |
setvbuf |
文件操作函数 |
设置文件流的缓冲区,当入参size小于0时,返回-1,并设置错误码。当入参buf为NULL,则会动态申请默认大小空间作为文件缓冲区 |
stdio.h |
snprintf |
格式化输入输出函数 |
格式化字符串 |
stdio.h |
sprintf |
格式化输入输出函数 |
格式化字符串 |
stdio.h |
sscanf |
格式化输入输出函数 |
格式化输入字符串 |
stdio.h |
tmpnam |
文件操作函数 |
返回指向唯一文件名的指针 |
stdio.h |
ungetc |
文件操作函数 |
将一指定字符写回文件流中 |
stdio.h |
vasprintf |
格式化输入输出函数 |
格式化字符串 |
stdio.h |
vfprintf |
格式化输入输出函数 |
格式化输出数据至文件 |
stdio.h |
vfscanf |
格式化输入输出函数 |
从文件流读取字符串,根据参数format转化并格式化数据 |
stdio.h |
vprintf |
格式化输入输出函数 |
格式化输出 |
stdio.h |
vscanf |
格式化输入输出函数 |
格式化字符串输入 |
stdio.h |
vsnprintf |
格式化输入输出函数 |
格式化字符串 |
stdio.h |
vsprintf |
格式化输入输出函数 |
格式化字符串 |
stdio.h |
vsscanf |
格式化输入输出函数 |
格式化输入字符串 |
stdlib.h |
abort |
系统函数 |
产生异常终止系统 |
stdlib.h |
abs |
数学计算函数 |
求整形绝对值 |
stdlib.h |
atof |
数据转换函数 |
将字符串转换成浮点型数 |
stdlib.h |
atoi |
数据转换函数 |
将字符串转换成整型数 |
stdlib.h |
atol |
数据转换函数 |
将字符串转换成长整型数 |
stdlib.h |
atoll |
数据转换函数 |
将字符串转换成长长整型数 |
stdlib.h |
bsearch |
数据结构函数 |
二元搜索 |
stdlib.h |
labs |
数学计算函数 |
求长整形绝对值 |
stdlib.h |
llabs |
数学计算函数 |
求长整形绝对值 |
stdlib.h |
mblen |
多字节字符函数 |
多字节字符字符串长度 |
stdlib.h |
mbstowcs |
多字节字符函数 |
转换多字节字符串到宽字符字符串 |
stdlib.h |
mbtowc |
多字节字符函数 |
转换多字节字符到宽字符字符 |
stdlib.h |
mkostemps |
文件处理函数 |
产生唯一的临时文件名 |
stdlib.h |
mkstemps |
文件处理函数 |
建立唯一的临时文件 |
stdlib.h |
qsort |
排序函数 |
利用快速排序法排列数组 |
stdlib.h |
qsort_r |
排序函数 |
利用快速排序法排列数组 |
stdlib.h |
rand |
随机数函数 |
产生随机数 |
stdlib.h |
srand |
随机数函数 |
设置随机数种子 |
stdlib.h |
strtod |
数据转换函数 |
将字符串转换成浮点型数 |
stdlib.h |
strtof |
字符串处理函数 |
字符串转单精度浮点型数 |
stdlib.h |
strtol |
数据转换函数 |
将字符串转换成长整型数 |
stdlib.h |
strtold |
字符串处理函数 |
字符串转长双精度浮点型数 |
stdlib.h |
strtoll |
字符串处理函数 |
字符串转双长整型数 |
stdlib.h |
strtoul |
数据转换函数 |
将字符串转换成无符号长整型数 |
stdlib.h |
strtoull |
字符串处理函数 |
字符串转无符号双长整数 |
stdlib.h |
wctomb |
多字节字符函数 |
转换宽字符字符到多字节字符 |
string.h |
memchr |
字符串处理函数 |
在某一内存范围内查找一特定字符 |
string.h |
memcmp |
字符串处理函数 |
比较内存内容 |
string.h |
memcpy |
字符串处理函数 |
拷贝内存内容 |
string.h |
memmove |
字符串处理函数 |
拷贝内存内容 |
string.h |
memrchr |
字符串处理函数 |
查找字符首次出现位置 |
string.h |
memset |
字符串处理函数 |
将一段内存空间填入某值 |
string.h |
strcasestr |
字符串处理函数 |
在一字符串中查找指定字符串,忽略大小写 |
string.h |
strcat |
字符串处理函数 |
连接两个字符串 |
string.h |
strchr |
字符串处理函数 |
查找字符首次出现位置 |
string.h |
strchrnul |
字符串处理函数 |
查找字符首次出现位置 |
string.h |
strcmp |
字符串处理函数 |
比较字符串 |
string.h |
strcoll |
字符串处理函数 |
根据 LC_COLLATE 比较字符串 |
string.h |
strcpy |
字符串处理函数 |
拷贝字符串 |
string.h |
strcspn |
字符串处理函数 |
入参1字符串中连续有几个字符都不属于入参2字符串 |
string.h |
strerror |
字符串处理函数 |
返回一个指向错误消息字符串的指针 |
string.h |
strlcpy |
字符串处理函数 |
拷贝字符串 |
string.h |
strlen |
字符串处理函数 |
返回字符串长度 |
string.h |
strncat |
字符串处理函数 |
连接两个字符串 |
string.h |
strncmp |
字符串处理函数 |
比较字符串 |
string.h |
strncpy |
字符串处理函数 |
拷贝字符串 |
string.h |
strpbrk |
字符串处理函数 |
查找字符串中第一个出现的指定字符 |
string.h |
strrchr |
字符串处理函数 |
查找字符串中最后出现的指定字符 |
string.h |
strsep |
字符串处理函数 |
分解字符串为一组字符串 |
string.h |
strspn |
字符串处理函数 |
返回字符串中连续不含指定字符串内容的字符数 |
string.h |
strstr |
字符串处理函数 |
在一字符串中查找指定字符串 |
string.h |
strtok |
字符串处理函数 |
分割字符串 |
string.h |
strxfrm |
字符串处理函数 |
根据LC_COLLATE来转换字符串src,若传入n的长度小于字符串src的长度,则不作转换,直接返回字符串src的长度 |
strings.h |
bcmp |
内存操作 |
同memcmp |
strings.h |
bcopy |
内存操作 |
同memmove |
strings.h |
bzero |
内存操作 |
同memset |
strings.h |
rindex |
字符处理函数 |
字符处理 |
sys/mount.h |
mount |
文件系统操作 |
挂载文件系统 |
sys/mount.h |
umount |
文件系统操作 |
卸载文件系统 |
sys/prctl.h |
prctl |
pthread |
线程操作,目前只支持PR_SET_NAME操作修改线程名 |
sys/stat.h |
fstat64 |
文件操作接口 |
获取文件属性(64位) |
sys/statfs.h |
statfs |
文件操作接口 |
获取文件系统属性 |
sys/time.h |
adjtime |
时间函数 |
按时间值delta来微调系统时间 |
sys/time.h |
settimeofday |
时间函数 |
设置系统时间 |
threads.h |
tss_get |
pthread |
获取调用线程的键绑定,同pthread_getspecific |
time.h |
asctime |
时间函数 |
将时间和日期以字符串形式表示 |
time.h |
clock |
时间函数 |
获取处理器时钟所使用的时间 |
time.h |
ctime |
时间函数 |
将时间和日期以字符串形式表示 |
time.h |
difftime |
时间函数 |
返回两个时间之间的差(秒) |
time.h |
gmtime |
时间函数 |
取得目前的时间和日期 |
time.h |
localtime |
时间函数 |
取得当地目前的时间和日期 |
time.h |
mktime |
时间函数 |
将时间结构数据转换成经过的秒数 |
time.h |
stime |
时间函数 |
设置时间 |
time.h |
strftime |
时间函数 |
格式化日期和时间,格式化字符不支持%z或%Z的格式化转换 |
time.h |
time |
时间函数 |
获取当前的系统时间 |
time64.h |
asctime64 |
时间函数 |
将时间和日期以字符串形式表示 |
time64.h |
asctime64_r |
时间函数 |
将时间和日期以字符串形式表示 |
time64.h |
ctime64 |
时间函数 |
将时间和日期以字符串形式表示 |
time64.h |
ctime64_r |
时间函数 |
将时间和日期以字符串形式表示 |
time64.h |
gmtime64 |
时间函数 |
取得目前的时间和日期 |
time64.h |
gmtime64_r |
时间函数 |
取得目前的时间和日期 |
time64.h |
gettimeofday64 |
时间函数 |
取得当前系统时间,LiteOS重新实现了该接口,用于在32位平台下获取2038年以后的系统时间 |
time64.h |
localtime64 |
时间函数 |
取得当地目前的时间和日期 |
time64.h |
localtime64_r |
时间函数 |
取得当地目前的时间和日期 |
time64.h |
mktime64 |
时间函数 |
将时间结构数据转换成经过的秒数 |
time64.h |
settimeofday64 |
时间函数 |
设置当前时间,LiteOS重新实现了该接口,用于在32位平台下设置当前系统时间为2038年以后的时间 |
tzdst.h |
settimezone |
时间函数 |
设置时区 |
tzdst.h |
dst_disable |
时间函数 |
失能夏令时 |
tzdst.h |
dst_enable |
时间函数 |
使能夏令时 |
tzdst.h |
dst_inquire |
时间函数 |
查询夏令时 |
uchar.h |
c32rtomb |
宽字符处理函数 |
将32位宽字符转换为多字节字符 |
uchar.h |
mbrtoc32 |
宽字符处理函数 |
将多字节字符转换为32位宽字符 |
unistd.h |
ftruncate64 |
文件操作函数 |
改变文件大小 |
unistd.h |
getentropy |
随机数函数 |
获取指定长度的随机数,若硬件不能提供随机数,则返回失败 |
unistd.h |
getpagesize |
系统函数 |
获取页大小 |
unistd.h |
lseek64 |
文件操作函数 |
改变文件读写指针 |
unistd.h |
pread64 |
文件操作函数 |
读文件 |
unistd.h |
pwrite64 |
文件操作函数 |
写文件 |
unistd.h |
usleep |
时间函数 |
以微秒为单位阻塞进程 |
wchar.h |
btowc |
宽字符处理函数 |
把单个字节转换为宽字符 |
wchar.h |
fgetwc |
宽字符处理函数 |
把单个字节转换为宽字符 |
wchar.h |
fgetwc_unlocked |
宽字符处理函数 |
把单个字节转换为宽字符(不加锁) |
wchar.h |
fputwc |
宽字符处理函数 |
写入一个宽字符到文件流 |
wchar.h |
fputwc_unlocked |
宽字符处理函数 |
写入一个宽字符到文件流(不加锁) |
wchar.h |
fwide |
文件操作函数 |
设置查询文件流的方向 |
wchar.h |
getwc |
宽字符处理函数 |
从文件中读取一个宽字符 |
wchar.h |
getwc_unlocked |
宽字符处理函数 |
从文件中读取一个宽字符(不加锁) |
wchar.h |
mbrlen |
宽字符处理函数 |
返回多字节字符的长度 |
wchar.h |
mbrtowc |
宽字符处理函数 |
多字节序列转换为宽字符 |
wchar.h |
mbsinit |
宽字符处理函数 |
测试初始的转换状态 |
wchar.h |
mbsrtowcs |
宽字符处理函数 |
多字节序列转换为宽字符 |
wchar.h |
putwc |
宽字符处理函数 |
将一指定宽字符写入文件中 |
wchar.h |
putwc_unlocked |
宽字符处理函数 |
将一指定宽字符写入文件中(不加锁) |
wchar.h |
swprintf |
格式化输入输出函数 |
格式化宽字符串 |
wchar.h |
ungetwc |
宽字符处理函数 |
将宽字符写回指定的文件流 |
wchar.h |
vfwprintf |
格式化输入输出函数 |
格式化输出宽字符数据至文件 |
wchar.h |
vfwscanf |
格式化输入输出函数 |
从输入流中格式化宽字符串 |
wchar.h |
vswprintf |
格式化输入输出函数 |
格式化宽字符串 |
wchar.h |
wcrtomb |
宽字符处理函数 |
将宽字符代码转换为字符 |
wchar.h |
wcschr |
宽字符处理函数 |
查找宽字符串中第一个出现的指定宽字符 |
wchar.h |
wcscmp |
宽字符处理函数 |
比较宽字符串 |
wchar.h |
wcscoll |
宽字符处理函数 |
采用目前区域的字符排列次序来比较宽字符串 |
wchar.h |
wcsftime |
宽字符处理函数 |
格式化时间 |
wchar.h |
wcsftime_l |
宽字符处理函数 |
格式化时间 |
wchar.h |
wcslen |
宽字符处理函数 |
返回宽字符串长度 |
wchar.h |
wcsncmp |
宽字符处理函数 |
比较两个宽字符串指定个数的字符 |
wchar.h |
wcsncpy |
宽字符处理函数 |
复制宽字符串 |
wchar.h |
wcsrtombs |
转换函数 |
把数组中存储的编码转换为多字节字符 |
wchar.h |
wcsstr |
宽字符处理函数 |
在一个宽字符串中搜索另一个宽字符串 |
wchar.h |
wcstol |
宽字符处理函数 |
将宽字符串转换为长整数 |
wchar.h |
wcstoll |
宽字符处理函数 |
将宽字符串转换为长长整数 |
wchar.h |
wcstoul |
宽字符处理函数 |
将宽字符串转换为无符号长整数 |
wchar.h |
wcstoull |
宽字符处理函数 |
将宽字符串转换为无符号长长整数 |
wchar.h |
wcsxfrm |
宽字符处理函数 |
根据程序当前的区域选项中的LC_COLLATE来转换宽字符串的前n个字符 |
wchar.h |
wctob |
宽字符处理函数 |
把宽字符转换为单字节字符 |
wchar.h |
wmemchr |
宽字符处理函数 |
在一个宽字符数组中搜索 |
wchar.h |
wmemcmp |
宽字符处理函数 |
比较两个宽字符数组 |
wchar.h |
wmemcpy |
宽字符处理函数 |
拷贝宽字符数组 |
wchar.h |
wmemmove |
宽字符处理函数 |
拷贝宽字符数组 |
wchar.h |
wmemset |
宽字符处理函数 |
填充一个宽字符数组 |
wctype.h |
iswalnum |
宽字符处理函数 |
判断宽字符是否字母或数字 |
wctype.h |
iswalpha |
宽字符处理函数 |
判断宽字符是否为英文字母 |
wctype.h |
iswcntrl |
宽字符处理函数 |
判断宽字符是否为控制字符 |
wctype.h |
iswctype |
宽字符处理函数 |
判断是否为宽字符类型 |
wctype.h |
iswdigit |
宽字符处理函数 |
判断宽字符是否为阿拉伯字符0到9 |
wctype.h |
iswgraph |
宽字符处理函数 |
判断宽字符是否为除空格外的可打印字符 |
wctype.h |
iswlower |
宽字符处理函数 |
判断宽字符是否为小写英文字母 |
wctype.h |
iswprint |
宽字符处理函数 |
判断宽字符是否为可打印字符 |
wctype.h |
iswpunct |
宽字符处理函数 |
判断宽字符是否为标点符号或特殊符号 |
wctype.h |
iswspace |
宽字符处理函数 |
判断宽字符是否为空格字符 |
wctype.h |
iswupper |
宽字符处理函数 |
判断宽字符是否为大写英文字母 |
wctype.h |
iswxdigit |
宽字符处理函数 |
判断宽字符是否为16进制数字 |
wctype.h |
towlower |
宽字符处理函数 |
将宽字符转换成小写字母 |
wctype.h |
towupper |
宽字符处理函数 |
将宽字符转换成大写字母 |
wctype.h |
wctype |
宽字符处理函数 |
判断是否为宽字符类型 |
Libm支持接口¶
LiteOS提供一套Libm开源接口,具体的规格参见下表。
须知: Libm不支持设置错误返回码。
头文件 |
接口名 |
类型 |
说明 |
|---|---|---|---|
complex.h |
cabs |
复数计算函数 |
计算复数的绝对值 |
math.h |
acos |
数学计算函数 |
取反余弦函数值 |
math.h |
acosf |
数学计算函数 |
求反余弦函数 |
math.h |
acosh |
数学计算函数 |
求反双曲余弦值 |
math.h |
acoshf |
数学计算函数 |
求反双曲余弦值 |
math.h |
acoshl |
数学计算函数 |
求反双曲余弦值 |
math.h |
acosl |
数学计算函数 |
求反余弦函数 |
math.h |
asin |
数学计算函数 |
取反正弦函数值 |
math.h |
asinf |
数学计算函数 |
求反正弦函数 |
math.h |
asinh |
数学计算函数 |
求反双曲正弦值 |
math.h |
asinhf |
数学计算函数 |
求反双曲正弦值 |
math.h |
asinhl |
数学计算函数 |
求反双曲正弦值 |
math.h |
asinl |
数学计算函数 |
求反正弦函数 |
math.h |
atan |
数学计算函数 |
取反正切函数值 |
math.h |
atan2 |
数学计算函数 |
取得反正切函数值 |
math.h |
atan2f |
数学计算函数 |
求反正切的值(以弧度表示) |
math.h |
atan2l |
数学计算函数 |
求反正切的值(以弧度表示) |
math.h |
atanf |
数学计算函数 |
求反正切函数 |
math.h |
atanh |
数学计算函数 |
求反双曲线正切函数 |
math.h |
atanhf |
数学计算函数 |
求反双曲线正切函数 |
math.h |
atanhl |
数学计算函数 |
求反双曲线正切函数 |
math.h |
atanl |
数学计算函数 |
求反正切函数 |
math.h |
cbrt |
数学计算函数 |
求立方根函数 |
math.h |
cbrtf |
数学计算函数 |
求立方根函数 |
math.h |
cbrtl |
数学计算函数 |
求立方根函数 |
math.h |
ceil |
数学计算函数 |
计算最小整数值 |
math.h |
ceilf |
数学计算函数 |
计算最小整数值 |
math.h |
ceill |
数学计算函数 |
计算最小整数值 |
math.h |
copysign |
数学计算函数 |
返回x,但是符号位变为y的符号位 |
math.h |
copysignf |
数学计算函数 |
返回x,但是符号位变为y的符号位 |
math.h |
copysignl |
数学计算函数 |
返回x,但是符号位变为y的符号位 |
math.h |
cos |
数学计算函数 |
求余弦函数 |
math.h |
cosf |
数学计算函数 |
求余弦函数 |
math.h |
cosh |
数学计算函数 |
求双曲线余弦函数 |
math.h |
coshf |
数学计算函数 |
求双曲线余弦函数 |
math.h |
coshl |
数学计算函数 |
求双曲线余弦函数 |
math.h |
cosl |
数学计算函数 |
求余弦函数 |
math.h |
drem |
数学计算函数 |
求余数(double) |
math.h |
dremf |
数学计算函数 |
求余数(float) |
math.h |
erf |
数学计算函数 |
求误差函数 |
math.h |
erfc |
数学计算函数 |
求误差补函数 |
math.h |
erfcf |
数学计算函数 |
求误差补函数 |
math.h |
erfcl |
数学计算函数 |
求误差补函数 |
math.h |
erff |
数学计算函数 |
求误差函数 |
math.h |
erfl |
数学计算函数 |
求误差函数 |
math.h |
exp |
数学计算函数 |
计算指数 |
math.h |
exp10 |
数学计算函数 |
计算以10为底的x次方值 |
math.h |
exp10l |
数学计算函数 |
计算以10为底的x次方值 |
math.h |
exp2 |
数学计算函数 |
计算以2为底的x次方值 |
math.h |
exp2f |
数学计算函数 |
计算以2为底的x次方值 |
math.h |
exp2l |
数学计算函数 |
计算以2为底的x次方值 |
math.h |
expf |
数学计算函数 |
计算以e为底的x次方值 |
math.h |
expl |
数学计算函数 |
计算以e为底的x次方值 |
math.h |
expm1 |
数学计算函数 |
exp(x) - 1.0 |
math.h |
expm1f |
数学计算函数 |
exp(x) - 1 |
math.h |
expm1l |
数学计算函数 |
exp(x) - 1 |
math.h |
fabs |
数学计算函数 |
计算浮点型数的绝对值 |
math.h |
fabsf |
数学计算函数 |
求浮点数的绝对值 |
math.h |
fabsl |
数学计算函数 |
求浮点数的绝对值 |
math.h |
fdim |
数学计算函数 |
计算入参的差值的绝对值 |
math.h |
fdimf |
数学计算函数 |
计算入参的差值的绝对值 |
math.h |
fdiml |
数学计算函数 |
计算入参的差值的绝对值 |
math.h |
finite |
数学计算函数 |
判断数据是否有效 |
math.h |
finitef |
数学计算函数 |
判断数据是否有效 |
math.h |
floor |
数学计算函数 |
向下取整 |
math.h |
floorf |
数学计算函数 |
向下取整 |
math.h |
floorl |
数学计算函数 |
向下取整 |
math.h |
fma |
数学计算函数 |
浮点运算(x · y) + z |
math.h |
fmaf |
数学计算函数 |
浮点运算(x · y) + z |
math.h |
fmal |
数学计算函数 |
浮点运算(x · y) + z |
math.h |
fmax |
数学计算函数 |
返回两个浮点入参的较大值 |
math.h |
fmaxf |
数学计算函数 |
返回两个浮点入参的较大值 |
math.h |
fmaxl |
数学计算函数 |
返回两个浮点入参的较大值 |
math.h |
fmin |
数学计算函数 |
返回两个浮点入参的较小值 |
math.h |
fminf |
数学计算函数 |
返回两个浮点入参的较小值 |
math.h |
fminl |
数学计算函数 |
返回两个浮点入参的较小值 |
math.h |
fmod |
数学计算函数 |
计算余数 |
math.h |
fmodf |
数学计算函数 |
计算余数 |
math.h |
fmodl |
数学计算函数 |
计算余数 |
math.h |
frexp |
数学计算函数 |
将浮点型数分为尾数与指数 |
math.h |
frexpf |
数学计算函数 |
把浮点数分解成尾数和指数 |
math.h |
frexpl |
数学计算函数 |
把浮点数分解成尾数和指数 |
math.h |
hypot |
数学计算函数 |
返回直角三角形斜边长度 |
math.h |
hypotf |
数学计算函数 |
返回直角三角形斜边长度 |
math.h |
hypotl |
数学计算函数 |
返回直角三角形斜边长度 |
math.h |
ilogb |
数学计算函数 |
与logb相同,但是返回有符号数的整数 |
math.h |
ilogbf |
数学计算函数 |
与logb相同,但是返回有符号数的整数 |
math.h |
ilogbl |
数学计算函数 |
与logb相同,但是返回有符号数的整数 |
math.h |
isnan |
数据判断 |
判断参数是否为不可表示的值 |
math.h |
j0 |
数学计算函数 |
返回x的一类0阶的Bessel函数 |
math.h |
j0f |
数学计算函数 |
返回x的一类0阶的Bessel函数 |
math.h |
j1 |
数学计算函数 |
返回x的一类1阶的Bessel函数 |
math.h |
j1f |
数学计算函数 |
返回x的一类1阶的Bessel函数 |
math.h |
jn |
数学计算函数 |
返回x的一类n阶的Bessel函数 |
math.h |
jnf |
数学计算函数 |
返回x的一类n阶的Bessel函数 |
math.h |
ldexp |
数学计算函数 |
返回x乘以2的exp次幂 |
math.h |
ldexpf |
数学计算函数 |
返回x乘以2的exp次幂 |
math.h |
ldexpl |
数学计算函数 |
返回x乘以2的exp次幂 |
math.h |
lgamma |
数学计算函数 |
返回gamma函数的绝对值的自然对数,gamma函数为:gamma (x) = integral from 0 to ∞ of t^(x-1) e^-t dt |
math.h |
lgamma_r |
数学计算函数 |
lgamma_r与lgamma类似,但它将中间结果的符号存储在第二个入参指向的变量中,而不是存储在全局变量中。这个接口是可重入的。 |
math.h |
lgammaf |
数学计算函数 |
返回gamma函数的绝对值的自然对数,gamma函数为:gamma (x) = integral from 0 to ∞ of t^(x-1) e^-t dt |
math.h |
lgammaf_r |
数学计算函数 |
lgamma_r与lgamma类似,但它将中间结果的符号存储在第二个入参指向的变量中,而不是存储在全局变量中。这个接口是可重入的。 |
math.h |
lgammal |
数学计算函数 |
返回gamma函数的绝对值的自然对数,gamma函数为:gamma (x) = integral from 0 to ∞ of t^(x-1) e^-t dt |
math.h |
lgammal_r |
数学计算函数 |
lgamma_r与lgamma类似,但它将中间结果的符号存储在第二个入参指向的变量中,而不是存储在全局变量中。这个接口是可重入的。 |
math.h |
llrint |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值 |
math.h |
llrintf |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值 |
math.h |
llrintl |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值 |
math.h |
llround |
数学计算函数 |
四舍五入为整数 |
math.h |
llroundf |
数学计算函数 |
四舍五入为整数 |
math.h |
llroundl |
数学计算函数 |
四舍五入为整数 |
math.h |
log |
数学计算函数 |
计算以e为底的对数值 |
math.h |
log10 |
数学计算函数 |
计算以10为底的对数值 |
math.h |
log10f |
数学计算函数 |
计算以10为底的x对数值 |
math.h |
log10l |
数学计算函数 |
计算以10为底的x对数值 |
math.h |
log1p |
数学计算函数 |
log(1+x) |
math.h |
log1pf |
数学计算函数 |
log(1+x) |
math.h |
log1pl |
数学计算函数 |
log(1+x) |
math.h |
log2 |
数学计算函数 |
计算以2为底的x对数值 |
math.h |
log2f |
数学计算函数 |
计算以2为底的x对数值 |
math.h |
log2l |
数学计算函数 |
计算以2为底的x对数值 |
math.h |
logb |
数学计算函数 |
使用FLT_RADIX作为对数的底数,返回x绝对值的对数 |
math.h |
logbf |
数学计算函数 |
使用FLT_RADIX作为对数的底数,返回x绝对值的对数 |
math.h |
logbl |
数学计算函数 |
使用FLT_RADIX作为对数的底数,返回x绝对值的对数 |
math.h |
logf |
数学计算函数 |
计算以e为底的x对数值 |
math.h |
logl |
数学计算函数 |
计算以e为底的x对数值 |
math.h |
lrint |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值,返回值为long int型 |
math.h |
lrintf |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值,返回值为long int型 |
math.h |
lrintl |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值,返回值为long int型 |
math.h |
lround |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值,返回值为long int型 |
math.h |
lroundf |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值,返回值为long int型 |
math.h |
lroundl |
数学计算函数 |
根据当前的舍入方式,将x舍入为整数值,返回值为long int型 |
math.h |
modf |
数学计算函数 |
将浮点型数分解成整数与小数,返回小数部分 |
math.h |
modff |
数学计算函数 |
将一个浮点值分为整数和小数部分 |
math.h |
modfl |
数学计算函数 |
将一个浮点值分为整数和小数部分 |
math.h |
nan |
数据判断 |
返回非数 |
math.h |
nanf |
数据判断 |
返回非数 |
math.h |
nanl |
数据判断 |
返回非数 |
math.h |
nearbyint |
数学计算函数 |
根据当前舍入模式,将x舍入为整数值 |
math.h |
nearbyintf |
数学计算函数 |
根据当前舍入模式,将x舍入为整数值 |
math.h |
nearbyintl |
数学计算函数 |
根据当前舍入模式,将x舍入为整数值 |
math.h |
nextafter |
数学计算函数 |
取两个双精度浮点数之间与第一个数相邻的浮点数,两数相等,则返回第二个数 |
math.h |
nextafterf |
数学计算函数 |
取两个浮点数之间与第一个数相邻的浮点数,两数相等,则返回第二个数 |
math.h |
nextafterl |
数学计算函数 |
返回x在y方向上的下一个可表示的数 |
math.h |
nexttoward |
数学计算函数 |
返回x在y方向上的上一个可表示的数 |
math.h |
nexttowardf |
数学计算函数 |
返回x在y方向上的上一个可表示的数 |
math.h |
nexttowardl |
数学计算函数 |
返回x在y方向上的上一个可表示的数 |
math.h |
pow |
数学计算函数 |
计算次方值 |
math.h |
pow10 |
数学计算函数 |
计算10的x次方 |
math.h |
pow10f |
数学计算函数 |
计算10的x次方 |
math.h |
pow10l |
数学计算函数 |
计算10的x次方 |
math.h |
powf |
数学计算函数 |
求 x 的 y 次幂(次方) |
math.h |
powl |
数学计算函数 |
求 x 的 y 次幂(次方) |
math.h |
remainder |
数学计算函数 |
计算余数,舍入到最接近的整数 |
math.h |
remainderf |
数学计算函数 |
计算余数,舍入到最接近的整数 |
math.h |
remainderl |
数学计算函数 |
计算余数,舍入到最接近的整数 |
math.h |
remquo |
数学计算函数 |
计算余数 |
math.h |
remquof |
数学计算函数 |
计算余数 |
math.h |
remquol |
数学计算函数 |
计算余数 |
math.h |
rint |
数学计算函数 |
将浮点数舍入到最接近的整数 |
math.h |
rintf |
数学计算函数 |
将浮点数舍入到最接近的整数 |
math.h |
rintl |
数学计算函数 |
将浮点数舍入到最接近的整数 |
math.h |
round |
数学计算函数 |
返回x的四舍五入整数值 |
math.h |
roundf |
数学计算函数 |
返回x的四舍五入整数值 |
math.h |
roundl |
数学计算函数 |
返回x的四舍五入整数值 |
math.h |
scalb |
数学计算函数 |
返回x * 2exp |
math.h |
scalbf |
数学计算函数 |
返回x * 2exp |
math.h |
scalbln |
数学计算函数 |
返回x * 2exp |
math.h |
scalblnf |
数学计算函数 |
返回x * 2exp |
math.h |
scalblnl |
数学计算函数 |
返回x * 2exp |
math.h |
scalbn |
数学计算函数 |
返回x乘以FLT_RADIX的n次幂 |
math.h |
scalbnf |
数学计算函数 |
返回x乘以FLT_RADIX的n次幂 |
math.h |
scalbnl |
数学计算函数 |
返回x乘以FLT_RADIX的n次幂 |
math.h |
significand |
数学计算函数 |
返回范围[1,2)的尾数,scalb (x, (double) -ilogb (x)). |
math.h |
significandf |
数学计算函数 |
返回范围[1,2)的尾数,scalb (x, (double) -ilogb (x)). |
math.h |
sin |
数学计算函数 |
取正弦函数值 |
math.h |
sincos |
数学计算函数 |
取正弦余弦值 |
math.h |
sincosf |
数学计算函数 |
取正弦余弦值 |
math.h |
sincosl |
数学计算函数 |
取正弦余弦值 |
math.h |
sinf |
数学计算函数 |
求正弦函数 |
math.h |
sinh |
数学计算函数 |
取双曲线正弦函数值 |
math.h |
sinhf |
数学计算函数 |
求双曲线正弦函数 |
math.h |
sinhl |
数学计算函数 |
求双曲线正弦函数 |
math.h |
sinl |
数学计算函数 |
求正弦函数 |
math.h |
sqrt |
数学计算函数 |
计算平方根值 |
math.h |
sqrtf |
数学计算函数 |
计算平方根值 |
math.h |
sqrtl |
数学计算函数 |
计算平方根值 |
math.h |
tan |
数学计算函数 |
求正切函数 |
math.h |
tanf |
数学计算函数 |
求正切函数 |
math.h |
tanh |
数学计算函数 |
取双曲线正切函数值 |
math.h |
tanhf |
数学计算函数 |
求双曲线正切函数 |
math.h |
tanhl |
数学计算函数 |
求双曲线正切函数 |
math.h |
tanl |
数学计算函数 |
求正切函数 |
math.h |
tgamma |
数学计算函数 |
参数为arg的gamma函数返回 |
math.h |
tgammaf |
数学计算函数 |
参数为arg的gamma函数返回 |
math.h |
tgammal |
数学计算函数 |
参数为arg的gamma函数返回 |
math.h |
trunc |
时间函数 |
截取日期或数字,返回指定的值 |
math.h |
truncf |
时间函数 |
截取日期或数字,返回指定的值 |
math.h |
truncl |
时间函数 |
截取日期或数字,返回指定的值 |
math.h |
y0 |
数学计算函数 |
返回x的第二类0阶Bessel函数 |
math.h |
y0f |
数学计算函数 |
返回x的第二类0阶Bessel函数 |
math.h |
y1 |
数学计算函数 |
返回x的第二类1阶Bessel函数 |
math.h |
y1f |
数学计算函数 |
返回x的第二类1阶Bessel函数 |
math.h |
yn |
数学计算函数 |
返回x的第二类n阶Bessel函数 |
math.h |
ynf |
数学计算函数 |
返回x的第二类n阶Bessel函数 |
Libmat支持接口¶
LiteOS支持部分Libmat接口,具体如下表所示。
头文件 |
接口名 |
类型 |
说明 |
|---|---|---|---|
lib/libmat/libmat.h |
matrix_inv_f32 |
矩阵函数 |
32位浮点矩阵乘法 |
lib/libmat/libmat.h |
matrix_inv_f64 |
矩阵函数 |
64位浮点矩阵乘法, |
lib/libmat/libmat.h |
matrix_mul_f32 |
矩阵函数 |
32位浮点矩阵求逆 |
lib/libmat/libmat.h |
matrix_mul_f64 |
矩阵函数 |
64位浮点矩阵求逆 |
开发流程
打开菜单,进入Lib ---> Enable lib matrix菜单,完成矩阵模块的配置。
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_LIB_LIBMAT |
使能矩阵库功能 |
YES/NO |
NO |
NULL |
须知: 矩阵运算:
需要用户保证入参的矩阵是存在的,行列数是自身矩阵的行列数。
未做数据溢出判断,需要用户自己确保计算结果不会溢出 矩阵乘法: 需要用户保证入参的结果矩阵是空矩阵。
Libc/Libm/Libmat不支持接口¶
LiteOS的Libc/Libm/Libmat接口中,有一些未支持,具体参见下表。
头文件 |
接口名 |
|---|---|
alloca.h |
alloca |
arpa/inet.h |
inet_lnaof |
arpa/inet.h |
inet_makeaddr |
arpa/inet.h |
inet_netof |
arpa/inet.h |
inet_network |
arpa/nameser.h |
ns_get16 |
arpa/nameser.h |
ns_get32 |
arpa/nameser.h |
ns_initparse |
arpa/nameser.h |
ns_name_uncompress |
arpa/nameser.h |
ns_parserr |
arpa/nameser.h |
ns_put16 |
arpa/nameser.h |
ns_put32 |
arpa/nameser.h |
ns_skiprr |
byteswap.h |
bswap_16 |
byteswap.h |
bswap_32 |
byteswap.h |
bswap_64 |
complex.h |
cabsf |
complex.h |
cabsl |
complex.h |
cacos |
complex.h |
cacosf |
complex.h |
cacosh |
complex.h |
cacoshf |
complex.h |
cacoshl |
complex.h |
cacosl |
complex.h |
carg |
complex.h |
cargf |
complex.h |
cargl |
complex.h |
casin |
complex.h |
casinf |
complex.h |
casinh |
complex.h |
casinhf |
complex.h |
casinhl |
complex.h |
casinl |
complex.h |
catan |
complex.h |
catanf |
complex.h |
catanh |
complex.h |
catanhf |
complex.h |
catanhl |
complex.h |
catanl |
complex.h |
ccos |
complex.h |
ccosf |
complex.h |
ccosh |
complex.h |
ccoshf |
complex.h |
ccoshl |
complex.h |
ccosl |
complex.h |
cexp |
complex.h |
cexpf |
complex.h |
cexpl |
complex.h |
cimag |
complex.h |
cimagf |
complex.h |
cimagl |
complex.h |
clog |
complex.h |
clogf |
complex.h |
clogl |
complex.h |
conj |
complex.h |
conjf |
complex.h |
conjl |
complex.h |
cpow |
complex.h |
cpowf |
complex.h |
cpowl |
complex.h |
cproj |
complex.h |
cprojf |
complex.h |
cprojl |
complex.h |
creal |
complex.h |
crealf |
complex.h |
creall |
complex.h |
csin |
complex.h |
csinf |
complex.h |
csinh |
complex.h |
csinhf |
complex.h |
csinhl |
complex.h |
csinl |
complex.h |
csqrt |
complex.h |
csqrtf |
complex.h |
csqrtl |
complex.h |
ctan |
complex.h |
ctanf |
complex.h |
ctanh |
complex.h |
ctanhf |
complex.h |
ctanhl |
complex.h |
ctanl |
crypt.h |
crypt |
crypt.h |
crypt_r |
dirent.h |
getdents |
dirent.h |
versionsort |
dlfcn.h |
dladdr |
dlfcn.h |
dlinfo |
fcntl.h |
lockf |
fcntl.h |
lockf64 |
fcntl.h |
name_to_handle_at |
fcntl.h |
open_by_handle_at |
fcntl.h |
openat64 |
fcntl.h |
posix_fadvise64 |
fcntl.h |
posix_fallocate64 |
fcntl.h |
readahead |
fcntl.h |
splice |
fcntl.h |
sync_file_range |
fcntl.h |
tee |
fcntl.h |
vmsplice |
fenv.h |
feclearexcept |
fenv.h |
fegetenv |
fenv.h |
fegetexceptflag |
fenv.h |
fegetround |
fenv.h |
feholdexcept |
fenv.h |
feraiseexcept |
fenv.h |
fesetenv |
fenv.h |
fesetexceptflag |
fenv.h |
fesetround |
fenv.h |
fetestexcept |
fenv.h |
feupdateenv |
getopt.h |
getopt_long |
getopt.h |
getopt_long_only |
libintl.h |
bind_textdomain_codeset |
libintl.h |
bindtextdomain |
libintl.h |
dcgettext |
libintl.h |
dcngettext |
libintl.h |
dgettext |
libintl.h |
dngettext |
libintl.h |
gettext |
libintl.h |
ngettext |
libintl.h |
textdomain |
link.h |
dl_iterate_phdr |
locale.h |
localeconv |
malloc.h |
malloc_usable_size |
malloc.h |
valloc |
mntent.h |
addmntent |
mntent.h |
endmntent |
mntent.h |
getmntent |
mntent.h |
getmntent_r |
mntent.h |
hasmntopt |
mntent.h |
setmntent |
netdb.h |
gethostbyaddr |
netdb.h |
gethostbyaddr_r |
netdb.h |
gethostbyname2 |
netdb.h |
gethostbyname2_r |
netdb.h |
getservbyname_r |
netdb.h |
getservbyport_r |
netdb.h |
herror |
netdb.h |
hstrerror |
netinet/ether.h |
ether_aton |
netinet/ether.h |
ether_aton_r |
netinet/ether.h |
ether_hostton |
netinet/ether.h |
ether_line |
netinet/ether.h |
ether_ntoa |
netinet/ether.h |
ether_ntoa_r |
netinet/ether.h |
ether_ntohost |
poll.h |
ppoll |
pty.h |
forkpty |
pty.h |
openpty |
pwd.h |
fgetpwent |
pwd.h |
putpwent |
resolv.h |
dn_expand |
resolv.h |
dn_skipname |
resolv.h |
res_init |
resolv.h |
res_mkquery |
resolv.h |
res_query |
resolv.h |
res_querydomain |
resolv.h |
res_search |
resolv.h |
res_send |
sched.h |
clone |
sched.h |
sched_getcpu |
sched.h |
setns |
sched.h |
unshare |
search.h |
hcreate_r |
search.h |
hdestroy_r |
search.h |
hsearch_r |
search.h |
tdestroy |
shadow.h |
endspent |
shadow.h |
fgetspent |
shadow.h |
getspent |
shadow.h |
getspnam |
shadow.h |
getspnam_r |
shadow.h |
lckpwdf |
shadow.h |
putspent |
shadow.h |
setspent |
shadow.h |
sgetspent |
shadow.h |
ulckpwdf |
signal.h |
raise |
signal.h |
sigandset |
signal.h |
sigisemptyset |
signal.h |
sigorset |
spawn.h |
posix_spawn |
spawn.h |
posix_spawn_file_actions_addchdir_np |
spawn.h |
posix_spawn_file_actions_addclose |
spawn.h |
posix_spawn_file_actions_adddup2 |
spawn.h |
posix_spawn_file_actions_addfchdir_np |
spawn.h |
posix_spawn_file_actions_addopen |
spawn.h |
posix_spawn_file_actions_destroy |
spawn.h |
posix_spawn_file_actions_init |
spawn.h |
posix_spawnattr_destroy |
spawn.h |
posix_spawnattr_getflags |
spawn.h |
posix_spawnattr_getpgroup |
spawn.h |
posix_spawnattr_getschedparam |
spawn.h |
posix_spawnattr_getschedpolicy |
spawn.h |
posix_spawnattr_getsigdefault |
spawn.h |
posix_spawnattr_getsigmask |
spawn.h |
posix_spawnattr_init |
spawn.h |
posix_spawnattr_setflags |
spawn.h |
posix_spawnattr_setpgroup |
spawn.h |
posix_spawnattr_setschedparam |
spawn.h |
posix_spawnattr_setschedpolicy |
spawn.h |
posix_spawnattr_setsigdefault |
spawn.h |
posix_spawnattr_setsigmask |
spawn.h |
posix_spawnp |
stdio.h |
cuserid |
stdio.h |
fgetc_unlocked |
stdio.h |
fgetln |
stdio.h |
fopencookie |
stdio.h |
fputc_unlocked |
stdio.h |
getw |
stdio.h |
setlinebuf |
stdio.h |
tmpfile |
stdio.h |
tmpfile64 |
stdlib.h |
aligned_alloc |
stdlib.h |
at_quick_exit |
stdlib.h |
div |
stdlib.h |
ecvt |
stdlib.h |
fcvt |
stdlib.h |
gcvt |
stdlib.h |
getenv |
stdlib.h |
getloadavg |
stdlib.h |
ldiv |
stdlib.h |
lldiv |
stdlib.h |
mkdtemp |
stdlib.h |
mkostemp |
stdlib.h |
ptsname_r |
stdlib.h |
quick_exit |
stdlib.h |
reallocarray |
stdlib.h |
system |
stdlib.h |
tcgetwinsize |
stdlib.h |
tcsetwinsize |
stdlib.h |
valloc |
stdlib.h |
wcstombs |
string.h |
explicit_bzero |
string.h |
memmem |
string.h |
mempcpy |
string.h |
strlcat |
string.h |
strverscmp |
strings.h |
ffsl |
strings.h |
ffsll |
strings.h |
index |
sys/acct.h |
acct |
sys/auxv.h |
getauxval |
sys/cachectl.h |
cachectl |
sys/cachectl.h |
cacheflush |
sys/epoll.h |
epoll_create |
sys/epoll.h |
epoll_create1 |
sys/epoll.h |
epoll_ctl |
sys/epoll.h |
epoll_pwait |
sys/epoll.h |
epoll_wait |
sys/eventfd.h |
eventfd |
sys/eventfd.h |
eventfd_read |
sys/eventfd.h |
eventfd_write |
sys/fanotify.h |
fanotify_init |
sys/fanotify.h |
fanotify_mark |
sys/file.h |
flock |
sys/fsuid.h |
setfsgid |
sys/fsuid.h |
setfsuid |
sys/inotify.h |
inotify_add_watch |
sys/inotify.h |
inotify_init |
sys/inotify.h |
inotify_init1 |
sys/inotify.h |
inotify_rm_watch |
sys/io.h |
ioperm |
sys/io.h |
iopl |
sys/klog.h |
klogctl |
sys/membarrier.h |
membarrier |
sys/mman.h |
madvise |
sys/mman.h |
memfd_create |
sys/mman.h |
mincore |
sys/mman.h |
mlock2 |
sys/mman.h |
mremap |
sys/mman.h |
remap_file_pages |
sys/mount.h |
umount2 |
sys/personality.h |
personality |
sys/ptrace.h |
ptrace |
sys/quota.h |
quotactl |
sys/random.h |
getrandom |
sys/reboot.h |
reboot |
sys/resource.h |
prlimit |
sys/sem.h |
semtimedop |
sys/sendfile.h |
sendfile |
sys/signalfd.h |
signalfd |
sys/socket.h |
accept4 |
sys/socket.h |
recvmmsg |
sys/socket.h |
sendmmsg |
sys/stat.h |
fstatat64 |
sys/stat.h |
lchmod |
sys/stat.h |
lstat64 |
sys/statfs.h |
fstatfs |
sys/statvfs.h |
fstatvfs |
sys/statvfs.h |
statvfs |
sys/swap.h |
swapoff |
sys/swap.h |
swapon |
sys/sysinfo.h |
get_avphys_pages |
sys/sysinfo.h |
get_nprocs |
sys/sysinfo.h |
get_nprocs_conf |
sys/sysinfo.h |
get_phys_pages |
sys/sysinfo.h |
sysinfo |
sys/time.h |
futimes |
sys/time.h |
futimesat |
sys/time.h |
lutimes |
sys/timeb.h |
ftime |
sys/timerfd.h |
timerfd_create |
sys/timerfd.h |
timerfd_gettime |
sys/timerfd.h |
timerfd_settime |
sys/timex.h |
adjtimex |
sys/timex.h |
clock_adjtime |
sys/uio.h |
preadv |
sys/uio.h |
process_vm_readv |
sys/uio.h |
process_vm_writev |
sys/uio.h |
pwritev |
sys/wait.h |
wait3 |
sys/wait.h |
wait4 |
sys/xattr.h |
fgetxattr |
sys/xattr.h |
flistxattr |
sys/xattr.h |
fremovexattr |
sys/xattr.h |
fsetxattr |
sys/xattr.h |
getxattr |
sys/xattr.h |
lgetxattr |
sys/xattr.h |
listxattr |
sys/xattr.h |
llistxattr |
sys/xattr.h |
lremovexattr |
sys/xattr.h |
lsetxattr |
sys/xattr.h |
removexattr |
sys/xattr.h |
setxattr |
syslog.h |
vsyslog |
termios.h |
cfmakeraw |
termios.h |
cfsetspeed |
threads.h |
call_once |
threads.h |
cnd_broadcast |
threads.h |
cnd_destroy |
threads.h |
cnd_init |
threads.h |
cnd_signal |
threads.h |
cnd_timedwait |
threads.h |
cnd_wait |
threads.h |
mtx_destroy |
threads.h |
mtx_init |
threads.h |
mtx_lock |
threads.h |
mtx_timedlock |
threads.h |
mtx_trylock |
threads.h |
mtx_unlock |
threads.h |
thrd_create |
threads.h |
thrd_current |
threads.h |
thrd_detach |
threads.h |
thrd_equal |
threads.h |
thrd_exit |
threads.h |
thrd_join |
threads.h |
thrd_sleep |
threads.h |
thrd_yield |
threads.h |
tss_create |
threads.h |
tss_delete |
threads.h |
tss_set |
time.h |
timegm |
time.h |
timespec_get |
time64.h |
timegm64 |
time64.h |
timelocal64 |
uchar.h |
c16rtomb |
uchar.h |
mbrtoc16 |
ucontext.h |
getcontext |
ucontext.h |
makecontext |
ucontext.h |
setcontext |
ucontext.h |
swapcontext |
unistd.h |
_Fork |
unistd.h |
acct |
unistd.h |
brk |
unistd.h |
chroot |
unistd.h |
copy_file_range |
unistd.h |
daemon |
unistd.h |
dup3 |
unistd.h |
eaccess |
unistd.h |
endusershell |
unistd.h |
execvpe |
unistd.h |
get_current_dir_name |
unistd.h |
getdomainname |
unistd.h |
getdtablesize |
unistd.h |
gettid |
unistd.h |
getusershell |
unistd.h |
issetugid |
unistd.h |
pipe2 |
unistd.h |
posix_close |
unistd.h |
sbrk |
unistd.h |
setdomainname |
unistd.h |
setresgid |
unistd.h |
setresuid |
unistd.h |
setusershell |
unistd.h |
syncfs |
unistd.h |
syscall |
unistd.h |
truncate64 |
unistd.h |
ualarm |
unistd.h |
vfork |
unistd.h |
vhangup |
utmp.h |
endutent |
utmp.h |
getutent |
utmp.h |
getutid |
utmp.h |
getutline |
utmp.h |
login_tty |
utmp.h |
pututline |
utmp.h |
setutent |
utmp.h |
updwtmp |
utmp.h |
utmpname |
utmpx.h |
updwtmpx |
utmpx.h |
utmpxname |
wchar.h |
fgetws |
wchar.h |
fgetws_unlocked |
wchar.h |
fputws |
wchar.h |
fputws_unlocked |
wchar.h |
fwprintf |
wchar.h |
fwscanf |
wchar.h |
getwchar |
wchar.h |
getwchar_unlocked |
wchar.h |
putwchar |
wchar.h |
putwchar_unlocked |
wchar.h |
swscanf |
wchar.h |
vswscanf |
wchar.h |
vwprintf |
wchar.h |
vwscanf |
wchar.h |
wcscat |
wchar.h |
wcscpy |
wchar.h |
wcscspn |
wchar.h |
wcsncat |
wchar.h |
wcspbrk |
wchar.h |
wcsrchr |
wchar.h |
wcsspn |
wchar.h |
wcstod |
wchar.h |
wcstof |
wchar.h |
wcstok |
wchar.h |
wcstold |
wchar.h |
wcswcs |
wchar.h |
wprintf |
wchar.h |
wscanf |
wctype.h |
towctrans |
wctype.h |
wctrans |
须知: 自LiteOS V200R005C00版本起不再支持“stdlib.h”头文件中提供的atexit()。
CMSIS接口¶
CMSIS v1.0¶
本部分简要介绍LiteOS适配CMSIS v1.0接口的情况,更多关于接口声明、入参介绍、返回值类型、接口使用条件等详细介绍,请参见《CMSIS v1.0接口官方手册》。
CMSIS v1.0适配接口¶
LiteOS目前已支持大部分CMSIS v1.0接口,接口声明在“self_src/compat/cmsis/cmsis_os1.h”,接口描述详见下表。
接口名 |
类型 |
描述 |
|---|---|---|
osKernelInitialize |
内核类接口 |
初始化操作系统 |
osKernelStart |
内核类接口 |
启动操作系统 |
osKernelRunning |
内核类接口 |
判断系统是否启动 |
osKernelSysTick |
内核类接口 |
获取系统启动后时间(单位:tick) |
osThreadCreate |
任务/线程类接口 |
创建一个任务 |
osThreadGetId |
任务/线程类接口 |
获取当前的任务句柄 |
osThreadTerminate |
任务/线程类接口 |
终止某个任务 |
osThreadYield |
任务/线程类接口 |
切换至同优先级的就绪任务 |
osThreadSetPriority |
任务/线程类接口 |
设置任务优先级 |
osThreadGetPriority |
任务/线程类接口 |
获取任务优先级 |
osDelay |
延时类接口 |
任务延时处理 |
osTimerCreate |
定时器类接口 |
创建定时器 |
osTimerStart |
定时器类接口 |
启动定时器(若定时器正在计时会先停止该定时器) |
osTimerStop |
定时器类接口 |
停止定时器 |
osTimerDelete |
定时器类接口 |
删除定时器 |
osSignalSet |
信号类接口 |
设置信号 |
osSignalClear |
信号类接口 |
清除信号 |
osSignalWait |
信号类接口 |
等待信号 |
osMutexCreate |
互斥锁类接口 |
创建互斥锁 |
osMutexWait |
互斥锁类接口 |
获取互斥锁(在规定时间内阻塞等待) |
osMutexRelease |
互斥锁类接口 |
释放互斥锁 |
osMutexDelete |
互斥锁类接口 |
删除互斥锁 |
osSemaphoreCreate |
信号量类接口 |
创建信号量 |
osSemaphoreWait |
信号量类接口 |
获取信号量(阻塞等待) |
osSemaphoreRelease |
信号量类接口 |
释放信号量 |
osSemaphoreDelete |
信号量类接口 |
删除信号量 |
osPoolCreate |
块状内存类接口 |
创建块状内存池 |
osPoolAlloc |
块状内存类接口 |
申请内存 |
osPoolCAlloc |
块状内存类接口 |
申请内存并清零 |
osPoolFree |
块状内存类接口 |
释放内存 |
osMessageCreate |
指针消息类接口 |
创建消息队列(不带内容,一般为数据指针) |
osMessagePut |
指针消息类接口 |
往消息队列里放入消息 |
osMessageGet |
指针消息类接口 |
从消息队列里获取消息 |
osMailCreate |
内容消息类接口 |
创建消息队列(带内容,可理解为在osMessage的基础上增加块状内存创建) |
osMailAlloc |
内容消息类接口 |
申请内存(用于存放消息内容) |
osMailCAlloc |
内容消息类接口 |
申请内存并清零(用于存放消息内容) |
osMailPut |
内容消息类接口 |
往消息队列里放入消息 |
osMailGet |
内容消息类接口 |
从消息队列里获取消息 |
osMailFree |
内容消息类接口 |
释放已申请的内存 |
osWait |
延时类接口 |
等待任一信号或消息发生 |
CMSIS v1.0不支持接口¶
无。
CMSIS v1.0标准接口适配差异¶
考虑接口的易用性和LiteOS内部机制与CMSIS标准接口的差异,在适配CMSIS v1.0接口时,对部分接口进行了修改,详见下表。
接口名 |
类型 |
描述 |
|---|---|---|
osTimerStart |
定时器类接口 |
增加一种定时器类型osTimerDelay,与osTimerOnce同为单次定时器,差别在于osTimerOnce超时后会删除定时器,但osTimerDelay不会,可以通过osTimerStart重复启动。 |
osBinarySemaphoreCreate |
信号量类接口 |
新增接口,用于创建二值信号量。 |
osTimerRestart |
定时器类接口 |
重新启动定时器。 |
CMSIS v1.0非标准接口
接口名 |
类型 |
描述 |
|---|---|---|
osMailClear |
内容消息类接口 |
清空指定mail内容 |
osMailDelete |
内容消息类接口 |
删除指定mailQ |
CMSIS v2.0¶
本部分简要介绍LiteOS适配CMSIS v2.0接口的情况,更多关于接口声明、入参介绍、返回值类型、接口使用条件等详细介绍,请参见《CMSIS v2.0接口官方手册》。
CMSIS v2.0适配接口¶
LiteOS目前已支持大部分CMSIS v2.0接口,接口声明在“open_source/CMSIS/CMSIS/RTOS2/Include/cmsis_os2.h“”,接口描述详见下表。
接口名 |
类型 |
描述 |
|---|---|---|
osKernelInitialize |
内核类接口 |
初始化操作系统 |
osKernelGetInfo |
内核类接口 |
获取系统版本信息 |
osKernelGetState |
内核类接口 |
获取系统状态(具体状态可参考osThreadState_t) |
osKernelStart |
内核类接口 |
启动操作系统 |
osKernelLock |
内核类接口 |
锁内核(锁调度) |
osKernelUnlock |
内核类接口 |
解锁内核(解锁调度) |
osKernelRestoreLock |
内核类接口 |
恢复内核锁状态 |
osKernelGetTickCount |
内核类接口 |
获取系统启动后时间(单位:tick) |
osKernelGetTickFreq |
内核类接口 |
获取每秒的tick数 |
osKernelGetSysTimerCount |
内核类接口 |
获取系统启动后时间(单位:cycle) |
osKernelGetSysTimerFreq |
内核类接口 |
获取每秒的CPU cycle数 |
osThreadNew |
任务/线程类接口 |
创建任务 |
osThreadJoin |
任务/线程类接口 |
等待指定的任务并回收任务资源(需要开启内核LOSCFG_TASK_JOINABLE配置) |
osThreadDetach |
任务/线程类接口 |
设置任务为detached属性(需要开启内核LOSCFG_TASK_JOINABLE配置) |
osThreadGetName |
任务/线程类接口 |
获取任务名 |
osThreadGetId |
任务/线程类接口 |
获取任务句柄 |
osThreadGetState |
任务/线程类接口 |
获取任务状态 |
osThreadGetStackSize |
任务/线程类接口 |
获取任务栈大小 |
osThreadGetStackSpace |
任务/线程类接口 |
获取未使用过的任务栈空间 |
osThreadSetPriority |
任务/线程类接口 |
设置任务优先级 |
osThreadGetPriority |
任务/线程类接口 |
获取任务优先级 |
osThreadYield |
任务/线程类接口 |
切换至同优先级的就绪任务 |
osThreadSuspend |
任务/线程类接口 |
挂起任务(恢复前无法得到调度) |
osThreadResume |
任务/线程类接口 |
恢复任务 |
osThreadTerminate |
任务/线程类接口 |
终止任务(建议不要主动终止任务) |
osThreadGetCount |
任务/线程类接口 |
获取已创建的任务数量 |
osThreadFlagsSet |
任务事件类接口 |
写入指定事件 |
osThreadFlagsClear |
任务事件类接口 |
清除指定事件 |
osThreadFlagsGet |
任务事件类接口 |
获取当前任务事件 |
osThreadFlagsWait |
任务事件类接口 |
等待指定事件 |
osDelay |
延时类接口 |
任务延时(单位:tick) |
osDelayUntil |
延时类接口 |
延时至某一时刻(单位:tick) |
osTimerNew |
定时器类接口 |
创建定时器 |
osTimerGetName |
定时器类接口 |
获取定时器名称(目前固定返回NULL) |
osTimerStart |
定时器类接口 |
启动定时器(若定时器正在计时会先停止该定时器) |
osTimerStop |
定时器类接口 |
停止定时器 |
osTimerIsRunning |
定时器类接口 |
定时器是否在计时中 |
osTimerDelete |
定时器类接口 |
删除定时器 |
osEventFlagsNew |
事件类接口 |
创建事件(与任务事件ThreadFlags的差别在于有独立的句柄和控制块) |
osEventFlagsGetName |
事件类接口 |
获取事件名称(目前固定返回NULL) |
osEventFlagsSet |
事件类接口 |
写入指定事件 |
osEventFlagsClear |
事件类接口 |
清楚指定事件 |
osEventFlagsGet |
事件类接口 |
获取当前事件值 |
osEventFlagsWait |
事件类接口 |
等待指定事件 |
osEventFlagsDelete |
事件类接口 |
删除事件 |
osMutexNew |
互斥锁类接口 |
创建互斥锁 |
osMutexGetName |
互斥锁类接口 |
获取互斥锁名称(目前固定返回NULL) |
osMutexAcquire |
互斥锁类接口 |
获取互斥锁(阻塞等待) |
osMutexRelease |
互斥锁类接口 |
释放互斥锁 |
osMutexGetOwner |
互斥锁类接口 |
获取持有该互斥锁的任务句柄 |
osMutexDelete |
互斥锁类接口 |
删除互斥锁 |
osSemaphoreNew |
信号量类接口 |
创建信号量 |
osSemaphoreGetName |
信号量类接口 |
获取信号量名称(目前固定返回NULL) |
osSemaphoreAcquire |
信号量类接口 |
获取信号量(阻塞等待) |
osSemaphoreRelease |
信号量类接口 |
释放信号量 |
osSemaphoreGetCount |
信号量类接口 |
获取信号量的计数值 |
osSemaphoreDelete |
信号量类接口 |
删除信号量 |
osMessageQueueNew |
消息队列类接口 |
创建消息队列 |
osMessageQueueGetName |
消息队列类接口 |
获取消息队列名称(目前固定返回NULL) |
osMessageQueuePut |
消息队列类接口 |
往消息队列里放入消息 |
osMessageQueueGet |
消息队列类接口 |
从消息队列里获取消息 |
osMessageQueueGetCapacity |
消息队列类接口 |
获取消息队列节点数量 |
osMessageQueueGetMsgSize |
消息队列类接口 |
获取消息队列节点大小 |
osMessageQueueGetCount |
消息队列类接口 |
获取当前消息队列里的消息数量 |
osMessageQueueGetSpace |
消息队列类接口 |
获取当前消息队列里的剩余消息数量 |
osMessageQueueDelete |
消息队列类接口 |
删除消息队列 |
CMSIS v2.0不支持接口¶
CMSIS v2.0不支持接口描述如下表所示。
接口名 |
类型 |
描述 |
|---|---|---|
osKernelSuspend |
内核类接口 |
挂起内核阻止调度,一般用于低功耗处理,目前LiteOS已提供Tickless、Runstop等低功耗机制,暂未适配该接口。 |
osKernelResume |
内核类接口 |
同osKernelSuspend描述。 |
osThreadExit |
任务/线程类接口 |
LiteOS任务支持自删除,任务退出前不需要调用osThreadExit。 |
osThreadEnumerate |
任务/线程类接口 |
获取已创建的任务列表,目前未适配该接口,用户可以调用LiteOS的LOS_TaskInfoGet等维测接口获取任务状态。 |
osMemoryPoolAlloc |
块状内存类接口 |
接口需要支持超时时间内申请内存块,目前LiteOS暂未提供这种机制。 |
osMemoryPoolFree |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolNew |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolGetName |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolGetCapacity |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolGetBlockSize |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolGetCount |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolGetSpace |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMemoryPoolDelete |
块状内存类接口 |
由于osMemoryPoolAlloc未实现,剩余osMemPool类接口暂不实现。 |
osMessageQueueReset |
消息队列类接口 |
操作系统暂不提供清空队列接口,由用户对队列内容进行操作,避免资源泄漏或其他异常。 |
CMSIS v2.0标准接口适配差异¶
考虑接口的易用性和LiteOS内部机制与CMSIS标准接口的差异,在适配CMSIS v2.0接口时,对部分接口进行了修改,详见下表。
接口名 |
类型 |
描述 |
|---|---|---|
osKernelGetTick2ms |
内核类接口 |
新增接口,用于获取系统启动后时间(单位:ms)。接口声明在“compat/cmsis/cmsis_os.h”。 |
osMs2Tick |
内核类接口 |
新增接口,用于毫秒与系统Tick转换。接口声明在“compat/cmsis/cmsis_os.h”。 |
osThreadNew |
任务/线程类接口 |
优先级范围仅支持[osPriorityLow3, osPriorityHigh]。 |
osThreadSetPriority |
任务/线程类接口 |
优先级范围仅支持[osPriorityLow3, osPriorityHigh]。 |
CMSIS v2.0非标准接口
接口名 |
类型 |
描述 |
|---|---|---|
osThreadGetArgument |
任务/线程类接口 |
获取线程参数。 |
osTimerExtNew |
定时器类接口 |
创建定时器(可指定定时器是否对齐)。 |
linux适配¶
LiteOS目前适配的是linux-4.19.90版本。Linux适配支持裁剪,如果需要使用Linux适配功能,可以打开菜单,选择Compat ---> Enable Linux以使能Linux适配模块。
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_COMPAT_LINUX |
Linux适配模块的裁剪开关 |
YES/NO |
YES |
无 |
完成量(completion)¶
概述¶
基本概念
完成量(completion)是一种轻量级任务同步机制。完成量是对信号量的一种补充,Linux的信号量机制,在同一个信号量上允许并发执行up()和down(),在多CPU运行场景下,up()可能试图访问一个不存在的信号量,为了防止这种错误,专门设计了完成量这种机制。
当一个任务需要等待另一个任务完成某操作后才能执行时,通过完成量允许被等待任务在完成指定操作后通知等待任务,从而唤醒等待任务,实现任务同步。
开发指导¶
使用场景
完成量应用于多任务同步场合,实现任务间的同步。
功能
功能分类 |
接口名 |
描述 |
兼容性 |
|---|---|---|---|
初始化完成量 |
init_completion |
初始化一个完成量 |
不完全兼容,如果入参为空指针会退出,不做初始化 |
等待完成量 |
wait_for_completion |
永久等待完成量 |
不完全兼容,如果入参为空指针或接口使用场景不正确会退出 |
wait_for_completion_timeout |
超时等待完成量,超时时间单位为Tick |
不完全兼容,接口实现中增加了非法判断,使用时请查看API参考 |
|
唤醒等待完成量的任务 |
complete |
唤醒等待该完成量的任务队列中的首个任务 |
不完全兼容,如果入参为空会退出 |
complete_all |
唤醒所有等待该完成量的任务 |
不完全兼容,如果入参为空会退出 |
开发流程
完成量的典型开发流程:
打开菜单,选择Compat ---> Enable Linux ---> Enable completion,使能完成量。
配置项
含义
取值范围
默认值
依赖
LOSCFG_COMPAT_LINUX_COMPLETION
使能Linux完成量
YES/NO
NO
无
初始化完成量init_completion,该接口会创建一个完成量。
等待完成量wait_for_completion_timeout/wait_for_completion。
唤醒等待完成量的任务complete/complete_all。
注意事项¶
完成量类似于信号量机制,参考信号量机制“注意事项”,即不能在中断中以阻塞模式(永久阻塞和定时阻塞)操作完成量,因为中断不能被阻塞。
需要用户保证完成量接口的完成量指针入参的合法性,即入参必须为合法的完成量指针。
编程实例¶
实例描述
示例中,任务Example_TaskEntry创建了任务Example_Completion。Example_Completion等待完成量而阻塞,Example_TaskEntry唤醒该完成量。通过打印的先后顺序可以理解完成量操作时伴随的任务切换。
在任务Example_TaskEntry中创建任务Example_Completion,其中任务Example_Completion优先级高于Example_TaskEntry。
在任务Example_Completion中等待完成量,阻塞,发生任务切换,执行任务Example_TaskEntry。
在任务Example_TaskEntry中唤醒完成量,发生任务切换,执行任务Example_Completion。
Example_Completion得以执行,直到该任务结束。
Example_TaskEntry得以执行,直到任务结束。
编程示例
前提条件:在menuconfig菜单中已经使能Linux适配模块。
代码实现如下:
#include "linux/completion.h"
#include "los_task.h"
/* 任务ID */
UINT32 g_testTaskId;
/* 完成量 */
struct completion exampleCompletion;
/* 用例任务入口函数 */
VOID Example_Completion(VOID)
{
UINT32 ret;
/* 以超时等待方式等待完成量,超时时间为100Ticks */
printf("Example_Completion wait completion\n");
ret = wait_for_completion_timeout(&exampleCompletion, 100);
if(ret == 0) {
printf("Example_Completion, wait completion timeout\n");
} else {
printf("Example_Completion, wait completion success\n");
}
return;
}
UINT32 Example_TaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task;
/* 完成量初始化 */
init_completion(&exampleCompletion);
/* 创建任务 */
memset(&task, 0, sizeof(TSK_INIT_PARAM_S));
task.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Completion;
task.pcName = "EventTsk1";
task.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task.usTaskPrio = 8;
ret = LOS_TaskCreate(&g_testTaskId, &task);
if(ret != LOS_OK) {
printf("task create failed \n");
return LOS_NOK;
}
/* 唤醒完成量 */
printf("Example_TaskEntry complete\n");
complete(&exampleCompletion);
printf("Delete Task.\n");
/* 删除任务 */
ret = LOS_TaskDelete(g_testTaskId);
if(ret != LOS_OK) {
printf("task delete failed\n");
return LOS_NOK;
}
return LOS_OK;
}
结果验证
编译运行得到的结果为:
Example_Completion wait completion
Example_TaskEntry complete
Example_Completion, wait completion success
Delete Task.
工作队列(workqueue)¶
概述¶
基本概念
工作队列提供一种机制:任何系统模块都可以将工作项挂载到工作队列,由工作队列按照FIFO方式处理挂载在上面的工作项。
工作队列通过工作队列任务处理挂载到工作队列中的工作项。队列处理任务允许被重新调度和睡眠,从而节省系统资源。
LiteOS的工作队列机制提供了丰富的对外接口以管理工作队列。当有工作项挂载到工作队列中时,工作队列处理任务将被唤醒去处理工作队列中的工作项,直到所有工作项都被处理完,工作队列处理任务将重新进入睡眠状态。
开发指导¶
使用场景
工作队列运用在对时间要求不高的场景,原因是工作队列里的工作只有在工作队列处理任务得到运行时才能被逐一处理,而工作队列处理任务是否能被及时唤醒调度取决于其他内核任务,另外在中断里也不能调度,只有在退出中断后才能得到调度。
功能
功能分类 |
接口名 |
描述 |
兼容性 |
|---|---|---|---|
创建/销毁工作队列 |
create_singlethread_workqueue |
在当前CPU上创建一个工作队列 |
完全兼容 |
create_workqueue |
LiteOS只在当前CPU上创建一个工作队列,而对于多CPU场景,linux会在每个CPU上都创建一个工作队列 |
不完全兼容 |
|
destroy_workqueue |
销毁一个工作队列 |
完全兼容 |
|
初始化工作 |
INIT_WORK |
初始化一个工作,将该工作与指定的处理函数绑定。当该工作被执行时,就会执行这个处理函数 |
完全兼容 |
INIT_DELAYED_WORK |
初始化一个延迟工作,将该工作与指定的处理函数绑定,该工作将延迟一段时间后才会被执行。当该工作被执行时,就会执行这个处理函数 |
完全兼容 |
|
挂载工作项到工作队列 |
queue_work |
挂载一个工作项到指定工作队列 |
不完全兼容,有参数非法性检查,详见API参考 |
queue_delayed_work |
挂载一个延迟工作项到指定工作队列 |
不完全兼容,有参数非法性检查,详见API参考 |
|
schedule_work |
挂载一个工作项到系统默认工作队列 |
完全兼容 |
|
schedule_delayed_work |
挂载一个延迟工作项到系统默认工作队列 |
不完全兼容,有参数非法性检查,详见API参考 |
|
查询工作的状态 |
work_busy |
查询一个指定工作当前的状态 |
不完全兼容,返回值不同,详见API参考 |
执行工作 |
flush_work |
阻塞等待一个指定的非延迟工作项执行,完成后返回 |
完全兼容 |
flush_delayed_work |
取消延迟,并阻塞等待这个工作项执行,完成后返回 |
完全兼容 |
|
取消工作 |
cancel_delayed_work |
取消一个延迟工作,如果有其他任务正在取消该工作,则返回失败 |
不完全兼容,返回值不同 |
cancel_delayed_work_sync |
取消一个延迟工作项,须等待工作项执行结束再返回。如果有其他任务正在取消该工作,那么等待其执行完毕 |
不完全兼容,返回值不同 |
|
cancel_work_sync |
取消一个工作项,须等待工作项执行结束再返回。如果有其他任务正在取消该工作,那么等待其执行完毕 |
不完全兼容,返回值不同 |
说明:
创建工作队列时,会初始化信号量,用于工作队列处理任务的睡眠和唤醒,以及初始化重要结构体并搭建工作队列链表。
销毁工作队列时,系统先对资源进行加锁操作,再删除工作队列处理任务,释放信号量资源,释放内存空间等,最后解锁。
有两种类型的工作:非延迟工作和延迟工作。工作可以立即得到挂载,而延迟工作需要延时指定时间后才能被挂载到工作队列中。
开发流程
工作队列的典型流程:
打开菜单,选择Compat ---> Enable Linux ---> Enable workqueue,使能工作队列。
配置项
含义
取值范围
默认值
依赖
LOSCFG_COMPAT_LINUX_WORKQUEUE
使能Linux工作队列
YES/NO
NO
LOSCFG_COMPAT_LINUX_TIMER
调用create_workqueue接口创建工作队列。
调用INIT_WORK/INIT_DELAYED_WORK初始化工作。
调用queue_work/queue_delayed_work接口挂载工作项到工作队列。
调用flush_work/flush_delayed_work接口立即执行工作。
调用cancel_delayed_work_sync/cancel_work_sync接口取消工作。
调用destroy_workqueue接口删除工作队列。
注意事项¶
挂载到工作队列的工作项由工作队列处理任务进行处理,而任务的调度运行在时间上具有不确定性,所以对时间要求高的工作不建议使用工作队列进行处理。
LiteOS使用工作队列名对工作队列进行标识,所以不能存在相同名称的工作队列。
可以使用schedule_work/schedule_delayed_work接口向系统默认的工作队列挂载工作,而不用再创建工作队列。
编程实例¶
实例描述
本实例实现如下功能:
创建名称为wq_test的工作队列。
分配工作内存并且初始化工作。
向工作队列中挂载工作。
立即执行工作。
销毁工作队列。
编程示例
前提条件:在menuconfig菜单中已经使能Linux适配模块。
代码实现如下:
#include "los_printf.h"
#include "linux/workqueue.h"
static void Work_Func(struct work_struct *work)
{
int i;
for (i = 0; i < 2; i++) {
dprintf("workqueue function is been called!..%d..%d..\n", i, work->work_status);
}
}
UINT32 Example_Workqueue(VOID)
{
struct workqueue_struct *wq;
struct work_struct *work;
UINT32 ret = LOS_OK;
wq = create_workqueue("test1008");
dprintf("create the workqueue successfully!\n\n");
work =(struct work_struct *)malloc(sizeof(struct work_struct));
if (!work) {
ret = LOS_NOK;
}
dprintf("create work ok!\n\n");
INIT_WORK(work, Work_Func);
dprintf("init the work ok!\n\n");
ret = queue_work(wq, work);
dprintf("mount the work into workqueue successfully!\n\n");
ret = flush_work(work);
dprintf("flush the work ok!\n\n");
destroy_workqueue(wq);
dprintf("destroy the work ok!\n\n");
return ret;
}
结果验证
编译运行得到的结果为:
create the workqueue successfully!
create work ok!
init the work ok!
mount the work into workqueue successfully!
workqueue function is been called!..0..3..
workqueue function is been called!..1..3..
flush the work ok!
destroy the work ok!
等待队列(waitqueue)¶
概述¶
基本概念
等待队列常用于线程等待资源。
当一个线程必须获取某些资源后才能进行下一步操作,且不占用CPU资源的时候,可以将该线程放入等待队列中,然后系统会切换到其他进程,等资源准备好后再唤醒等待队列。等待队列可以概括为两种等待情况:超时等待和循环等待。
LiteOS的等待队列机制提供了丰富的对外接口以管理等待队列,可以满足linux系统等待队列的绝大多数功能。
开发指导¶
使用场景
与工作队列相比,等待队列运用在对时间要求较高的场景,尤其是超时等待机制,常见场景有:同步系统资源访问、中断底半部和异步事件通知等。
功能
功能分类 |
接口名 |
描述 |
兼容性 |
注意事项 |
|---|---|---|---|---|
初始化等待队列头 |
DECLARE_WAIT_QUEUE_HEAD |
定义并初始化等待队列头 |
完全兼容 |
不涉及 |
init_waitqueue_head |
初始化等待队列头 |
完全兼容 |
不涉及 |
|
获取等待队列合法性 |
waitqueue_active |
判断等待队列是否可用 |
完全兼容 |
用户保证入参是合法指针,不能是空指针 |
等待事件 |
wait_event |
等待事件 |
完全兼容 |
不涉及 |
wait_event_interruptible |
等待事件 |
不完全兼容,该事件不可被信号中断 |
不涉及 |
|
wait_event_interruptible_timeout |
超时等待事件 |
不完全兼容,该事件不可被信号中断,返回值不同,入参范围有限制,详见API参考 |
不涉及 |
|
唤醒事件 |
wake_up_interruptible |
唤醒事件 |
不完全兼容,入参合法时直接返回 |
不涉及 |
wake_up_interruptible_poll |
唤醒事件 |
完全兼容 |
不涉及 |
|
wake_up |
唤醒事件 |
完全兼容 |
不涉及 |
说明: 有两种类型的等待队列:
循环等待,只有等到资源后才会退出等待。
超时等待,即使未等到资源,在等待时间到达之后,也会退出等待。
开发流程
等待队列的典型开发流程:
打开菜单,选择Compat ---> Enable Linux ---> Enable waitqueue,使能等待队列。
配置项
含义
取值范围
默认值
依赖
LOSCFG_COMPAT_LINUX_WAITQUEUE
使能Linux等待队列
YES/NO
NO
LOSCFG_BASE_IPC_EVENT
调用DECLARE_WAIT_QUEUE_HEAD/init_waitqueue_head接口初始化等待队列头。
调用wait_event/wait_event_interruptible/wait_event_interruptible_timeout等待资源。
调用wake_up/wake_up_interruptible/wake_up_interruptible_poll唤醒等待队列。
注意事项¶
LiteOS的等待队列不支持linux内核signal打断的机制。
LiteOS不支持在一个等待队列中增加多个等待项,一个等待队列中只能有一个等待项。
编程实例¶
实例描述
本实例实现如下功能:
定义并初始化一个等待队列头。
创建一个任务,在任务中挂起事件,等待被唤醒。
唤醒事件。
编程示例
前提条件:在menuconfig菜单中已经使能Linux适配模块。
代码实现如下:
#include "los_printf.h"
#include "los_task.h"
#include "linux/wait.h.h"
static UINT32 g_TestTaskID;
static DECLARE_WAIT_QUEUE_HEAD(g_waitQueue);
static BOOL g_condition = FALSE;
static VOID Example_Task(VOID)
{
UINT32 timeout = 10; // 10 ticks timeout
UINT32 ret;
ret = wait_event_interruptible_timeout(g_waitQueue, g_condition, timeout);
if (ret > 0) {
g_TestCount++;
}
}
UINT32 Example_Waitqueue(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S stTask = {0};
stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Task;
stTask.usTaskPrio = TSK_PRIOR_HI;
stTask.pcName = "wait_task";
stTask.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
stTask.uwResved = LOS_TASK_STATUS_DETACHED;
g_TestCount = 0;
ret = LOS_TaskCreate(&g_TestTaskID, &stTask);
if (ret != LOS_OK) {
printf("Example_Task create Failed!\r\n");
return LOS_NOK;
}
LOS_TaskDelay(2);
g_condition = TRUE;
wake_up_interruptible(&g_waitQueue);
LOS_TaskDelay(1);
if (g_TestCount == 1) {
printf("Wakeup succeeded!\r\n");
return LOS_OK;
} else {
printf("Wakeup failed!\r\n");
return LOS_NOK;
}
}
结果验证
编译运行得到的结果为:
Wakeup succeeded!
中断¶
概述¶
基本概念
Linux内核中有专门的中断函数。为提高系统使用的友好度,LiteOS基于自身的中断机制,适配了Linux中的相关中断接口。
LiteOS的中断支持:
中断申请
中断删除
中断使能
中断屏蔽
中断底半部(基于workqueue)
中断顶半部和底半部的概念:
中断处理程序耗时应尽可能短,以满足中断的快速响应,为了平衡中断处理程序的性能与工作量,将中断处理程序分解为两部分:顶半部和底半部。
顶半部完成尽可能少的比较紧急的任务,它往往只是简单地读取寄存器中的中断状态并清除中断标志位,即进行“登记工作”,将耗时的底半部处理程序挂到系统的底半部执行队列中去。
开发指导¶
功能
LiteOS中断模块适配的linux接口如下表,接口详细信息可以查看API参考。
接口名 |
描述 |
兼容性 |
|---|---|---|
request_irq |
申请中断,将中断处理程序注册到指定中断号对应的链表中,支持一个中断号注册多个中断处理程序 |
不完全兼容,详见API参考 |
free_irq |
删除中断 |
完全兼容 |
enable_irq |
使能指定中断 |
完全兼容 |
disable_irq |
屏蔽指定中断 |
完全兼容 |
说明: 中断底半部的功能,可利用workqueue实现,在上半部中初始化一个工作项,将其添加到workqueue中,系统会在空闲时执行workqueue里的底半部,详细使能用法请查阅“工作队列(workqueue)”。
开发流程
打开菜单,选择Compat ---> Enable Linux ---> Enable irq interrupt,使能中断。
配置项
含义
取值范围
默认值
依赖
LOSCFG_COMPAT_LINUX_IRQ
使能Linux中断
YES/NO
NO
LOSCFG_ARCH_INTERRUPT_TAKEOVER
调用申请中断接口request_irq。
调用disable_irq接口屏蔽指定中断。
调用enable_irq接口使能指定中断。
调用free_irq接口删除中断。
注意事项¶
调用request_irq()申请中断时,中断处理程序入参要符合(int, void*)标准格式。直接调用LOS_HwiCreate()创建中断时,中断处理程序入参可以为NULL。
中断处理程序中禁止调用request_irq()和free_irq()接口。
当中断ID为共享中断时,不能通过LOS_HwiCreate()创建中断,且request_irq()入参dev指针不能为NULL,应与处理程序一一对应。直接调用LOS_HwiCreate()创建的中断,不能通过free_irq()删除。
编程实例¶
实例描述
本实例实现如下功能:
申请中断。
删除中断。
编程示例
前提条件:
在menuconfig菜单中使能Linux适配模块。
在menuconfig菜单中配置中断使用最大数和可设置的中断优先级个数。
代码实现如下:
#include "los_hwi.h"
#include "linux/interrupt.h"
#define HWI_NUM_INT50 50
void Uart_Irqhandle(int irq, void *dev)
{
printf("\nuart0:the function1 \n");
}
void Hwi_Test(void)
{
int a = 1;
void *dev = &a;
unsigned long flags = 0;
const char * name = "hwiTest";
request_irq(HWI_NUM_INT50, Uart_Irqhandle, flags, name, dev); // 创建中断
free_irq(HWI_NUM_INT50, dev); // 删除中断
}
高精度定时器¶
概述¶
基本概念
高精度软件定时器利用一定的硬件定时器资源,通过算法实现更高精度的软件定时器,满足了对精确时间的需求。
LiteOS高精度软件定时器设计了一套软件架构,提供了微秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动。
开发指导¶
使用场景
内核软件定时器的定时精度为毫秒级别,定时精度并不高,无法满足对定时精度要求更高的场合。高精度软件定时器弥补了内核软件定时器在这方面的不足,提供了精度到达微秒级别的定时接口。
接口名 |
描述 |
兼容性 |
|---|---|---|
hrtimer_init |
初始化高精度软件定时器资源,这部分功能已经在内核初始化阶段实现,所以这个接口只是适配linux接口,并无实现。 |
完全兼容 |
hrtimer_create |
创建高精度软件定时器,设置定时时间及回调函数。 |
LiteOS自定义接口 |
hrtimer_start |
启动高精度定时器。 |
不完全兼容,返回值不同,详见API参考 |
hrtimer_cancel |
取消还未超时的高精度定时器。 |
不完全兼容,返回值不同,详见API参考 |
hrtimer_forward |
修改还未超时的高精度定时器的定时时间,重新设定超时时间。 |
不完全兼容,入参不同,详见API参考 |
hrtimer_is_queued |
如果高精度定时器已创建,则返回0,否则返回1。 |
不完全兼容,返回值不同,详见API参考 |
开发流程
高精度软件定时器使用的典型流程:
打开菜单,选择Compat ---> Enable Linux ---> Enable Linux Hrtimer,使能高精度定时器。
配置项
含义
取值范围
默认值
依赖
LOSCFG_COMPAT_LINUX_HRTIMER
使能Linux高精度定时器
YES/NO
YES
LOSCFG_COMPAT_LINUX
定义一个hrtimer变量和一个ktime变量。
接着根据定时时间,给ktime变量赋值,分别为sec及usec字段。
然后调用hrtimer_create接口,创建高精度定时器,设置其定时器时间及回调函数。
最后调用hrtimer_start接口,启动高精度软件定时器。
调用hrtimer_cancel与hrtimer_forward接口取消或修改定时器的超时时间。
hrtimer_is_queued接口辅助用户判断高精度定时器是否已经创建。
注意事项¶
高精度软件定时器接口涉及的hrtimer变量,需要用户提供,接口不会为其申请任何资源。
hrtimer_init接口没有实现,只是适配linux接口,所以使用时可不调用。
目前高精度软件定时器只支持相对时间模式,不支持绝对时间模式。
高精度软件定时器依赖硬件,使用前需要确认硬件是否支持。
高精度定时器存在指令时间消耗,所以实际定时触发时间会大于设置的时间。
编程实例¶
实例描述
本实例实现如下功能:
用户定义swtmr变量及time变量。
调用hrtimer_create接口创建高精度定时器,设置其定时器时间及回调函数。
调用hrtimer_start接口启动定时器。
调用hrtimer_forward接口修改定时时间,由原来的80ms修改为40ms。
调用LOS_Msleep函数使任务延迟50ms。
再调用hrtimer_cancel接口取消定时器,发现此时定时器已超时不存在了。
编程实例
前提条件:已使能高精度定时器。
代码实现如下:
#include "los_base.h"
#include "linux/hrtimer.h"
static enum hrtimer_restart Hrtimer_Func(struct hrtimer *arg)
{
dprintf("The hrtimer is timeout!!!\n");
return HRTIMER_NORESTART;
}
static UINT32 TestCase(VOID)
{
struct hrtimer swtmr;
union ktime time;
union ktime interval = { 0 };
interval.tv.sec = 0;
interval.tv.usec = 40000;
int ret;
dprintf("----->test start<-----\n");
time.tv.sec = 0;
time.tv.usec = 80000;
ret = hrtimer_create(&swtmr, time, Hrtimer_Func);
if (ret == 0) {
dprintf("Hrtimer ctreate successfully!\n");
}
ret = hrtimer_start(&swtmr, time, HRTIMER_MODE_REL);
if (ret == 0) {
dprintf("Hrtimer start successfully!\n");
}
ret = hrtimer_forward(&swtmr, interval);
LOS_Msleep(50);
ret = hrtimer_cancel(&swtmr);
if (ret == 0) {
dprintf("Hrtimer already timeout!\n");
}
dprintf("----->test end<-----\n");
return LOS_OK;
}
结果验证
编译运行得到的结果为:
----->test start<-----
Hrtimer ctreate successfully!
Hrtimer start successfully!
The hrtimer is timeout!!!
Hrtimer already timeout!
----->test end<-----
linux适配接口概览¶
LiteOS提供的linux适配接口如下表所示。
序号 |
文件名 |
接口名 |
说明 |
兼容性 |
注意事项 |
|---|---|---|---|---|---|
1 |
clock.h |
sched_clock |
获取系统时间 |
完全兼容 |
不涉及 |
2 |
completion.h |
complete |
唤醒一个等待completion的线程 |
不完全兼容,如果入参为空会退出 |
不涉及 |
3 |
completion.h |
complete_all |
唤醒所有等待completion的线程 |
不完全兼容,如果入参为空会退出 |
不涉及 |
4 |
completion.h |
init_completion |
初始化completion结构 |
不完全兼容,如果入参为空指针会退出,不做初始化 |
不涉及 |
5 |
completion.h |
wait_for_completion |
等待completion |
不完全兼容,如果入参为空指针或接口使用场景不正确会退出 |
不涉及 |
6 |
completion.h |
wait_for_completion_timeout |
等待completion,若超时则结束等待 |
不完全兼容,接口实现中增加了非法判断,详见API参考 |
不涉及 |
7 |
crc32.h |
crc32 |
计算CRC |
完全兼容 |
不涉及 |
8 |
delay.h |
msleep |
延时,程序暂停若干时间(单位:ms) |
完全兼容 |
不涉及 |
9 |
delay.h |
udelay |
忙等延迟。只用于极小时间(微秒级)的延迟 |
完全兼容 |
不涉及 |
10 |
delay.h |
mdelay |
忙等延迟,延迟时间单位:ms |
完全兼容 |
不涉及 |
11 |
hrtimer.h |
hrtimer_cancel |
取消计时器并等待处理程序完成 |
不完全兼容,返回值不同,而且依赖平台,需要适配后才能实现,详见API参考 |
不涉及 |
12 |
hrtimer.h |
hrtimer_create |
设置时间段并创建回调函数 |
LiteOS自定义接口,依赖平台,需要适配后才能实现 |
不涉及 |
13 |
hrtimer.h |
hrtimer_forward |
转发计时器到期 |
不完全兼容,入参不同,而且依赖平台,需要适配后才能实现,详见API参考 |
不涉及 |
14 |
hrtimer.h |
hrtimer_init |
将计时器初始化为给定的时钟 |
依赖平台,需要适配后才能实现 |
不涉及 |
15 |
hrtimer.h |
hrtimer_is_queued |
检查计时器是否在队列之一上 |
不完全兼容,返回值不同,而且依赖平台,需要适配后才能实现,详见API参考 |
不涉及 |
16 |
hrtimer.h |
hrtimer_start |
(重新)在当前CPU上启动hrtimer |
不完全兼容,返回值不同,而且依赖平台,需要适配后才能实现,详见API参考 |
不涉及 |
17 |
interrupt.h |
disable_irq |
屏蔽中断 |
完全兼容 |
不涉及 |
18 |
interrupt.h |
enable_irq |
使能中断 |
完全兼容 |
不涉及 |
19 |
interrupt.h |
free_irq |
释放中断服务 |
完全兼容 |
不涉及 |
20 |
interrupt.h |
request_irq |
注册中断服务 |
完全兼容 |
不涉及 |
21 |
io.h |
readb |
从I/O读取1字节 |
完全兼容 |
不涉及 |
22 |
io.h |
readl |
从I/O读取4字节 |
完全兼容 |
不涉及 |
23 |
io.h |
readw |
从I/O读取2字节 |
完全兼容 |
不涉及 |
24 |
io.h |
writeb |
向I/O写入1字节 |
完全兼容 |
不涉及 |
25 |
io.h |
writel |
向I/O写入4字节 |
完全兼容 |
不涉及 |
26 |
io.h |
writew |
向I/O写入2字节 |
完全兼容 |
不涉及 |
27 |
jiffies.h |
get_jiffies_64 |
获取自系统启动以来的滴答数 |
完全兼容 |
不涉及 |
28 |
jiffies.h |
jiffies_to_msecs |
将滴答数转换成毫秒 |
功能完全兼容,参数范围限制 |
不涉及 |
29 |
jiffies.h |
msecs_to_jiffies |
将毫秒转换成滴答数 |
不完全兼容,支持频率有限制 |
不涉及 |
30 |
kernel.h |
div_s64 |
除法运算 |
完全兼容 |
不涉及 |
31 |
kernel.h |
div_s64_rem |
除法运算 |
完全兼容 |
保证入参合法 |
32 |
kernel.h |
div_u64 |
除法运算 |
完全兼容 |
不涉及 |
33 |
kernel.h |
div_u64_rem |
除法运算 |
完全兼容 |
保证入参合法 |
34 |
kernel.h |
div64_s64 |
除法运算 |
完全兼容 |
不涉及 |
35 |
kernel.h |
div64_u64 |
除法运算 |
完全兼容 |
不涉及 |
36 |
kernel.h |
div64_u64_rem |
除法运算 |
完全兼容 |
保证入参合法 |
37 |
kernel.h |
do_div_imp |
除法运算 |
完全兼容 |
不涉及 |
38 |
kernel.h |
do_div_s64_imp |
除法运算 |
完全兼容 |
不涉及 |
39 |
kernel.h |
ERR_PTR |
错误码操作 |
完全兼容 |
不涉及 |
40 |
kernel.h |
IS_ERR |
错误码判断 |
完全兼容 |
不涉及 |
41 |
kernel.h |
PTR_ERR |
错误码操作 |
完全兼容 |
不涉及 |
42 |
list.h |
LIST_HEAD_INIT |
初始化双链表(非指针) |
完全兼容 |
不涉及 |
43 |
list.h |
LIST_HEAD |
声明并初始化链表头 |
完全兼容 |
不涉及 |
44 |
list.h |
INIT_LIST_HEAD |
初始化双链表(指针) |
不完全兼容,变更为宏或内联 |
保证入参合法 |
45 |
list.h |
list_add |
在节点后添加新节点 |
完全兼容 |
保证入参合法 |
46 |
list.h |
list_add_tail |
在节点前添加新节点 |
完全兼容 |
保证入参合法 |
47 |
list.h |
list_is_last |
判断是否为尾节点 |
完全兼容 |
保证入参合法 |
48 |
list.h |
list_empty |
判断链表是否为空 |
完全兼容 |
保证入参合法 |
49 |
list.h |
list_entry |
获取结构体地址 |
完全兼容 |
保证入参合法 |
50 |
list.h |
list_first_entry |
获取首节点结构体指针 |
完全兼容 |
保证入参合法 |
51 |
list.h |
list_for_each |
遍历链表 |
完全兼容 |
保证入参合法 |
52 |
list.h |
list_for_each_entry |
遍历链表获取结构体 |
完全兼容 |
保证入参合法 |
53 |
list.h |
list_for_each_entry_reverse |
反向遍历获取结构体 |
完全兼容 |
保证入参合法 |
54 |
list.h |
list_for_each_entry_safe |
安全遍历(带删除) |
完全兼容 |
保证入参合法 |
55 |
list.h |
list_for_each_safe |
安全遍历链表 |
完全兼容 |
保证入参合法 |
56 |
list.h |
list_del |
删除节点 |
完全兼容 |
保证入参合法 |
57 |
list.h |
list_move |
移动节点到另一链表 |
完全兼容 |
保证入参合法 |
58 |
list.h |
list_del_init |
删除并重新初始化 |
完全兼容 |
保证入参合法 |
59 |
platform_device.h |
platform_device_register |
注册设备 |
完全兼容 |
不涉及 |
60 |
platform_device.h |
platform_device_unregister |
注销设备 |
完全兼容 |
不涉及 |
61 |
platform_device.h |
platform_driver_register |
注册驱动 |
完全兼容 |
不涉及 |
62 |
platform_device.h |
platform_driver_unregister |
注销驱动 |
不完全兼容,返回值不同 |
不涉及 |
63 |
platform_device.h |
platform_get_drvdata |
获取驱动私有数据 |
完全兼容 |
不涉及 |
64 |
platform_device.h |
platform_get_irq |
获取设备中断号 |
完全兼容 |
不涉及 |
65 |
platform_device.h |
platform_get_resource |
获取设备资源 |
完全兼容 |
不涉及 |
66 |
platform_device.h |
platform_ioremap_resource |
获取设备内存基址 |
完全兼容 |
不涉及 |
67 |
platform_device.h |
platform_set_drvdata |
设置驱动私有数据 |
完全兼容 |
不涉及 |
68 |
rtc.h |
is_leap_year |
是否是闰年 |
完全兼容 |
不涉及 |
69 |
rtc.h |
rtc_time_to_tm |
绝对时间转年月日 |
完全兼容 |
不涉及 |
70 |
rtc.h |
rtc_tm_to_time |
年月日转绝对时间 |
完全兼容 |
不涉及 |
71 |
rwsem.h |
DECLARE_RWSEM |
定义并初始化读写锁 |
完全兼容 |
不涉及 |
72 |
rwsem.h |
init_rwsem |
初始化读写锁 |
完全兼容 |
不涉及 |
73 |
rwsem.h |
down_read |
获取读锁(睡眠) |
完全兼容 |
不涉及 |
74 |
rwsem.h |
down_read_trylock |
尝试获取读锁 |
完全兼容 |
不涉及 |
75 |
rwsem.h |
down_write |
获取写锁(睡眠) |
完全兼容 |
不涉及 |
76 |
rwsem.h |
down_write_trylock |
尝试获取写锁 |
完全兼容 |
不涉及 |
77 |
rwsem.h |
up_read |
释放读锁 |
完全兼容 |
不涉及 |
78 |
rwsem.h |
up_write |
释放写锁 |
完全兼容 |
不涉及 |
79 |
rwsem.h |
downgrade_write |
写锁降级为读锁 |
完全兼容 |
不涉及 |
80 |
scatterlist.h |
sg_init_one |
初始化集散序列 |
完全兼容 |
保证入参合法 |
81 |
scatterlist.h |
sg_init_table |
初始化集散表格 |
完全兼容 |
保证入参合法 |
82 |
scatterlist.h |
sg_mark_end |
标记序列结束 |
完全兼容 |
保证入参合法 |
83 |
scatterlist.h |
sg_set_buf |
设置集散序列缓冲区 |
完全兼容 |
保证入参合法 |
84 |
sched.h |
schedule_timeout |
任务延时唤醒 |
完全兼容 |
不涉及 |
85 |
semaphore.h |
DEFINE_SEMAPHORE |
定义并初始化信号量 |
完全兼容 |
不涉及 |
86 |
semaphore.h |
sema_init |
动态初始化信号量 |
不完全兼容,计数值有限制 |
不涉及 |
87 |
semaphore.h |
down |
获取信号量(不可唤醒睡眠) |
不完全兼容,不支持计数值>1 |
不涉及 |
88 |
semaphore.h |
down_interruptible |
获取信号量(可中断睡眠) |
不完全兼容,不支持计数值>1 |
不涉及 |
89 |
semaphore.h |
down_trylock |
尝试获取信号量 |
不完全兼容,不支持计数值>1 |
不涉及 |
90 |
semaphore.h |
up |
释放信号量 |
不完全兼容,不支持计数值>1 |
不涉及 |
91 |
semaphore.h |
sema_destory |
销毁信号量 |
非标准接口 |
不涉及 |
92 |
slab.h |
kfree |
释放内存 |
完全兼容 |
不涉及 |
93 |
slab.h |
kmalloc |
分配内存 |
完全兼容 |
不涉及 |
94 |
slab.h |
kzalloc |
分配并清零内存 |
完全兼容 |
不涉及 |
95 |
slab.h |
vfree |
释放虚拟内存 |
完全兼容 |
不涉及 |
96 |
slab.h |
vmalloc |
分配虚拟内存 |
完全兼容 |
不涉及 |
97 |
spinlock_types.h |
DEFINE_SPINLOCK |
定义并初始化自旋锁 |
完全兼容 |
不涉及 |
98 |
spinlock.h |
spin_lock_init |
动态初始化自旋锁 |
完全兼容 |
不涉及 |
99 |
spinlock.h |
spin_lock |
获取自旋锁 |
完全兼容 |
不涉及 |
100 |
spinlock.h |
spin_unlock |
释放自旋锁 |
完全兼容 |
不涉及 |
101 |
spinlock.h |
spin_lock_irqsave |
关中断并加锁 |
完全兼容 |
不涉及 |
102 |
spinlock.h |
spin_unlock_irqrestore |
开中断并解锁 |
完全兼容 |
不涉及 |
103 |
string.h |
strlcpy |
拷贝字符串 |
完全兼容 |
不涉及 |
104 |
timer.h |
add_timer |
添加定时器 |
功能完全兼容,参数有限制 |
不涉及 |
105 |
timer.h |
del_timer |
删除定时器 |
完全兼容 |
不涉及 |
106 |
timer.h |
del_timer_sync |
同步删除定时器 |
完全兼容 |
不涉及 |
107 |
timer.h |
init_timer |
初始化定时器 |
完全兼容 |
不涉及 |
108 |
timer.h |
mod_timer |
修改定时器到期时间 |
完全兼容 |
不涉及 |
109 |
wait.h |
init_waitqueue_head |
初始化等待队列头 |
完全兼容 |
不涉及 |
110 |
wait.h |
wait_event |
等待事件 |
完全兼容 |
不涉及 |
111 |
wait.h |
wait_event_interruptible |
可中断等待事件 |
不完全兼容,不可被信号中断 |
不涉及 |
112 |
wait.h |
wait_event_interruptible_timeout |
带超时的等待事件 |
不完全兼容,入参与返回值差异 |
不涉及 |
113 |
wait.h |
waitqueue_active |
检查等待队列是否活跃 |
完全兼容 |
不涉及 |
114 |
wait.h |
wake_up |
唤醒任务 |
完全兼容 |
不涉及 |
115 |
wait.h |
wake_up_interruptible |
唤醒可中断任务 |
不完全兼容,无signal唤醒 |
不涉及 |
116 |
wait.h |
wake_up_interruptible_poll |
唤醒任务 |
完全兼容 |
不涉及 |
117 |
wait.h |
DECLARE_WAIT_QUEUE_HEAD |
定义并初始化等待队列头 |
完全兼容 |
不涉及 |
118 |
workqueue.h |
cancel_delayed_work |
取消延迟工作 |
完全兼容 |
不涉及 |
119 |
workqueue.h |
cancel_delayed_work_sync |
同步取消延迟工作 |
不完全兼容,返回值逻辑有别 |
不涉及 |
120 |
workqueue.h |
cancel_work_sync |
同步取消工作 |
不完全兼容,状态判断逻辑有别 |
不涉及 |
121 |
workqueue.h |
create_singlethread_workqueue |
创建单线程工作队列 |
完全兼容 |
不涉及 |
122 |
workqueue.h |
create_workqueue |
创建工作队列 |
不完全兼容,CPU创建逻辑不同 |
不涉及 |
123 |
workqueue.h |
destroy_workqueue |
销毁工作队列 |
不完全兼容,带参数检测 |
不涉及 |
124 |
workqueue.h |
flush_delayed_work |
刷新延迟工作 |
完全兼容 |
不涉及 |
125 |
workqueue.h |
flush_work |
刷新工作 |
完全兼容 |
不涉及 |
126 |
workqueue.h |
INIT_DELAYED_WORK |
初始化延迟工作项 |
完全兼容 |
不涉及 |
127 |
workqueue.h |
INIT_WORK |
初始化工作项 |
完全兼容 |
不涉及 |
128 |
workqueue.h |
queue_delayed_work |
提交延迟工作项 |
完全兼容 |
不涉及 |
129 |
workqueue.h |
queue_work |
提交工作项 |
不完全兼容,带参数检测 |
不涉及 |
130 |
workqueue.h |
schedule_delayed_work |
调度延迟工作项 |
不完全兼容,带参数检测 |
不涉及 |
131 |
workqueue.h |
schedule_work |
调度工作项 |
不完全兼容,带参数检测 |
不涉及 |
132 |
workqueue.h |
work_busy |
检查工作项状态 |
不完全兼容,带参数检测 |
不涉及 |
说明: 完全兼容表示功能与linux一致,错误码返回值要以实际代码为准,部分兼容表示部分功能与linux一致。
linux不支持接口概览¶
LiteOS的linux适配接口中,有一些未支持,包括但不限于下表所示。
头文件 |
接口名 |
说明 |
兼容性 |
|---|---|---|---|
bug.h |
BUG |
提供断言并输出信息 |
不支持 |
compiler.h |
likely |
判断语句,值为真可能性更大 |
不支持 |
compiler.h |
unlikely |
判断语句,值为假可能性更大 |
不支持 |
fb.h |
fb_alloc_cmap |
分配颜色表内存空间 |
不支持 |
fb.h |
fb_cmap_to_user |
复制颜色表 |
不支持 |
fb.h |
fb_copy_cmap |
复制颜色表 |
不支持 |
fb.h |
fb_dealloc_cmap |
释放颜色表内存空间 |
不支持 |
fb.h |
fb_default_cmap |
设置默认颜色表 |
不支持 |
fb.h |
fb_pan_display |
刷新操作屏 |
不支持 |
fb.h |
fb_set_cmap |
设定颜色表 |
不支持 |
fb.h |
fb_set_user_cmap |
设定颜色表 |
不支持 |
fb.h |
fb_set_var |
设置fbinfo显示模式和可变参数 |
不支持 |
fb.h |
framebuffer_alloc |
向内核申请一块空间 |
不支持 |
fb.h |
framebuffer_release |
向内核释放一块空间 |
不支持 |
fb.h |
register_framebuffer |
将fbinfo结构注册到内核 |
不支持 |
fb.h |
unregister_framebuffer |
注销fbinfo |
不支持 |
kernel.h |
copy_from_user |
将用户空间的数据传送到内核空间 |
不支持 |
kernel.h |
copy_to_user |
从内核区中读取数据到用户区 |
不支持 |
kernel.h |
ioremap_cached |
内核虚拟地址映射 |
不支持 |
kernel.h |
ioremap_nocache |
内核虚拟地址映射 |
不支持 |
kernel.h |
iounmap |
取消ioremap函数所做的映射 |
不支持 |
module.h |
module_put |
减少模块使用计数 |
不支持 |
module.h |
try_module_get |
增加模块使用计数 |
不支持 |
pm.h |
dpm_resume_end |
resume所有非系统设备,即执行所有注册设备的resume回调函数 |
不支持 |
pm.h |
dpm_suspend_start |
suspend所有非系统设备,即调用所有注册设备的suspend回调函数 |
不支持 |
sched.h |
cond_resched |
调度一个新程序投入运行 |
不支持 |
sched.h |
signal_pending |
检查当前进程是否有信号处理 |
不支持 |
semaphore.h |
init_MUTEX |
初始化信号量值为1 |
不支持 |
semaphore.h |
init_MUTEX_LOCKED |
初始化信号量值为0 |
不支持 |
string.h |
simple_strtol |
将一个字符串转换成unsigend long long型数据 |
不支持 |
suspend.h |
pm_resume |
用于resume系统 |
不支持 |
suspend.h |
pm_suspend |
用于挂起系统 |
不支持 |
timer.h |
timer_pending |
判断一个处在定时器管理队列中的定时器对象是否已经被调度执行 |
不支持 |
wait.h |
add_wait_queue |
将进程插入队列尾部 |
不支持 |
wait.h |
DECLARE_WAITQUEUE |
定义并初始化一个等待队列 |
不支持 |
wait.h |
remove_wait_queue |
将等待队列wait从附属的等待队列头q指向的等待队列链表中移除 |
不支持 |
wakelock.h |
wake_lock |
激活锁 |
不支持 |
wakelock.h |
wake_lock_active |
判断锁当前是否有效 |
不支持 |
wakelock.h |
wake_lock_init |
初始化一个锁 |
不支持 |
wakelock.h |
wake_unlock |
解锁使之成为无效锁 |
不支持 |
RT-Thread适配¶
本部分简要介绍LiteOS适配RT-Thread v5.2.1接口的情况。
RT-Thread适配接口¶
接口名 |
类型 |
描述 |
|---|---|---|
rt_thread_init |
线程管理 |
静态初始化线程句柄及用户提供的栈空间 |
rt_thread_create |
线程管理 |
从堆中动态分配空间并创建新线程 |
rt_thread_startup |
线程管理 |
启动线程,将其加入系统的就绪队列 |
rt_thread_detach |
线程管理 |
脱离(卸载)一个静态定义的线程对象 |
rt_thread_delete |
线程管理 |
彻底删除动态创建的线程并回收内存 |
rt_thread_self |
线程管理 |
获取当前正在运行的线程句柄 |
rt_thread_yield |
线程管理 |
线程自愿放弃CPU,让同优先级线程运行 |
rt_thread_sleep |
线程管理 |
线程挂起指定的系统时钟节拍(Tick)数 |
rt_thread_delay |
线程管理 |
线程延时指定节拍(功能同sleep) |
rt_thread_mdelay |
线程管理 |
线程延时指定的毫秒(ms)数 |
rt_thread_control |
线程管理 |
更改或获取线程的动态运行参数 |
rt_thread_suspend |
线程管理 |
将指定线程挂起,使其进入阻塞态 |
rt_thread_resume |
线程管理 |
恢复被挂起的线程至就绪态 |
rt_schedule |
线程管理 |
强制立即执行一次系统任务调度 |
rt_enter_critical |
线程管理 |
进入临界区,锁定调度器防止线程切换 |
rt_exit_critical |
线程管理 |
退出临界区,解锁调度器 |
rt_critical_level |
线程管理 |
查询当前临界区嵌套的深度等级 |
rt_thread_idle_gethandler |
线程管理 |
获取系统空闲线程的句柄对象 |
rt_tick_get |
时钟管理 |
获取系统自启动以来的总时钟节拍数 |
rt_tick_increase |
时钟管理 |
增加系统Tick数(通常在时钟中断调用) |
rt_tick_from_millisecond |
时钟管理 |
将毫秒时间值换算为对应的系统节拍数 |
rt_timer_init |
定时器管理 |
静态初始化内核定时器对象 |
rt_timer_detach |
定时器管理 |
卸载静态定义的定时器对象 |
rt_timer_create |
定时器管理 |
动态创建并初始化一个软件定时器 |
rt_timer_delete |
定时器管理 |
删除动态定时器并回收其内存空间 |
rt_timer_start |
定时器管理 |
正式启动指定的定时器计数 |
rt_timer_stop |
定时器管理 |
强制停止正在运行的定时器 |
rt_timer_control |
定时器管理 |
修改定时器的超时阈值或触发模式 |
rt_interrupt_enter |
中断管理 |
中断服务函数入口标记,管理中断嵌套 |
rt_interrupt_leave |
中断管理 |
中断服务函数出口标记,触发必要调度 |
rt_interrupt_get_nest |
中断管理 |
获取当前中断嵌套的总层数 |
rt_hw_interrupt_disable |
中断管理 |
执行底层关中断指令,保存CPU状态 |
rt_hw_interrupt_enable |
中断管理 |
执行底层开中断指令,恢复CPU状态 |
rt_hw_interrupt_init |
中断管理 |
底层硬件中断控制器初始化 |
rt_hw_interrupt_mask |
中断管理 |
屏蔽指定的硬件中断源 |
rt_hw_interrupt_umask |
中断管理 |
取消屏蔽指定的硬件中断源 |
rt_hw_interrupt_install |
中断管理 |
绑定中断向量号与服务函数 |
rt_hw_interrupt_get_irq |
中断管理 |
获取当前正在处理的中断号 |
rt_hw_interrupt_ack |
中断管理 |
向中断控制器发送中断确认信号 |
rt_hw_interrupt_set_pending |
中断管理 |
手动设置中断挂起状态 |
rt_hw_interrupt_get_pending |
中断管理 |
获取当前中断的挂起状态 |
rt_hw_interrupt_clear_pending |
中断管理 |
清除指定中断的挂起标志 |
rt_hw_interrupt_set_priority |
中断管理 |
设置特定中断源的优先级 |
rt_hw_interrupt_get_priority |
中断管理 |
获取特定中断源的优先级 |
rt_hw_interrupt_set_priority_mask |
中断管理 |
设置全局中断优先级遮罩 |
rt_hw_interrupt_get_priority_mask |
中断管理 |
获取全局中断优先级遮罩值 |
rt_sem_init |
信号量 |
静态初始化信号量控制块 |
rt_sem_detach |
信号量 |
卸载静态定义的信号量对象 |
rt_sem_create |
信号量 |
动态分配并创建信号量 |
rt_sem_delete |
信号量 |
销毁动态信号量并释放内存 |
rt_sem_take |
信号量 |
获取(锁定)信号量,支持阻塞 |
rt_sem_trytake |
信号量 |
非阻塞式尝试获取信号量 |
rt_sem_release |
信号量 |
释放信号量并唤醒等待线程 |
rt_mutex_init |
互斥量 |
静态初始化互斥锁对象 |
rt_mutex_detach |
互斥量 |
卸载静态定义的互斥锁 |
rt_mutex_create |
互斥量 |
动态分配并创建互斥锁 |
rt_mutex_delete |
互斥量 |
销毁动态互斥锁并释放内存 |
rt_mutex_take |
互斥量 |
获取互斥锁,支持优先级继承 |
rt_mutex_release |
互斥量 |
释放互斥锁并恢复持有者优先级 |
rt_event_init |
事件 |
静态初始化多事件同步对象 |
rt_event_detach |
事件 |
卸载静态定义的事件对象 |
rt_event_create |
事件 |
动态分配并创建事件控制块 |
rt_event_delete |
事件 |
销毁动态事件对象 |
rt_event_send |
事件 |
发送指定的事件标志位 |
rt_event_recv |
事件 |
接收事件标志,支持逻辑等待条件 |
rt_mb_init |
邮箱 |
静态初始化线程间通信邮箱 |
rt_mb_detach |
邮箱 |
卸载静态定义的邮箱对象 |
rt_mb_create |
邮箱 |
动态分配并创建邮箱空间 |
rt_mb_delete |
邮箱 |
销毁动态邮箱并释放内存 |
rt_mb_send |
邮箱 |
向邮箱投递一封4字节邮件 |
rt_mb_send_wait |
邮箱 |
投递邮件,支持队满时阻塞等待 |
rt_mb_recv |
邮箱 |
从邮箱中读取并取出一封邮件 |
rt_mq_init |
消息队列 |
静态初始化消息队列对象 |
rt_mq_detach |
消息队列 |
卸载静态定义的消息队列 |
rt_mq_create |
消息队列 |
动态分配并创建消息队列空间 |
rt_mq_delete |
消息队列 |
销毁动态队列并释放内存 |
rt_mq_send |
消息队列 |
向队列发送一条自定义长度消息 |
rt_mq_send_wait |
消息队列 |
发送消息,支持队列满时限时等待 |
rt_mq_urgent |
消息队列 |
发送紧急消息,将其排在队首 |
rt_mq_recv |
消息队列 |
从消息队列中接收一条消息内容 |
rt_malloc |
动态内存管理 |
从系统堆中分配连续内存块 |
rt_realloc |
动态内存管理 |
重新调整已分配内存块的大小 |
rt_calloc |
动态内存管理 |
分配内存并自动清零数据位 |
rt_malloc_align |
动态内存管理 |
分配按指定字节对齐的内存块 |
rt_free |
动态内存管理 |
释放动态分配的内存块并归还堆 |
rt_free_align |
动态内存管理 |
释放对齐分配的内存块 |
rt_memheap_init |
动态内存管理 |
初始化多个内存堆管理对象 |
rt_mp_init |
内存池管理 |
静态初始化固定内存块池 |
rt_mp_detach |
内存池管理 |
卸载静态定义的内存池 |
rt_mp_create |
内存池管理 |
动态创建并分配固定内存块池 |
rt_mp_delete |
内存池管理 |
销毁动态内存池并释放资源 |
rt_mp_alloc |
内存池管理 |
从内存池中申请一个固定大小的块 |
rt_mp_free |
内存池管理 |
将内存块释放回对应的内存池 |
rt_thread_idle_sethook |
系统钩子 |
注册空闲线程执行时的钩子函数 |
rt_thread_idle_delhook |
系统钩子 |
删除已注册的空闲线程钩子函数 |
rt_interrupt_enter_sethook |
系统钩子 |
设置进入中断时的审计钩子函数 |
rt_interrupt_leave_sethook |
系统钩子 |
设置退出中断时的审计钩子函数 |
rt_malloc_sethook |
系统钩子 |
设置内存分配成功的钩子函数 |
rt_free_sethook |
系统钩子 |
设置内存释放成功的钩子函数 |
rt_mp_alloc_sethook |
系统钩子 |
设置从内存池分配块的钩子函数 |
rt_mp_free_sethook |
系统钩子 |
设置释放内存池块的钩子函数 |
rt_scheduler_sethook |
系统钩子 |
设置系统任务切换的钩子函数 |
rt_thread_suspend_sethook |
系统钩子 |
设置线程挂起操作的钩子函数 |
rt_thread_resume_sethook |
系统钩子 |
设置线程恢复操作的钩子函数 |
rt_thread_inited_sethook |
系统钩子 |
设置线程初始化完成的钩子函数 |
rt_strstr |
字符串操作 |
在字符串中查找子字符串位置 |
rt_strcasecmp |
字符串操作 |
忽略大小写比较两个字符串 |
rt_strncpy |
字符串操作 |
拷贝指定长度的字符串内容 |
rt_strncmp |
字符串操作 |
比较指定长度的两个字符串 |
rt_strcmp |
字符串操作 |
比较两个字符串的字面值 |
rt_strnlen |
字符串操作 |
计算指定长度限制内的字符串长度 |
rt_strlen |
字符串操作 |
计算以空字符结尾的字符串长度 |
rt_strdup |
字符串操作 |
复制并动态分配一个字符串副本 |
rt_snprintf |
字符串操作 |
安全地格式化字符串至缓冲区 |
rt_vsprintf |
字符串操作 |
使用变量参数列表格式化字符串 |
rt_sprintf |
字符串操作 |
格式化并打印字符串至缓冲区 |
rt_memset |
内存操作 |
使用特定字节填充内存区域 |
rt_memcpy |
内存操作 |
在内存区域间执行高效拷贝 |
rt_memmove |
内存操作 |
处理重叠区域的安全内存拷贝 |
rt_memcmp |
内存操作 |
比较两块内存区域的内容差异 |
rt_show_version |
内核基础服务 |
在控制台显示内核版本及版权信息 |
rt_console_get_device |
内核基础服务 |
获取当前控制台使用的设备句柄 |
rt_console_set_device |
内核基础服务 |
设置系统控制台使用的通信设备 |
rt_kputs |
内核基础服务 |
内核级基础字符串输出函数 |
rt_kprintf |
内核基础服务 |
内核专用格式化打印输出函数 |
__rt_ffs |
内核基础服务 |
快速查找二进制位中第一个1的位置 |
rt_get_errno |
错误代码管理 |
获取当前线程最近一次发生的错误码 |
rt_set_errno |
错误代码管理 |
设置当前线程的全局错误代码 |
rt_atomic_load |
原子操作 |
执行硬件级不可中断的读取操作 |
rt_atomic_store |
原子操作 |
执行硬件级不可中断的写入操作 |
rt_atomic_exchange |
原子操作 |
执行硬件级不可中断的数据交换 |
rt_atomic_add |
原子操作 |
执行原子加法运算 |
rt_atomic_sub |
原子操作 |
执行原子减法运算 |
rt_atomic_xor |
原子操作 |
执行原子异或运算 |
rt_atomic_and |
原子操作 |
执行原子与运算 |
rt_atomic_or |
原子操作 |
执行原子或运算 |
rt_atomic_flag_test_and_set |
原子操作 |
测试并设置原子标志位 |
rt_atomic_flag_clear |
原子操作 |
清除原子标志位状态 |
rt_atomic_compare_exchange_strong |
原子操作 |
强原子比较并交换操作 |
rt_wqueue_wait |
等待队列 |
将线程挂起在指定等待队列上 |
rt_wqueue_wakeup |
等待队列 |
唤醒等待队列上的指定线程 |
rt_workqueue_destroy |
工作队列 |
销毁指定的工作队列及其线程 |
rt_workqueue_dowork |
工作队列 |
立即执行工作队列中的任务项 |
rt_workqueue_cancel_work |
工作队列 |
取消尚未执行的工作任务项 |
rt_workqueue_cancel_work_sync |
工作队列 |
同步取消并等待工作项完成 |
rt_work_init |
工作队列 |
初始化一个异步工作项任务 |
socket |
套接字 |
创建一个兼容BSD标准的网络套接字 |
bind |
套接字 |
绑定套接字至特定的地址和端口 |
listen |
套接字 |
设置套接字进入监测状态 |
accept |
套接字 |
接受一个远端套接字的连接请求 |
connect |
套接字 |
发起与远程服务器的网络连接 |
send |
套接字 |
通过套接字发送网络数据内容 |
recv |
套接字 |
从套接字中接收网络数据内容 |
sendto |
套接字 |
向指定地址发送UDP数据报文 |
recvfrom |
套接字 |
从指定地址接收UDP数据报文 |
closesocket |
套接字 |
关闭套接字并释放相关资源 |
shutdown |
套接字 |
断开套接字的发送或接收通道 |
setsockopt |
套接字 |
设置套接字的网络通信参数 |
getsockopt |
套接字 |
获取套接字的网络通信参数 |
getpeername |
套接字 |
获取与套接字连接的对端地址 |
getsockname |
套接字 |
获取套接字本地绑定的地址 |
ioctlsocket |
套接字 |
执行套接字的输入输出控制操作 |
gethostbyname |
套接字 |
通过域名解析获取主机的 IP 地址 |
rt_ringbuffer_init |
环形缓冲区 |
静态初始化环形缓冲区结构 |
rt_ringbuffer_reset |
环形缓冲区 |
重置环形缓冲区,清除所有数据 |
rt_ringbuffer_put |
环形缓冲区 |
向环形缓冲区压入指定数据内容 |
rt_ringbuffer_put_force |
环形缓冲区 |
强制存入数据(覆盖旧数据) |
rt_ringbuffer_putchar |
环形缓冲区 |
向缓冲区存入单个字符 |
rt_ringbuffer_putchar_force |
环形缓冲区 |
强制存入单个字符 |
rt_ringbuffer_get |
环形缓冲区 |
从环形缓冲区提取并读出数据 |
rt_ringbuffer_getchar |
环形缓冲区 |
从缓冲区读出单个字符 |
rt_ringbuffer_data_len |
环形缓冲区 |
获取缓冲区中当前已存储的数据长度 |
rt_ringbuffer_create |
环形缓冲区 |
动态创建并分配环形缓冲区空间 |
rt_ringbuffer_destroy |
环形缓冲区 |
销毁动态缓冲区并释放内存 |
rt_ringbuffer_get_size |
环形缓冲区 |
获取环形缓冲区的总容量大小 |
rt_rbb_init |
环形块状缓冲区 |
静态初始化块状缓冲区管理结构 |
rt_rbb_create |
环形块状缓冲区 |
动态创建环形块状缓冲区对象 |
rt_rbb_destroy |
环形块状缓冲区 |
销毁块状缓冲区并释放资源 |
rt_rbb_get_buf_size |
环形块状缓冲区 |
获取块状缓冲区的内部总长度 |
rt_rbb_blk_alloc |
环形块状缓冲区 |
从缓冲区分配一个数据块句柄 |
rt_rbb_blk_put |
环形块状缓冲区 |
将数据块标记为已就绪状态 |
rt_rbb_blk_get |
环形块状缓冲区 |
从缓冲区获取已就绪的数据块 |
rt_rbb_blk_free |
环形块状缓冲区 |
释放数据块并回收缓冲区空间 |
rt_rbb_blk_queue_get |
环形块状缓冲区 |
从块队列中获取一个数据块 |
rt_rbb_blk_queue_len |
环形块状缓冲区 |
获取当前队列中块的数量 |
rt_rbb_blk_queue_buf |
环形块状缓冲区 |
获取块队列关联的缓冲区地址 |
rt_rbb_blk_queue_free |
环形块状缓冲区 |
释放整个块队列相关的资源 |
rt_rbb_next_blk_queue_len |
环形块状缓冲区 |
获取下一个就绪块的数据长度 |
rt_completion_init |
完成信号量 |
静态初始化单次同步完成量 |
rt_completion_wait |
完成信号量 |
阻塞线程直至收到任务完成信号 |
rt_completion_done |
完成信号量 |
发送完成信号,唤醒等待的线程 |
rt_pipe_open |
管道 |
打开指定的流式通信管道 |
rt_pipe_close |
管道 |
关闭已打开的流式通信管道 |
rt_pipe_read |
管道 |
从管道中读取流式数据内容 |
rt_pipe_write |
管道 |
向管道中写入流式数据内容 |
rt_pipe_create |
管道 |
创建用于线程间通信的虚拟管道 |
rt_pipe_delete |
管道 |
删除指定的通信管道并释放资源 |
ulog_init |
日志组件 |
初始化ULOG日志管理框架 |
ulog_deinit |
日志组件 |
反初始化并关闭日志框架 |
ulog_flush |
日志组件 |
强制刷新并输出日志缓冲区内容 |
ulog_hexdump |
日志组件 |
以十六进制格式打印内存数据日志 |
ulog_raw |
日志组件 |
输出不带日志头格式的原始数据 |
dlopen |
动态模块 |
打开并加载一个动态库模块 |
dlsym |
动态模块 |
在动态库中查找特定符号的地址 |
dlclose |
动态模块 |
关闭并卸载指定的动态库模块 |
rt_spin_unlock_irqrestore |
自旋锁与多核 |
释放自旋锁并恢复之前的中断状态 |
rt_cpu_self |
自旋锁与多核 |
获取当前执行代码的CPU核心对象 |
rt_cpu_index |
自旋锁与多核 |
获取当前CPU核心的逻辑索引号 |
RT-Thread接口适配差异¶
考虑接口的易用性和LiteOS内部机制与RT-Thread标准接口的差异,在适配RT-Thread接口时,对部分接口进行了修改,详见下表。
接口名 |
类型 |
描述 |
|---|---|---|
INIT_EXPORT |
初始化 |
不支持RT-Thread自带的INIT_EXPORT,如果想系统启动自动初始化函数,可参考LiteOS的宏 LOS_SYS_INIT。 |
rt_mq_init |
消息队列 |
不支持RT_IPC_FLAG_PRIO模式。 |
rt_mq_create |
消息队列 |
不支持RT_IPC_FLAG_PRIO模式。 |
object相关接口 |
object |
暂不支持。 |
rt_spin_lock |
自旋锁 |
使用全局锁。 |
rt_spin_unlock |
自旋锁 |
使用全局锁。 |
rt_spin_lock_irqsave |
自旋锁 |
使用全局锁。 |
rt_spin_unlock_irqrestore |
自旋锁 |
使用全局锁。 |
rt_cpu_self |
自旋锁 |
仅支持单核。 |
rt_workqueue_cancel_work_sync |
工作队列 |
不支持同步等待机制。 |
rt_event_init |
事件 |
接口中的参数name实际并未使用。 |
rt_event_create |
事件 |
接口中的参数name实际并未使用。 |
rt_timer_init |
定时器 |
接口中的参数name实际并未使用。 |
rt_timer_control |
定时器 |
相比原生接口,新增支持命令RT_TIMER_CTRL_GET_STATE(获取定时器状态)、RT_TIMER_CTRL_GET_REMAIN_TIME(获得软件定时器剩余Tick数)。 |
rt_sem_init |
信号量 |
不支持PRIO。 |
rt_sem_detach |
信号量 |
不支持PRIO。 |
rt_mq_init |
消息队列 |
不支持PRIO。 |
rt_mq_create |
消息队列 |
不支持PRIO。 |
rt_mutex_init |
互斥锁 |
接口中的参数flag实际并未使用。 |
rt_mutex_create |
互斥锁 |
接口中的参数flag实际并未使用。 |
涉及任务优先级相关接口 |
任务 |
在RT-Thread里优先级级别是根据数字从大到小排序,在LiteOS里是从小到大排序。 |
FreeRTOS适配¶
本部分简要介绍LiteOS适配FreeRTOS v11.1.0接口的情况。
FreeRTOS适配接口¶
接口名 |
类型 |
描述 |
|---|---|---|
portENTER_CRITICAL |
中断管理 |
进入硬件级临界区,禁止所有可屏蔽中断 |
portEXIT_CRITICAL |
中断管理 |
退出硬件级临界区,恢复中断处理 |
portCLEAR_INTERRUPT_MASK_FROM_ISR |
中断管理 |
在中断服务函数中恢复中断遮罩状态 |
portSET_INTERRUPT_MASK_FROM_ISR |
中断管理 |
在中断服务函数中屏蔽中断并保存当前状态 |
xTaskGetTickCount |
任务管理 |
获取系统启动后的总时钟节拍数(Tick) |
xTaskGetCurrentTaskHandle |
任务管理 |
获取当前正在运行的任务句柄 |
xTaskCreate |
任务管理 |
动态创建一个新任务并分配堆栈 |
xTaskCreateStatic |
任务管理 |
静态初始化任务(使用用户提供的内存) |
vTaskDelete |
任务管理 |
删除一个任务并释放其占用的资源 |
vTaskDelay |
任务管理 |
任务进入阻塞态,延时指定的时钟节拍 |
vTaskDelayUntil |
任务管理 |
任务延时至绝对时间点,用于周期性执行 |
xTaskDelayUntil |
任务管理 |
任务延时至绝对时间点的函数变体 |
uxTaskPriorityGet |
任务管理 |
获取指定任务的当前运行优先级 |
vTaskPrioritySet |
任务管理 |
动态修改指定任务的运行优先级 |
vTaskSuspend |
任务管理 |
挂起指定任务,使其停止运行 |
vTaskResume |
任务管理 |
恢复被挂起的任务进入就绪态 |
xTaskResumeFromISR |
任务管理 |
在中断服务函数中恢复被挂起的任务 |
xTaskAbortDelay |
任务管理 |
强制中止任务的延时或阻塞状态 |
uxTaskPriorityGetFromISR |
任务管理 |
在中断中安全获取任务的优先级 |
uxTaskBasePriorityGet |
任务管理 |
获取任务的基础优先级(不受优先级继承影响) |
uxTaskBasePriorityGetFromISR |
任务管理 |
在中断中获取任务的基础优先级 |
uxTaskGetSystemState |
任务管理 |
获取系统中所有任务的状态快照 |
vTaskGetInfo |
任务管理 |
获取单个任务的详细信息 |
xTaskGetApplicationTaskTag |
任务管理 |
获取与任务关联的标签值 |
uxTaskGetStackHighWaterMark |
任务管理 |
获取任务栈运行时的最小剩余空间 |
xTaskCallApplicationTaskHook |
任务管理 |
调用任务关联的钩子回调函数 |
vTaskSetApplicationTag |
任务管理 |
为任务分配一个应用标签值 |
vTaskSetThreadLocalStoragePointer |
任务管理 |
设置任务的线程本地存储(TLS)指针 |
pvTaskGetThreadLocalStoragePointer |
任务管理 |
获取任务的线程本地存储(TLS)指针 |
vTaskSetTimeOutState |
任务管理 |
初始化一个超时状态结构体 |
xTaskCheckForTimeOut |
任务管理 |
检查任务阻塞是否已达到超时时间 |
eTaskConfirmSleepModeStatus |
任务管理 |
确认系统是否可以进入低功耗睡眠模式 |
taskYIELD |
任务管理 |
强制执行任务切换,让出CPU |
taskENTER_CRITICAL |
任务管理 |
任务级进入临界区(锁定调度器) |
taskEXIT_CRITICAL |
任务管理 |
任务级退出临界区(解锁调度器) |
taskENTER_CRITICAL_FROM_ISR |
任务管理 |
中断级进入临界区安全接口 |
taskEXIT_CRITICAL_FROM_ISR |
任务管理 |
中断级退出临界区安全接口 |
vTaskStartScheduler |
任务管理 |
启动任务调度器,系统开始运行 |
vTaskEndScheduler |
任务管理 |
停止任务调度器运行 |
vTaskSuspendAll |
任务管理 |
挂起调度器,禁止所有任务切换 |
xTaskResumeAll |
任务管理 |
恢复调度器运行 |
vTaskStepTick |
任务管理 |
在低功耗模式唤醒后补偿时钟节拍 |
xTaskCatchUpTicks |
任务管理 |
修正缺失的系统时钟节拍数 |
xTaskNotifyGive |
任务通知 |
向指定任务发送一个简单的通知(增量) |
xTaskNotifyGiveIndexed |
任务通知 |
向指定索引的任务通知组发送通知 |
vTaskNotifyGiveFromISR |
任务通知 |
在中断中向任务发送通知(增量) |
vTaskNotifyGiveIndexedFromISR |
任务通知 |
在中断中向指定索引发送通知 |
ulTaskNotifyTake |
任务通知 |
等待任务通知(类似轻量级信号量) |
ulTaskNotifyTakeIndexed |
任务通知 |
等待指定索引的任务通知 |
xTaskNotify |
任务通知 |
向任务发送带有特定值和动作的通知 |
xTaskNotifyIndexed |
任务通知 |
向任务指定索引发送带值的通知 |
xTaskNotifyAndQuery |
任务通知 |
发送通知并获取任务之前的通知状态 |
xTaskNotifyAndQueryIndexed |
任务通知 |
对指定索引发送通知并查询状态 |
xTaskNotifyAndQueryFromISR |
任务通知 |
中断中发送通知并查询状态的安全接口 |
xTaskNotifyAndQueryIndexedFromISR |
任务通知 |
中断中对指定索引发送通知并查询 |
xTaskNotifyFromISR |
任务通知 |
在中断中向任务发送带值的通知 |
xTaskNotifyIndexedFromISR |
任务通知 |
中断中对指定索引发送通知的安全接口 |
xTaskNotifyWait |
任务通知 |
阻塞等待任务通知到来 |
xTaskNotifyWaitIndexed |
任务通知 |
阻塞等待指定索引的任务通知 |
xTaskNotifyStateClear |
任务通知 |
清除任务的通知挂起状态 |
xTaskNotifyStateClearIndexed |
任务通知 |
清除指定索引的任务通知状态 |
ulTasknotifyValueClear |
任务通知 |
清除任务通知的具体数值 |
ulTaskNotifyValueClearIndexed |
任务通知 |
清除指定索引的任务通知数值 |
uxQueueMessagesWaiting |
队列管理 |
获取队列中当前存储的消息数量 |
uxQueueMessagesWaitingFromISR |
队列管理 |
在中断中获取队列消息数量 |
uxQueueSpacesAvailable |
队列管理 |
获取队列中剩余的可用空闲槽位 |
vQueueDelete |
队列管理 |
删除队列并释放相关内存 |
xQueueCreate |
队列管理 |
动态创建一个新的队列 |
xQueueCreateStatic |
队列管理 |
静态初始化一个队列 |
xQueueGetStaticBuffers |
队列管理 |
获取静态队列使用的缓冲区指针 |
xQueueIsQueueEmptyFromISR |
队列管理 |
在中断中判断队列是否为空 |
xQueueIsQueueFullFromISR |
队列管理 |
在中断中判断队列是否已满 |
xQueueOverwrite |
队列管理 |
向队列写入数据(若满则覆盖旧数据) |
xQueueOverwriteFromISR |
队列管理 |
中断中执行覆盖写入的安全接口 |
xQueueReceive |
队列管理 |
从队列中读取并移除一个消息 |
xQueueReceiveFromISR |
队列管理 |
中断中接收消息的安全接口 |
xQueueReset |
队列管理 |
重置队列至初始空状态 |
xQueueSend |
队列管理 |
向队列尾部发送一个消息 |
xQueueSendFromISR |
队列管理 |
中断中发送消息的安全接口 |
xQueueSendToBack |
队列管理 |
向队列尾部发送消息(同Send) |
xQueueSendToBackFromISR |
队列管理 |
中断中向队尾发送消息的安全接口 |
xQueueSendToFront |
队列管理 |
向队列头部发送消息(插队) |
xQueueSendToFrontFromISR |
队列管理 |
中断中向队头发送消息的安全接口 |
xQueueCreateSet |
队列集 |
创建一个队列集合用于监控多个对象 |
xQueueAddToSet |
队列集 |
将队列或信号量添加到队列集中 |
xQueueRemoveFromSet |
队列集 |
从队列集中移除指定成员 |
xQueueSelectFromSet |
队列集 |
阻塞等待队列集中有对象变为可用 |
xQueueSelectFromSetFromISR |
队列集 |
中断中选择可用队列成员的安全接口 |
xStreamBufferCreate |
流缓冲区 |
动态创建用于字节流传输的缓冲区 |
xStreamBufferCreateStatic |
流缓冲区 |
静态初始化一个流缓冲区 |
xStreamBufferSend |
流缓冲区 |
向流缓冲区写入字节数据 |
xStreamBufferSendFromISR |
流缓冲区 |
中断中写入字节流的安全接口 |
xStreamBufferReceive |
流缓冲区 |
从流缓冲区读取字节数据 |
xStreamBufferReceiveFromISR |
流缓冲区 |
中断中读取字节流的安全接口 |
vStreamBufferDelete |
流缓冲区 |
删除流缓冲区并释放内存 |
xStreamBufferBytesAvailable |
流缓冲区 |
获取当前可读取的字节数 |
xStreamBufferSpacesAvailable |
流缓冲区 |
获取当前可写入的空闲字节数 |
xStreamBufferSetTriggerLevel |
流缓冲区 |
设置解除阻塞所需的最小字节触发量 |
xStreamBufferReset |
流缓冲区 |
清空并重置流缓冲区 |
xStreamBufferResetFromISR |
流缓冲区 |
中断中重置流缓冲区的安全接口 |
xStreamBufferIsEmpty |
流缓冲区 |
判断流缓冲区是否为空 |
xStreamBufferIsFull |
流缓冲区 |
判断流缓冲区是否已满 |
xStreamBufferGetStaticBuffers |
流缓冲区 |
获取静态流缓冲区的内部指针 |
uxStreamBufferGetStreamBufferNotificationIndex |
流缓冲区 |
获取关联的任务通知索引号 |
vStreamBufferSetStreamBufferNotificationIndex |
流缓冲区 |
设置关联的任务通知索引号 |
xStreamBatchingBufferCreate |
流缓冲区 |
创建支持批处理模式的流缓冲区 |
xStreamBufferSendCompletedFromISR |
流缓冲区 |
中断中标记发送完成的通知函数 |
xStreamBufferReceiveCompletedFromISR |
流缓冲区 |
中断中标记接收完成的通知函数 |
xStreamBatchingBufferCreateStatic |
流缓冲区 |
静态创建批处理模式的流缓冲区 |
xMessageBufferCreate |
消息缓冲区 |
动态创建用于变长消息传输的缓冲区 |
xMessageBufferCreateStatic |
消息缓冲区 |
静态初始化一个消息缓冲区 |
xMessageBufferSend |
消息缓冲区 |
向缓冲区发送一条变长消息 |
xMessageBufferSendFromISR |
消息缓冲区 |
中断中发送变长消息的安全接口 |
xMessageBufferReceive |
消息缓冲区 |
从缓冲区接收一条变长消息 |
xMessageBufferReceiveFromISR |
消息缓冲区 |
中断中接收变长消息的安全接口 |
vMessageBufferDelete |
消息缓冲区 |
删除消息缓冲区并释放内存 |
xMessageBufferSpacesAvailable |
消息缓冲区 |
获取缓冲区剩余可用消息空间 |
xMessageBufferReset |
消息缓冲区 |
清空并重置消息缓冲区 |
xMessageBufferResetFromISR |
消息缓冲区 |
中断中重置消息缓冲区的安全接口 |
xMessageBufferIsEmpty |
消息缓冲区 |
判断消息缓冲区是否为空 |
xMessageBufferIsFull |
消息缓冲区 |
判断消息缓冲区是否已满 |
xMessageBufferSendCompletedFromISR |
消息缓冲区 |
中断中标记消息发送完成 |
xMessageBufferReceiveCompletedFromISR |
消息缓冲区 |
中断中标记消息接收完成 |
xMessageBufferGetStaticBuffers |
消息缓冲区 |
获取静态消息缓冲区的内部指针 |
xMessageBufferSpaceAvailable |
消息缓冲区 |
获取消息缓冲区可用的字节空间 |
xSemaphoreCreateBinary |
信号量 |
动态创建一个二值信号量 |
xSemaphoreCreateBinaryStatic |
信号量 |
静态创建一个二值信号量 |
vSemaphoreCreateBinary |
信号量 |
旧版本创建二值信号量的宏 |
xSemaphoreCreateCounting |
信号量 |
动态创建一个计数信号量 |
xSemaphoreCreateCountingStatic |
信号量 |
静态创建一个计数信号量 |
xSemaphoreCreateMutex |
信号量 |
动态创建一个互斥锁(支持优先级继承) |
xSemaphoreCreateMutexStatic |
信号量 |
静态创建一个互斥锁 |
xSemaphoreCreateRecursiveMutex |
信号量 |
动态创建一个递归互斥锁 |
xSemaphoreCreateRecursiveMutexStatic |
信号量 |
静态创建一个递归互斥锁 |
xSemaphoreGetMutexHolder |
信号量 |
获取当前持有互斥锁的任务句柄 |
uxSemaphoreGetCount |
信号量 |
获取信号量的当前计数值 |
xSemaphoreTake |
信号量 |
获取信号量/锁(支持阻塞等待) |
xSemaphoreTakeFromISR |
信号量 |
中断中获取信号量的安全接口 |
xSemaphoreTakeRecursive |
信号量 |
获取递归互斥锁 |
xSemaphoreGive |
信号量 |
释放信号量/锁 |
xSemaphoreGiveFromISR |
信号量 |
中断中释放信号量的安全接口 |
xSemaphoreGiveRecursive |
信号量 |
释放递归互斥锁 |
vSemaphoreDelete |
信号量 |
删除信号量/锁并释放内存 |
xTimerCreate |
软件定时器 |
动态创建一个软件定时器 |
xTimerCreateStatic |
软件定时器 |
静态初始化一个软件定时器 |
xTimerIsTimerActive |
软件定时器 |
判断定时器当前是否正在运行 |
xTimerStart |
软件定时器 |
启动或重新启动定时器计数 |
xTimerStartFromISR |
软件定时器 |
中断中启动定时器的安全接口 |
xTimerStop |
软件定时器 |
停止定时器计数 |
xTimerStopFromISR |
软件定时器 |
中断中停止定时器的安全接口 |
xTimerChangePeriod |
软件定时器 |
动态修改定时器的超时周期 |
xTimerChangePeriodFromISR |
软件定时器 |
中断中修改周期的安全接口 |
xTimerDelete |
软件定时器 |
删除定时器并释放内存 |
xTimerReset |
软件定时器 |
重置定时器的计数值 |
xTimerResetFromISR |
软件定时器 |
中断中重置定时器的安全接口 |
pvTimerGetTimerID |
软件定时器 |
获取分配给定时器的用户ID指针 |
vTimerSetTimerID |
软件定时器 |
设置分配给定时器的用户ID指针 |
vTimerSetReloadMode |
软件定时器 |
设置定时器模式(单次或自动重载) |
xTimerGetTimerID |
软件定时器 |
获取定时器的ID值 |
xTimerGetReloadMode |
软件定时器 |
获取定时器的重载模式设置 |
xTimerPendFunctionCall |
软件定时器 |
将函数推迟到定时器任务中执行 |
xTimerPendFunctionCallFromISR |
软件定时器 |
中断中推迟函数执行的安全接口 |
pcTimerGetName |
软件定时器 |
获取定时器的名称字符串 |
xTimerGetPeriod |
软件定时器 |
获取定时器的周期时间 |
xTimerGetExpiryTime |
软件定时器 |
获取定时器下一次到期的时间点 |
xEventGroupCreate |
事件组 |
动态创建一个事件标志组 |
xEventGroupCreateStatic |
事件组 |
静态创建一个事件标志组 |
vEventGroupDelete |
事件组 |
删除事件组并释放内存 |
xEventGroupWaitBits |
事件组 |
阻塞等待事件组中特定位的组合状态 |
xEventGroupSetBits |
事件组 |
设置(置位)事件组中的特定位 |
xEventGroupSetBitsFromISR |
事件组 |
中断中设置事件位的安全接口 |
xEventGroupClearBits |
事件组 |
清除事件组中的特定位 |
xEventGroupClearBitsFromISR |
事件组 |
中断中清除事件位的安全接口 |
xEventGroupGetBits |
事件组 |
获取当前事件组的所有位标志状态 |
xEventGroupGetBitsFromISR |
事件组 |
中断中获取事件位状态的安全接口 |
xEventGroupSync |
事件组 |
事件组同步(用于多个任务同步点) |
xEventGroupGetStaticBuffer |
事件组 |
获取静态事件组的内部指针 |
pvPortMalloc |
内存管理 |
内核级动态内存分配函数 |
vPortFree |
内存管理 |
释放由内核分配的内存块 |
FreeRTOS接口适配差异¶
考虑接口的易用性和LiteOS内部机制与FreeRTOS标准接口的差异,在适配FreeRTOS接口时,对部分接口进行了修改,详见下表。
接口名 |
类型 |
描述 |
|---|---|---|
portSET_INTERRUPT_MASK_FROM_ISR |
中断管理 |
暂不支持按优先级屏蔽中断,该宏的效果等同于portDISABLE_INTERRUPTS。 |
xQueueAddToSet |
队列集 |
当前接口新增约束:当队列不为空/互斥量被其他线程占用/信号量被其他线程持有时,无法将句柄添加到队列集。 |
xQueueRemoveFromSet |
中断管理 |
当前接口约束补充说明:当队列不为空/互斥量被其他线程占用/信号量被其他线程持有时,无法将句柄从队列集移出。 |
xQueueSelectFromSetFromISR |
中断管理 |
xTicksToWait参数为0 |
xTimerPendFunctionCall |
定时器 |
当前接口改为同步立即执行,依赖异步与任务上下文的应用逻辑。 |
vTimerSetReloadMode |
定时器 |
LiteOS不支持定时器模式的更改,因此该接口不能实际修改定时器模式。 |
xSemaphoreCreateCounting |
信号量 |
FreeRTOS 中“队列长度”在这里用于设置信号量的初始计数(count),而不是最大计数。 |
xTaskGetTickCount |
任务管理 |
自系统启动开始计数,原生是任务启动调度开始计数。 |
xTaskCreate |
任务管理 |
原生FreeRtos不会自动释放任务资源,当前实现会自动释放任务资源。 |
xTaskCreateStatic |
任务管理 |
原生FreeRtos不会自动释放任务资源,当前实现会自动释放任务资源。 |
通知值相关函数 |
任务管理 |
原生实现是将任务阻塞,当前适配机制是将任务挂起。 |
驱动开发指导¶
本章以Hi3556V200为例,使能开发者操作控制与ETH、USB2.0 DRD和SD/MMC/eMMC卡等驱动模块相连的LiteOS外围驱动设备。
说明: Hi3556V200/Hi3559V200无ETH模块。
基于驱动框架的开发指导¶
概述¶
基本概念
如图1所示,驱动开发就是按一定的规格实现硬件功能并进行抽象,提供给开发者一套统一的设备访问接口,方便开发者进行上层业务开发。
驱动设备
系统将所有外部设备看成是一类特殊文件,称为“设备文件”。外部设备可分为三类:
字符设备:设备以字符形式发送和接收数据,不支持随机访问。
块设备:利用一块系统内存作为缓冲区,以整个数据缓冲区的形式传输数据。块设备主要针对Flash等慢速设备,能避免CPU耗费过多的时间等待操作完成。
网络设备:所有对网络硬件的访问都通过接口进行。接口提供一个对所有类型的网络硬件一体化的操作集合,来发送和接收基本数据。
驱动程序
内核与外部设备之间的接口。驱动程序向应用程序屏蔽了硬件在实现上的细节。在应用程序看来,外部设备只是一个设备文件,使应用程序可以像操作普通文件一样来操作外部设备,如open ()、close ()、read ()、write () 等。
驱动框架管理
对驱动设备和驱动程序进行系统管理的模块。
图 1 基本设备驱动框架

运作机制
驱动框架涉及到2个比较重要的数据结构LosDevice(用于描述驱动设备)和LosDriver(用于描述驱动程序)。系统中有两个全局双向链表。一个是全局设备链表,管理挂载的所有驱动设备;另一个是全局驱动链表,管理所有挂载的驱动程序。
这两种数据结构介绍如下:
LosDevice
LosDevice代表一个驱动设备。结构体中重要的成员包括:
const CHAR *name:设备名,用以跟驱动配对。
struct LosDriver *driver:表示该驱动设备的驱动程序。
struct LosDeviceConfig cfg:设备io起始地址、io大小及中断号配置。
LOS_DL_LIST driverNode:对应的驱动节点,用以挂载到该驱动结构体中的deviceList上。
LOS_DL_LIST deviceItem:该驱动设备节点,用以挂载到全局设备链表中。
LosDriver
LosDriver代表一个设备驱动程序。结构体中重要的成员包括:
const CHAR *name:驱动名,用以跟设备配对。
LOS_DL_LIST deviceList:使用当前这个驱动程序的驱动设备的双向链表。
LOS_DL_LIST driverItem:该驱动程序节点,用以挂载到全局驱动链表里。
struct LosDriverOps ops:驱动实现的操作接口,如probe、remove等。
LosDevice和LosDriver之间的关系如图2所示。
图 2 LosDevice和LosDriver的关系图

LosDriver挂在全局驱动链表下,包含了一个deviceList链表,表示这个驱动程序操作(或控制)的所有设备。
LosDevice挂在全局设备链表下,包含了一个driver指针,表示这个设备对应的设备驱动程序。
一个驱动设备只对应一个驱动程序,而一个驱动程序可以支持多个驱动设备。当对应的设备/驱动挂载的时候,会根据名字去全局驱动链表/全局设备链表中寻找已经注册的对应的驱动/设备,执行注册过程。
开发指导¶
功能
LiteOS的驱动框架为用户提供下面几种功能,接口详细信息可以查看API参考。
功能分类 |
接口名 |
描述 |
|---|---|---|
操作驱动设备 |
LOS_DeviceRegister |
注册一个设备,添加到全局设备链表 |
LOS_DeviceUnregister |
注销一个设备,从全局设备链表中移除 |
|
LOS_DeviceDataGet |
获取指定设备的私有数据 |
|
LOS_DeviceRegBaseGet |
获取指定设备的io起始地址 |
|
LOS_DeviceRegSizeGet |
获取指定设备的io大小 |
|
LOS_DeviceIrqNumGet |
获取指定设备的中断号 |
|
注册/注销驱动程序 |
LOS_DriverRegister |
注册设备驱动程序,并和相应的设备绑定 |
LOS_DriverUnregister |
注销设备驱动程序 |
|
低功耗功能 |
LOS_PmSuspend |
挂起所有设备 |
LOS_PmResume |
恢复所有设备 |
开发流程
下面以注册USB驱动为例,讲解基于驱动框架的驱动开发流程。
打开菜单,选择Driver ---> Enable Driver Base,使能驱动框架。
配置项
含义
取值范围
默认值
依赖
LOSCFG_DRIVERS_BASE
使能驱动框架的开关
YES/NO
YES
无
注册驱动设备。
定义USB驱动设备实例。
struct LosDeviceRegs hiudc3_regs[] = { { .base = DWC_USB3_PORT1_BASE_ADDR, .size = DWC_USB3_PORT1_ADDR_OFFSET, }, }; static struct LosDevice hiudc3_device = { .name = "hi_udc3", .id = -1, .cfg = { .irqNum = NUM_HAL_INTERRUPT_USB_DEV, .numRegs = ARRAY_SIZE(hiudc3_regs), .regs = hiudc3_regs, }, };
将驱动设备注册到全局设备链表。
LOS_DeviceRegister(&hiudc3_device);预加载处理。
通过调用“linux/module.h”(在根目录“self_src/compat/linux/include”目录下)里面的预加载挂载函数,将驱动设备注册函数挂载到系统中。当系统启动时,系统会按照挂载函数优先级遍历预加载节点,从而注册驱动设备。系统提供如下预加载挂载函数,优先级从高到低:
pure_initcall(f) //0 core_initcall(f) //1 postcore_initcall(f) //2 arch_initcall(f) //3 subsys_initcall(f) //4 fs_initcall(f) //5 device_initcall(f) //6 late_initcall(f) //7
具体调用请参见“编程实例”。
须知:
执行预加载可能会影响到系统的某些功能,例如,当驱动程序初始化耗时较长时,会对快速启动(分散加载)的启动时间造成冲击。目前系统只支持优先级小于等于3的预加载函数。
注册设备驱动程序。
定义USB驱动程序实例。
static struct LosDriver hiudc3_driver = { .name = "hi_udc3", .ops = { .probe = hiudc3_probe, .remove = hiudc3_remove, }, .pmOps = { .suspend = hiudc3_suspend, .resume = hiudc3_resume, }, };
参数描述如表所示。
参数
描述
probe
该函数参数一般用于初始化驱动程序
remove
该函数参数一般用于去初始化驱动程序
name
驱动程序的属性name必须和对应驱动设备的name一致
suspend/resume
用于挂起/恢复驱动设备
注册USB驱动程序。
LOS_DriverRegister(&hiudc3_driver);
将加载的驱动注册到文件系统。
为方便用户使用,将驱动实例注册到文件系统,使对驱动的操作抽象为对文件的操作。具体实现请参见“适配文件操作的驱动开发”。
注意事项¶
调用预加载处理的驱动函数需要在“build/make/liteos_tables_ldflags.mk”中的LITEOS_TABLES_DRIVER_LDFLAGS添加“-u”声明,否则可能会导致链接器优化掉驱动初始化模块,造成不可预估的错误。
LITEOS_TABLES_DRIVER_LDFLAGS := \
-ui2c_init \
-ugpio_init \
-uregulator_init \
-umtd_init_list \
编程实例¶
实例描述
添加USB驱动。
编程实例
前提条件:在menuconfig菜单中使能驱动框架。
在指定开发板的“board.c”目录下定义驱动设备。
/* 定义驱动设备实例 */ #ifdef LOSCFG_DRIVERS_USB3_DEVICE_CONTROLLER struct LosDeviceRegs hiudc3_regs[] = { { .base = DWC_USB3_PORT1_BASE_ADDR, .size = DWC_USB3_PORT1_ADDR_OFFSET, }, }; static struct LosDevice hiudc3_device = { .name = "hi_udc3", .id = -1, .cfg = { .irqNum = NUM_HAL_INTERRUPT_USB_DEV, .numRegs = ARRAY_SIZE(hiudc3_regs), .regs = hiudc3_regs, }, }; #endif int machine_init(void) { #ifdef LOSCFG_DRIVERS_USB3_DEVICE_CONTROLLER (void)LOS_DeviceRegister(&hiudc3_device); #endif } arch_initcall(machine_init); //预加载驱动设备注册。内核初始化执行时,会预加载驱动设备注册
在“self_src/drivers/usb/adapt_liteos/controller/usb_device”目录下定义USB设备驱动程序。
/* 驱动程序实现的操作接口 */ static int hiudc3_probe(struct LosDevice *pdev) { …… } static int hiudc3_remove(struct LosDevice *pdev) { …… } static int hiudc3_suspend(struct LosDevice *pst_dev) { …… } static int hiudc3_resume(struct LosDevice *pst_dev) { …… } /* 定义设备驱动程序实例 */ static struct LosDriver hiudc3_driver = { .name = "hi_udc3", .ops = { .probe = hiudc3_probe, .remove = hiudc3_remove, }, .pmOps = { .suspend = hiudc3_suspend, .resume = hiudc3_resume, }, }; /* 初始化驱动程序,即注册驱动程序 */ int hiudc3_init(void) { return (int)LOS_DriverRegister(&hiudc3_driver); } /* 注销驱动程序 */ int hiudc3_exit(void) { return (int)LOS_DriverUnregister(&hiudc3_driver); }
适配文件操作的驱动开发¶
概述¶
基本概念
驱动开发就是把硬件的功能按操作系统的规格实现并抽象出来,提供给应用程序开发人员调用。
当在新的芯片上移植系统时,需要根据该芯片特性对支持的外部设备进行驱动开发。
LiteOS的驱动初始化函数主要初始化设备对应的驱动结构,提供给上层应用来注册设备驱动的控制节点。
开发指导¶
开发流程
推荐驱动开发人员使用VFS框架来注册/卸载设备驱动,基于文件系统的驱动开发主要涉及下面几步:
打开菜单,选择FileSystem ---> Enable VFS,使能VFS。
配置项
含义
取值范围
默认值
依赖
LOSCFG_FS_VFS
VFS框架的裁剪开关
YES/NO
YES
无
初始化驱动
在LiteOS上进行驱动开发的第一步,是编写驱动初始化函数。驱动初始化函数用于初始化设备所需的驱动结构,以及生成驱动的控制节点。
驱动初始化函数中需要调用设备驱动注册函数,用于注册并生成驱动节点。
register_driver(FAR const char *path, FAR const struct file_operations_vfs *fops, mode_t mode, FAR void *priv)
register_blockdriver(FAR const char *path, FAR const struct block_operations *bops, mode_t mode, FAR void *priv)
参数描述如下表所示:
参数 |
描述 |
|---|---|
path |
驱动节点路径,应用程序可通过该路径访问到驱动节点,进而访问设备驱动提供的操作接口。 |
fops/bops |
驱动操作结构体,为应用程序提供操作函数集。fops和bops分别表示字符设备和块设备的驱动操作集。 |
mode |
读写该驱动节点的权限,暂时无效,后续或提供支持。 |
priv |
驱动节点注册过程需要传入的参数。 |
编写完驱动初始化函数后,需要在适当的地方引导该初始化函数执行。用户可以在app_init函数里调用已编写好的驱动初始化函数引导设备初始化。
操作驱动节点
驱动初始化后,生成的设备驱动节点为应用提供操作设备的接口,下面以I2C设备驱动程序为例,说明用户程序与驱动操作函数的调用关系。
驱动操作函数集对于应用程序与驱动操作函数的调用关系非常重要。在编写驱动程序时,操作函数集需要实现硬件设备的各项机制,并在设备驱动注册时传入。操作函数集会成为应用程序需求的最终实现。
I2C设备驱动提供的操作函数集如下表所示:
操作函数集 |
对应的应用层接口 |
|---|---|
i2cdev_open |
open |
i2cdev_release |
close |
i2cdev_read |
read |
i2cdev_write |
write |
i2cdev_ioctl |
ioctl |
open操作
应用程序打开节点文件时,系统最终会在该驱动节点注册过程中调用驱动操作函数集中的open函数。
open函数对目标驱动设备函数结构体先进行实例化,再进行必要的初始化。在成功打开节点文件后,应用程序能够获取到对应驱动节点的文件描述符,并通过该文件描述符对驱动程序进行访问。
read/write操作
read/write操作是常用的访问设备的接口。不同类型的设备与驱动提供的read/write操作的功能互有差异。对于i2c设备,应用程序通过调用read/write接口可以实现对I2C设备进行读写。
i2cdev_read(struct file * filep, char __user *buf, size_t count) i2cdev_write(struct file * filep, const char __user *buf, size_t count)
参数描述如下表所示:
参数
描述
filep
文件描述结构体指针
buf
读出/写入数据的缓冲区指针
count
读出/写入数据的长度
ioctl操作
ioctl操作提供对驱动设备函数的配置管理。通过执行相应的命令,完成对设备属性的配置或访问。
I2C设备中,使用命令I2C_16BIT_REG、I2C_16BIT_DATA与I2C_TIMEOUT分别对应设置I2C设备的传输寄存器位宽、传输数据位宽与超时时间。
i2cdev_ioctl(struct file *filep, int cmd, unsigned long arg)参数描述如下表所示:
参数
描述
filep
文件描述结构体指针
cmd
操作命令
arg
附加参数
close操作
close操作对应着驱动操作函数集里的release函数。release函数对驱动程序的资源进行释放。
注意事项¶
无。
MMC驱动开发¶
概述¶
MMC(Multi Media Card)是 MMC协会订立、主要针对手机或平板电脑等产品的内嵌式存储器标准规格。MMC驱动用于处理对SD存储卡(Secure Digital Memory Card)和EMMC卡(Embedded Multi Media Card)的识别和读写等操作,并通过SDIO协议支持扩展外设(如蓝牙、WiFi、GPS等)。
LiteOS MMC驱动支持的设备包括TF卡、EMMC存储器和WiFi扩展外设。
TF(T-Flash)卡,又叫micro SD卡
TF卡主要通过挂载为FAT文件系统实现对设备的读写操作,挂载之前首先需要完成设备的识别,也可以通过裸读写接口对TF卡进行裸读写。
须知:
目前LiteOS不支持用户自定义TF卡分区。如有需要,用户需提前在PC端对TF卡做好分区操作。EMMC存储器
EMMC存储器主要通过挂载为FAT文件系统实现对设备的读写操作,挂载之前需要先调用接口自定义分区,然后进行设备的识别,也可以通过裸读写接口对EMMC存储器进行裸读写。
支持LiteOS调度模块启动前完成MMC驱动初始化,支持系统启动前(系统资源初始化后、第一次任务调度前)和异常阶段调用MMC异常读写接口对MMC存储进行读写和格式化,需打开宏LOSCFG_DRIVERS_MMC_RW_IN_BOOT_AND_EXC。
支持在LiteOS系统全局调度器启动前完成MMC低频切高频的tuning流程,支持EMMC器件在启动前运行在200MHZ频率。
支持RPMB(Replay Protected Memory Block)分区,通过RPMB操作接口对RPMB分区进行KEY的写入、Write Counter读取、RPMB数据区数据的读写(仅数据透传不涉及加解密),需打开宏LOSCFG_DRIVERS_MMC_RPMB。
提供发送CMD56命令的对外接口,获取EMMC存储器相关信息。
SDIO扩展外设
目前支持的外围设备只有WiFi模组。设备通过WiFi模组进行通信和数据传输,设备识别前需要根据硬件要求做好管脚复用配置。
LiteOS MMC驱动模块包括协议层和控制层:
协议层支持SD、EMMC、SDIO协议规范。
控制层支持Himci和Sdhci控制器,不同的芯片平台根据硬件IP选择对应的控制器驱动。
开发指导¶
MMC设备识别
LiteOS中的MMC设备分为嵌入式和非嵌入式两类:
嵌入式设备包括EMMC存储器和SDIO扩展外设,均不支持带电插拔。EMMC设备目前仅支持首次识别,不支持多次识别;SDIO扩展外设可以调用hisi_sdio_rescan接口实现对设备的卸载和加载识别(依赖于扩展外设的软件上下电)。
非嵌入式设备为TF卡。TF卡支持热插拔,通过线程轮询方式实现设备的动态卸载和加载。
须知: 除SDIO扩展外设可以调用sdio_rescan接口实现设备识别外,其他MMC设备的识别均在同一线程内实现,当存在多个MMC设备时,将根据控制器ID的顺序串行识别设备。
驱动初始化接口
接口名 |
描述 |
|---|---|
SD_MMC_Host_init |
遍历平台所有控制器并进行初始化 |
MMC_HostInitById |
初始化平台指定控制器,控制器由入参ID确定(可减少MMC驱动初始化的整体耗时) |
须知: MMC驱动初始化成功后,如果系统支持proc文件系统,会生成“proc/mci/mci_info”节点,通过“cat proc/mci/mci_info”可查看设备信息。此两个初始化接口不可混用,且MMC_HostInitById调用需保证单线程调用或者时间上互斥。
添加EMMC分区接口
接口名 |
接口参数 |
参数描述 |
|---|---|---|
add_mmc_partition |
struct disk_divide_info *info |
入参,是一个设备信息结构体,这里表示EMMC的分区信息,当前只支持传入全局变量struct disk_divide_info emmc |
size_t sectorStart |
入参,表示分区起始扇区 |
|
size_t sectorCount |
入参,表示分区扇区数 |
设备信息结构体定义如下所示:
#define MAX_DIVIDE_PART_PER_DISK 16 // 每个设备可支持的最大逻辑分区个数
#define MAX_PRIMARY_PART_PER_DISK 4 // 每个设备可支持的最大主分区个数
struct disk_divide_info{
UINT64 sector_count; // 分区块个数
UINT32 part_count; // 分区号
UINT32 sector_size; // 分区大小
struct partition_info part[MAX_DIVIDE_PART_PER_DISK + MAX_PRIMARY_PART_PER_DISK]; // 分区信息结构体
};
裸读写接口
TF卡裸读写接口说明
接口名
接口参数
参数描述
mmc_direct_write
mmc_direct_read
uint32_t host_idx
入参,表示TF卡所在的控制器编号
char * buffer
入参,表示读写数据保存的buffer地址(以CACHE_ALIGNED_SIZE对齐)
uint32_t start_sector
入参,表示读写的起始块地址
uint32_t nsectors
入参,表示读写的块数量
EMMC裸读写接口说明
接口名
接口参数
参数描述
emmc_raw_write
emmc_raw_read
char * buffer
入参,表示读写数据保存的buffer地址(以CACHE_ALIGNED_SIZE对齐)
uint32_t start_sector
入参,表示读写的起始块
uint32_t nsectors
入参,表示读写的块数量
MMC系统启动前和异常中读写/初始化接口说明
接口名
接口参数
参数描述
mmc_write_in_exception
mmc_read_in_exception
uint32_t host_idx
入参,表示mmc设备所在的控制器编号(支持TF卡与EMMC)
char * buffer
入参,表示读写数据保存的buffer地址(以CACHE_ALIGNED_SIZE对齐)
uint32_t start_sector
入参,表示读写的起始块地址
uint32_t nsectors
入参,表示读写的块数量
mmc_earse_in_exception
uint32_t host_idx
入参,表示mmc设备所在的控制器编号
uint32_t start_sector
入参,表示要格式化的起始块地址
uint32_t nsectors
入参,表示要格式化的块数量
须知:
MMC异常(中断)上下文读写接口支持TF卡与EMMC但不支持SDIO设备,不推荐在非异常(中断)中调用,在调用前用户需保证系统不处于MMC逻辑执行流程中,否则可能会导致接口读写失败。
RPMB接口
RPMB数据帧结构体
typedef struct {
uint8_t padding[RPMB_PADING_LEN];
uint8_t mac_key[RPMB_MAC_KEY_LEN];
uint8_t data[RPMB_DATA_LEN];
uint8_t rand[RPMB_RAND_LEN];
uint32_t write_cnt;
uint16_t address;
uint16_t block_cnt;
uint16_t op_result;
uint16_t req_resp;
} rpmb_data_t;
RPMB分区操作接口
接口名 |
接口参数 |
参数描述 |
|---|---|---|
mmc_rpmb_operate |
uint32_t index |
入参,表示MMC设备所在的控制器编号 |
uint32_t operType |
入参,对RPMB分区的操作类型,支持四种操作类型设置key(RPMB_SET_KEY)、获取Write Counter(RPMB_READ_WRITE_COUNTER)、写数据(RPMB_WRITE_DATA)、读数据(RPMB_READ_DATA) |
|
void *buf |
入/出参,RPMB数据帧结构体 |
发送CMD56命令接口
接口名 |
接口参数 |
参数描述 |
|---|---|---|
mmc_general_cmd |
uint32_t index |
入参,表示MMC设备所在的控制器编号。 |
uint32_t arg |
入参,与eMMC厂家有关,不同厂家参数不同。 |
|
unsigned char *buffer |
出参,返回eMMC设备的健康信息、厂家信息。 |
SDIO接口
LiteOS提供了一套完整的SDIO对外接口,包括发送命令、收发数据等。SDIO扩展外设通过调用这套接口实现对扩展外设的操作。
接口名 |
描述 |
|---|---|
sdio_get_func |
获得一个sdio_func,此sdio_func在SDIO设备识别后产生,通过func号、厂商ID、设备ID(此三个参数根据SDIO外设获得)唯一标识。 |
sdio_en_func |
使能,该接口通过CMD52命令写0x2地址实现。 |
sdio_dis_func |
禁止指定的sdio_func,该接口通过CMD52命令写0x2地址实现。 |
sdio_require_irq |
给指定的sdio_func注册SDIO中断处理函数,该中断处理函数需要用户自行实现,函数类型定义为:void (sdio_irq_handler_t)(struct sdio_func *)。 |
sdio_release_irq |
释放指定sdio_func中注册的SDIO中断处理函数。 |
sdio_read_byte |
从指定sdio_func的指定地址addr读取一字节数据,并返回读取的数据。 |
sdio_read_byte_ext |
从指定sdio_func的指定地址addr读取一字节数据,添加入参in到CMD参数中(暂不支持),并返回读取的数据。 |
sdio_read_incr_block |
从指定sdio_func的指定地址addr读取指定长度的数据到内存指定地址dst中,读取时设备地址会依次增长(比如设备的内存地址)。 该接口通过CMD53命令实现。 |
sdio_read_fifo_block |
从指定sdio_func的指定地址addr读取指定长度的数据到内存指定地址dst中,读取时设备地址固定不变(比如设备的FIFO)。 该接口通过CMD53命令实现。 |
sdio_write_incr_block |
将指定地址src中size长度的数据写入指定sdio_func的指定地址addr中,写入时设备地址会依次增长(比如设备的内存地址)。 该接口通过CMD53命令实现。 |
sdio_write_fifo_block |
将指定地址src中size长度的数据写入指定sdio_func的指定地址addr中,写入时设备地址固定不变(比如设备的FIFO)。 该接口通过CMD53命令实现。 |
sdio_write_byte |
将一字节数据写入指定sdio_func的指定地址addr,该接口通过CMD52命令实现。 |
sdio_write_byte_raw |
将一字节数据写入指定sdio_func的指定地址addr,写完后读回(设置RAW Flag),该接口通过CMD52命令实现。 |
sdio_func0_read_byte |
从设备指定地址addr读取一字节数据,并返回读取的数据,该接口通过CMD52命令读取func0实现。 |
sdio_func0_write_byte |
写入一字节数据到设备指定地址addr,该接口通过CMD52命令写入func0实现。 |
sdio_set_cur_blk_size |
配置SDIO当前块大小,此块小大不应大于512Byte。 |
sdio_rescan |
SDIO设备重新识别,实际执行操作为设备卸载和加载。 |
sdio_reset_comm |
reset SDIO设备。 |
LiteOS基于控制器能力,提供数据的离散模式传输,并提供了一套专门的读写接口。SDIO扩展外设通过调用这套接口实现离散传输,即可以将多块buffer数据一次性发送或者一次性读取数据到多块buffer,以减少多块数据传输场景下的数据拷贝,提升数据传输性能。
接口名 |
描述 |
|---|---|
sdio_readv_incr_block |
从指定sdio_func的指定地址addr读取数据,读取的数据长度和保存地址由入参struct mmc_sg *sg指定,sg中的数据个数由入参sg_nums决定,读取时设备地址会依次增长(比如设备的内存地址)。 该接口通过CMD53命令实现。 |
sdio_readv_fifo_block |
从指定sdio_func的指定地址addr读取数据,读取的数据长度和保存地址由入参struct mmc_sg *sg指定,sg中的数据个数由入参sg_nums决定,读取时设备地址固定不变(比如设备的FIFO)。 该接口通过CMD53命令实现。 |
sdio_writev_incr_block |
将入参struct mmc_sg *sg中的数据写入指定sdio_func的指定地址addr,sg中提供数据地址和长度,sg中的数据个数由入参sg_nums决定,写入时设备地址会依次增长(比如设备的内存地址)。 该接口通过CMD53命令实现。 |
sdio_writev_fifo_block |
将入参struct mmc_sg *sg中的数据写入指定sdio_func的指定地址addr,sg中提供数据地址和长度,sg中的数据个数由入参sg_nums决定,写入时设备地址固定不变(比如设备的FIFO)。 该接口通过CMD53命令实现。 |
mmc_sg结构体如下所示:
struct mmc_sg {
size_t length; // 数据长度,不能大于buffer的内存空间大小
void *data; // buffer地址,必须cache line对齐,其指向的buffer内存空间大小也必须cache line对齐
};
EMMC存储器开发流程
添加EMMC分区
size_t part0_start_sector = 16 * (0x100000/512); size_t part0_count_sector = 1024 * (0x100000/512); extern struct disk_divide_info emmc; add_mmc_partition(&emmc, part0_start_sector, part0_count_sector);
初始化驱动
MMC_HostInitById(emmc_host_id);
说明:
emmc_host_id为EMMC存储器对应的控制器编号。
emmc介质识别成功后会生成“/dev/mmcblkxPy”的节点,供文件系统挂载。(x是设备ID,一般是0或者1,y是分区ID,由设备分区个数决定,从0开始)。设备识别后,读写设备
裸读写
调用EMMC裸读写接口进行读写。
挂载为FAT文件系统进行读写
mount("/dev/mmcblk0p0", "/sd", "vfat", 0, NULL);挂载后可通过文件系统标准接口对设备进行读写等操作。
说明:
请根据实际情况传入“/dev/mmcblk0p0”为EMMC存储器被识别后注册的设备名。
TF卡开发流程
初始化驱动
MMC_HostInitById(sd_host_id);
说明:
sd_host_id为TF卡对应的控制器编号。插入sd卡并识别成功后会生成“/dev/mmcblkxPy”的节点,供文件系统挂载。其中,x是设备ID,一般是0或者1,y是分区ID,由设备分区个数决定,从0开始。设备识别后,读写设备
裸读写
调用TF卡裸读写接口进行读写。
挂载为FAT文件系统读写
mount("/dev/mmcblk0p0", "/sd", "vfat", 0, NULL);挂载后可通过文件系统标准接口对介质进行读写等操作。
说明:
“/dev/mmcblk0p0”为TF卡被识别后注册的设备名,请根据实际情况传入。
SDIO扩展外设开发流程
管脚复用
配置IO寄存器,配置对应SDIO外围设备的CLK、CMD、DATA0、DATA1、DATA2、DATA3管脚寄存器。
初始化驱动
MMC_HostInitById(sdio_host_id);
说明:
sdio_host_id为SDIO扩展外设对应的控制器编号。SDIO wifi设备识别成功后会生成“/dev/sdio0”的节点。设备识别后获取func并使能func
struct sdio_func* func; func = sdio_get_func(func_num, manf_id, device_id); sdio_en_func(func);
设置当前block size
sdio_set_cur_blk_size(func, blk_size);注册中断处理函数
void handler_sample(struct sdio_func *func) { dprintf(“%s,%d func:0x%x\n”,__FUNCTION__,__LINE__,func); return; } sdio_require_irq(func, handler_sample);
后续就可以通过SDIO对外读写接口进行读写等操作了。
注意事项¶
正常操作过程中需要遵守的事项:
保证卡的金属片与卡槽硬件接触充分良好(如果接触不好,会出现检测错误或读写数据错误),测试薄的MMC卡时,必要时可以用手按住卡槽的通讯端测试。
读写过程中异常拔卡,驱动会直接卸载设备节点。在该场景下,用户必须释放正在操作的资源(比如打开的文件、目录等资源),并且对挂载点进行umount操作。否则,会造成资源泄漏或挂载点不能再次挂载。
SDIO外设业务调用sdio_require_irq注册中断后,在设备卸载前必须保证调用sdio_release_irq接口释放中断和相关资源,否则可能导致系统异常。
EMMC存储器进行驱动初始化前必须先进行添加分区,否则会导致设备识别时无法获取分区,设备识别后不支持动态添加和删除分区。
EMMC存储器挂载为FAT文件系统前需将dev设备格式化,否则可能导致挂载失败。
SDIO扩展外设进行驱动初始化前必须先进行管脚复用配置,否则会导致设备无法识别。
被禁止的操作:
读写SD卡时不要拔卡,否则会打印一些异常信息,并且可能会导致卡中文件或文件系统被破坏。
格式化SD卡时禁止拔卡,否则可能会造成SD卡永久性损坏。
上电状态下严禁拔掉SDIO扩展外设,否则可能会造成SDIO扩展外设或者硬件单板损坏。
不同平台控制器对应的介质可能不同,关于控制器编号与所支持的介质类型,请参考各芯片平台用户指南文档中关于EMMC/SD/SDIO控制器的章节。
USB驱动开发¶
概述¶
USB(Universal Serial Bus,通用串行总线)是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。LiteOS USB驱动主要包含协议层、Composite层和控制器驱动三部分。
协议层
协议层是设备具体实现层,支持上层业务通过USB口进行数据收发。
Composite层
Composite(复合设备)层连接协议层与控制器驱动,支持具体协议的加载、卸载及设备枚举过程。Composite作为虚拟设备,其他USB设备都需要挂载在Composite设备下面。由于Composite层封装了与控制端口交互的处理流程,USB设备只需要向Composite层注册相应的处理函数,具体协议的加载和设备枚举过程由Composite层执行。
控制器驱动
控制器驱动与硬件交互,主要支持数据收发和端口控制。
图 1 USB驱动基本框架

运作机制
USB协议驱动加载和数据收发主要流程如下:
USB驱动加载
USB设备驱动的加载通过Composite层封装统一的处理流程,由Composite调用具体协议的加载函数进行驱动程序的加载。待设备与主机通过USB线建立连接后,Composite层统一处理与主机之间的枚举过程,通过协议层注册的处理函数,进行端口申请和协议私有数据初始化。
USB数据收发
USB设备加载成功后,业务层通过协议层进行数据收发,协议层直接调用控制器驱动提供的端口控制接口进行端口数据收发处理。
开发指导¶
功能¶
功能
Hi35xx的USB功能支持Device模式,具体协议及功能参见下表。
功能 |
描述 |
使用说明 |
|---|---|---|
Device U盘 |
支持将挂载到系统的SD卡/MMC介质当做U盘,供USB Host端进行读写访问等操作。 |
|
UVC(USB Video Class) |
支持通过USB口进行视频数据的发送。 |
|
UAC(USB Audio Class) |
支持通过USB口进行音频数据的发送和接收。 |
|
UAV(UVC&UAC) |
支持通过USB口进行音视频数据的发送。 |
|
DFU(Device Firmware Upgrade) |
支持通过USB口对固件的升级,对接Host端DFU协议。 |
|
HID(Human Interface Device) |
支持通过USB口对HID设备的数据进行接收和发送。 |
|
虚拟网口 |
支持通过USB口进行网络数据的收发,模拟网口的功能。 |
|
虚拟串口 |
支持通过USB口进行串行通信,模拟串口的功能。 |
支持调测功能 |
复合设备 (虚拟网口&虚拟串口) |
支持基于USB口同时模拟网口和串口的功能。 |
|
UAC&HID复合设备 |
支持通过USB口进行音频数据、HID数据的发送和接收。 |
|
说明: USB3.0 Device对接USB2.0 Host时,由于设备侧硬件限制不支持降速,无法识别。
LiteOS USB驱动由于受芯片硬件资源限制,支持的规格也不尽相同,详细请参见下表所示。
芯片平台 |
U 盘 |
UVC |
UAC |
UAV |
DFU |
HID |
虚拟串口 |
虚拟网口RNDIS |
虚拟网口 ECM |
复合设备 SER_RNDIS和SER_ECM |
UAC&HID |
|---|---|---|---|---|---|---|---|---|---|---|---|
Hi3516DV300 (USB2.0) |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
说明: Hi3516DV300/Hi3556V200/Hi3559V200的USB2.0口,是基于USB3.0的控制器操作的,编译时需要选择3.0的控制器。
配置USB模块的内存¶
USB模块的正常运行需要一段uncached内存,所以需要单独为USB模块配置内存。
配置内存可通过定义以下函数实现:
void board_config(void)
{
g_sys_mem_addr_end = 0x88000000;
g_usb_mem_addr_start = g_sys_mem_addr_end;
g_usb_mem_size = 0x100000;
}
须知:
不可以修改void board_config(void)的声明,若应用层不定义void board_config(void)函数,LiteOS会按默认值分配OS可见内存大小。建议根据实际情况自行定义。
g_sys_mem_addr_end配置的是OS可见内存的结束位置。
g_usb_mem_addr_start配置的是USB内存的起始位置。
g_usb_mem_size配置的是USB内存的大小。
若系统完全不支持USB相关功能,则可以将g_usb_mem_addr_start和g_usb_mem_size配置为0。
动态切换USB协议¶
动态切换时,先调用usb_deinit卸载当前使用的协议,再通过usb_init重新加载待切换的协议。
部分协议使用中不支持卸载(如:DFU、UVC、Camera、HID),需要先停用当前业务后再调用usb_deinit卸载。
模块编译¶
USB驱动的源码路径为drivers/usb。用户在LiteOS根目录下执行make即可编译出对应的USB模块库。编译成功后,会在“out/<platform>/lib”目录下生成名为“libusb_base.a”、“libusb_device.a”的库文件,目录中的platform为具体的平台名。
图1是LiteOS中USB驱动menuconfig配置项截图,在“Driver”配置项下,可以通过选中“Enable USB”开启 USB 驱动功能。
图 1 USB驱动menuconfig配置

Device端
配置项如为图1所示的1~4,对应描述如下:
1表示是否打开USB驱动Device功能。
2表示Device控制器种类,分别有Dwc2.0和3.0(可视具体板子的 USB 口类别而定)。
3表示是否开启Device模式下的USB协议功能。
4表示Device模式下的具体USB协议功能,支持的USB协议如下,menuconfig配置项如图2所示:
Device U盘
UVC
UAC
Camera(UVC&UAC)
DFU升级
虚拟串口
虚拟网口ECM
虚拟网口RNDIS
复合设备(虚拟串口&虚拟网口)
HID
UAC&HID复合设备
图 2 USB驱动Device端配置

须知: 初始化Device模式也是调用usb_init(DEVICE, xxx) ,根据业务诉求开启需要的协议,也可以全部开启。另外,依赖的外围组件也需要提前加载好,具体参见“Device端”的编程实例。
编程实例¶
Device端¶
Device虚拟U盘¶
初始化
在初始化函数中初始化SD卡驱动和USB驱动:
SD_MMC_Host_init();
usbd_set_device_info(DEV_MASS, &str_manufacturer, &str_product, &str_serial_number, dev_id);
usb_init(DEVICE, DEV_MASS); // dtype设置为DEV_MASS
说明:
在多卡情况下(系统同时挂载2张卡及以上),USB2.0 Device默认绑定第一张卡设备节点,例如同时存在“/dev/mmcblk0p0”,“/dev/mmcblk1p0”时,USB2.0 Device会绑定“/dev/mmcblk0p0”,因此Host端通过USB口只能访问到一张SD/MMC卡。
USB2.0 Device连接Host端后,Host端可以通过USB端口访问到系统存储介质的文件。在Host端访问存储介质过程中,系统端请勿对MMC/SD Card存储介质进行读写、格式化等操作,避免Host端访问存储介质异常。
Device U盘对接PC进行格式化时,仅支持Windows PC进行快速格式化操作,请勿使用Windows PC的一般格式化操作或Linux的高级格式化命令,避免对分区信息进行修改,导致Device卸载虚拟U盘后mount失败。如果进行了此操作,mount失败后可重新format存储介质来解决。
目前虚拟U盘支持的存储介质有4种,分别是:SD卡、MMC、RAM、FTL spi_nor。初始化流程一致,在加载USB之前,都需要先将对应介质的文件系统初始化,确保“dev/”目录下有可供虚拟U盘读写的节点。
注册Device U盘回调函数
应用层通过fmass_register_notify(void(*notify)(void* context, int status), void* context)函数注册notify回调,驱动会在识别到接入和拔出Host端时调用此回调函数,从而实现DeviceU盘拔插相关的处理。
请在USB模块初始化后,立马调用fmass_register_notify()实现Device回调函数的注册。
须知: Notify回调函数中,在USB Device链接的情况下,需通过fmass_partition_startup()将需要被PC端识别的SD卡分区节点启动,并且保证其接口传进来的设备节点是存在的,如果不存在,就默认将“/dev/mmcblk0p0”虚拟化为U盘,若“/dev/mmcblk0p0”节点不存在,则会失败。
开启额外串口
开启LOSCFG_DRIVERS_USB_MASS_STORAGE_EXT_SER_FUNC,即可在虚拟U盘的基础上额外增加串口的功能。当前支持配置为单串口和双串口。
注意事项
在正常操作过程中需要遵守的事项:
Device U盘将SD卡交由USB Host处理,因此在Device U盘被Host端识别后,会通过回调函数通知APP软件模块,APP必须将所有录像抓拍等操作SD卡的业务停下,并umount SD卡分区,待USB Host离线后再mount和恢复相关业务。
USB Host端进行读写访问时,不要拔出单板的SD/MMC卡,否则会打印一些异常信息,并且可能会导致卡住文件或文件系统被破坏。
在操作过程中出现异常时的操作:
若拔出Device U盘后,再极其快速地插入Device U盘,可能会导致Host端检测不到U盘,因为单板Device U盘的检测注册/注销过程需要一定的时间。
编程示例
extern int fmass_register_notify(void(*notify)(void* context, int status), void* context);
extern int fmass_partition_startup(char* path);
void fmass_app_notify(void* conext, int status)
{
char *path = "/dev/mmcblk0p0";
if(status == 1)/*usb device connect*/
{
/* 停止录像抓拍等对/dev/mmcblk0p0分区的读写操作,并umount该分区 */
fmass_partition_startup(path); // startup fmass access partition
}
else
{
/* mount该分区,并通知其它模块该分区可以进行读写操作 */
}
}
void app_init()
{
uint32_t ret;
int handle;
const char manufacturer[38] = {
'H', 0, 'i', 0, 'S', 0, 'i', 0, 'l', 0, 'i', 0, 'c', 0, 'o', 0, 'n', 0,
'(', 0, 'S', 0, 'h', 0, 'a', 0, 'n', 0, 'g', 0, 'h', 0, 'a', 0, 'i', 0, ')', 0
};
struct device_string str_manufacturer = {
.str = manufacturer,
.len = 38
};
const char product[18] = {
'U', 0, 'S', 0, 'B', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0,
'C', 0, 'E', 0
};
struct device_string str_product = {
.str = product,
.len = 18
};
const char serial[16] = {
'2', 0, '0', 0, '2', 0, '0', 0, '0', 0, '6', 0, '2', 0,
'4', 0
};
struct device_string str_serial_number = {
.str = serial,
.len = 16
};
struct device_id dev_id = {
.vendor_id = 0x3361,
.product_id = 0x0108,
.release_num = 0x0318
};
SD_MMC_Host_init();
g_usb_mem_addr_start= g_sys_mem_addr_end;
g_usb_mem_size= 0x40000; //recommend 256K nonCache for usb
ret = usbd_set_device_info(DEV_MASS, &str_manufacturer, &str_product, &str_serial_number, dev_id);
if (ret != 0) {
dprintf("set fail!\n");
return;
}
ret = usb_init(DEVICE, DEV_MASS); // usb_init must after SD_MMC_Host_init
if(ret != 0) {
dprintf("usb init failed!\n");
return;
}
handle = fmass_register_notify(fmass_app_notify,NULL);
if (handle == -1) {
return;
}
}
内存中Fat32文件系统虚拟为U盘
制作FAT格式的文件系统镜像(以Ubuntu系统为例)。
制作FAT格式磁盘镜像。
mkfs.fat -s 64 -S 512 -f 2 -n "Internal SD" -F 32 -C usbdisk.tmp 2048其中2048表示磁盘空间大小,单位是KB。
挂载FAT格式磁盘镜像。
mount -o loop -t vfat usbdisk.tmp test/其中test为挂载目录。
在test目录下存放所需文件,然后将文件同步到磁盘。
sync test/卸载磁盘镜像。
umount test/制作文件系统镜像。
dd if=usbdisk.tmp of=udisk.img bs=1024 count=2048将“usbdisk.tmp”前面2MB的内容制作为文件系统镜像“udisk.img“”。
将制作好的文件系统镜像下载到flash上。
将“udisk.img”文件放到指定路径FAT_PART_IMAGE_PATH(“/littlefs/udisk.img”)。
调用ram_mass_storage_init接口注册RAM_DEV_NAME(/dev/uram)设备节点。
调用fmass_register_notify接口指定设备节点“/dev/uram”虚拟为U盘。
调用usbd_set_device_info和usb_init接口加载虚拟U盘协议。
须知:
本次制作的文件系统镜像申请的是2M容量,请根据实际业务需求调整容量大小,除了需要制定特定容量的文件系统镜像之外,还要修改对应的FAT_PART_SIZE大小。
Device UVC¶
Device UVC支持UVC1.0协议和UVC1.1协议版本的切换,打开菜单,进入Driver ---> Enable USB ---> Enable USB DWC Controller ---> Enable USB Gadget Support ---> USB Gadget Drivers ---> UVC Version可选择UVC1.0或UVC1.1版本。当前默认为UVC1.1协议,切换为UVC1.0可支持Windows xp系统,增加兼容性。
操作指导
UVC上层应用在USB初始前设置UVC传输能力集,开启VI,打开UVC设备。
就绪后,等待底层UVC连接,启动VPSS和VENC编码并传输码流数据,直至UVC通道断开。
切换新码流格式时,应用须先关闭前一次编码并清空缓存,再启动编码。
操作示例
方式1:直接调用device uvc相关接口控制数据传输。
通过fuvc_frame_descriptors_get接口设置UVC传输能力集(含:编码格式、分辨率和帧率)。
在初始化函数中调用usbd_set_device_info和usb_init接口实现USB驱动注册。
usbd_set_device_info(DEV_UVC, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_UVC); // dtype设置为DEV_UVC
开启VI后,调用uvc_open_device打开UVC设备,需指定分辨率和帧率。
通过uvc_wait_host等待板端与USB主机侧连接成功。
启动VPSS和VENC编码后,通过uvc_recv_pack开始执行视频数据传输。
通过uvc_video_stop停止视频数据传输,可调用uvc_close_device关闭USB设备。
方式2:打开device uvc注册的设备节点,并通过系统调用控制数据传输。
在初始化函数中调用usbd_set_device_info和usb_init接口实现USB驱动注册。
usbd_set_device_info(DEV_UVC, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_UVC); // dtype设置为DEV_UVC
开启VI后,调用open打开UVC设备节点“/dev/usb_uvc-0”,并通过调用select函数监视设备连接情况。
当板端与USB主机侧连接成功并可以开始传输数据时,select系统调用能够接收到来自host端的消息,此时调用ioctl接口发送VIDIOC_DQEVENT命令,并提供结构体struct usbd_uvc_video_event来接收数据。根据接收的数据将视频的分辨率、帧率等信息发送给VPSS和VENC。
启动VPSS和VENC编码后,通过uvc_recv_pack开始执行视频数据传输。同时通过系统调用ioctl发送VIDIOC_STREAMON命令通知USB设备可以开始进行数据传输。
通过调用VIDIOC_STREAMOFF可以停止视频传输,再通过系统调用close关闭USB设备。
须知: 方式1与方式2都有打开设备的操作,分别是直接调用接口uvc_open_device和调用系统接口open。当同时使用这两个接口时,只能打开一次设备,后调用的接口将无法打开设备且其各种操作都将失效。
Device UAC¶
UAC的数据处理流程,麦克风业务具体步骤如下:
在初始化函数中调用usbd_set_device_info和usb_init接口实现USB驱动注册。
usbd_set_device_info(DEV_UAC, str_manufacturer, str_product, str_serial_number, dev_id); usb_init(DEVICE, DEV_UAC); // dtype设置为DEV_UAC
开启VI后,通过uac_wait_host等待板端与USB。
通过fuac_get_opts接口获取当前Device UAC与Host协商的采样率、声道数和位深信息,计算每次要发送的音频长度和需要缓存的音频个数。
通过3计算出的数值,调用fuac_reqbuf_init接口初始化UAC内存池。
启动VPSS和VENC编码前调用fuac_reqbuf_get接口获取空闲buf,把编码后的音频填充到此buf。
结束音频传输时,把5中已获取的空闲buf,通过调用fuac_send_message接口,全部归还给USB,然后调用fuac_reqbuf_deinit接口释放UAC内存池。
调用usb_deinit接口卸载协议,注意卸载协议前必须保证UAC内存池的所有buf全部归还给USB层,否则会卸载失败。
扬声器业务具体步骤如下:
Device UAV(Camera)¶
UAV是同时支持UAC、UVC,相关操作流程,参见上面UVC、UAC使用示例。区别在于初始化时,传入的协议类型:
usbd_set_device_info(DEV_CAMERA, &str_manufacturer, &str_product, &str_serial_number, dev_id);
usb_init(DEVICE, DEV_CAMERA); // dtype设置为 DEV_CAMERA
Device虚拟串口¶
虚拟串口配置说明如下:
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_DRIVERS_USB_SERIAL_VFS_INTERFACE |
使能VFS的读写接口控制虚拟串口的读写和ioctl |
YES/NO |
YES |
LOSCFG_FS_VFS |
LOSCFG_DRIVERS_USB_SERIAL_FUNC_INTERFACE |
使能对外接口控制虚拟串口读写和ioctl |
YES/NO |
NO |
无 |
LOSCFG_DRIVERS_USB_SERIAL_GADGET_DEFAULT |
使能默认单串口 |
YES/NO |
YES |
无 |
LOSCFG_DRIVERS_USB_SERIAL_GADGET_MULT |
使能多串口 |
YES/NO |
NO |
无 |
LOSCFG_DRIVERS_USB_SERIAL_COUNT |
多串口使能时,设置串口具体数量 |
2-3 |
2 |
LOSCFG_DRIVERS_USB_SERIAL_GADGET_MULT |
LOSCFG_DRIVERS_USB_SERIAL_NOTIFICATION_FUNC |
使能串口通知功能(开启中断端点) |
YES/NO |
YES |
无 |
操作示例
方式一:初始化虚拟串口后,通过VFS 接口打开串口节点,并使用read、write、ioctl等接口控制串口。
当系统支持文件系统时,以下就是依赖文件系统的虚拟串口的初始化和使用流程:
在初始化函数中调用以下接口实现USB驱动注册:
usbd_set_device_info(DEV_SERIAL, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_SERIAL);
若需要基于USB虚拟的串口进行shell通信交互与调试信息打印,需要再进行console、Shell等与串口有依赖关系的模块初始化。
virtual_serial_init("/dev/ttyGS0"); system_console_init("/dev/serial");
若单板端写数据发送到USB主机,则直接调用write接口;若单板端读取从USB主机发来的数据,open串口后先调用ioctl接口,然后再调用read接口,ioctl用法如下:
ioctl(fd, CONSOLE_CMD_RD_BLOCK_SERIAL, 1);
须知:虚拟串口功能不能与Uart串口同时使用。
如果有对节点的多次open操作,需要在卸载前执行相同次数close操作,才能保证协议卸载时成功,否则会出现不可预知的错误。
虚拟串口最大支持读写4096Byte数据。
单串口模式下初始化后,在dev目录下会生成/dev/ttyGS0节点,在双串口模式下初始化后,在dev目录下会生成/dev/ttyGS0节点和/dev/ttyGS1节点。
方式二:直接调用非VFS模块的对外接口控制虚拟串口
当系统不支持文件系统时,虚拟串口和shell暂不兼容,初始化虚拟串口之后,需要调用usb_serial_ioctl接口,使虚拟串口可以正常读。用法如下:
usbd_set_device_info(DEV_SERIAL, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_SERIAL); usb_serial_ioctl(0,CONSOLE_CMD_RD_BLOCK_SERIAL, 1); // index参数默认使用0,当开启双串口时,可以选0或1。
然后根据业务需要调用usb_serial_read和usb_serial_write对虚拟串口进行读写操作。
须知:
非VFS模式的虚拟串口,使用对外接口时,入参index默认使用0,当仅支持一个虚拟串口时,只能用0,当开启双串口时,可以选0或1。
Host端如何识别串口
如果Host端为Windows PC,那么首先在PC端放置驱动文件“linux-cdc-acm.inf”,该文件在发布包中的路径:01.software/pc/usb_tools。
通过USB数据线将单板与Host端相连,PC端会自动加载驱动,第一次一般会失败,需要自行安装驱动,方法为:
右击计算机,进入管理界面。
打开设备管理器。
点开其他设备,会看到Gadget Serial(COMx),双击。
打开驱动程序界面,点击更新驱动程序,进入浏览计算机以查找驱动程序软件(R)。
把路径指向驱动文件“linux-cdc-acm.inf”所在的目录,点击下一步,计算机会自动进行安装驱动程序,安装成功后,关闭界面。
须知:“01. software/pc/usb_tools”目录下有“linux-cdc-acm.inf”文件,通常Win7无法自动加载虚拟串口驱动,需要手动加载;Win10通常会自动加载虚拟串口驱动,无需手动加载。
如果安装串口驱动失败,则需要修改“linux-cdc-acm.inf“文件,在[DeviceList]和[DeviceList.NTamd64]里添加“USB\VID_3361&PID_0102&REV_0318”,其中3361对应的是VID,0102对应的是PID,0318对应的是设备版本号,这3组数字是通过调用usbd_set_device_info接口来设置,请根据实际设备进行修改,需要和设置值保持一致。
单板端重新上电启动,在PC端就可以作为真正的串口来使用。
Device虚拟网口RNDIS¶
RNDIS目前可以不依赖lwip,支持对接不同的网络协议栈。RNDIS初始化方式分为两种。
第一种就是在开启了LWIP配置项后,步骤如下:
在初始化函数中调用以下接口实现TCP/IP协议栈初始化和USB驱动注册。
tcpip_init(NULL, NULL); // TCP/IP协议栈初始化 usbd_set_device_info(DEV_ETHERNET, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_ETHERNET);
USB虚拟网口默认未配置IP地址,请根据网络规划自行配置IP地址。
如果host端为Windows 7或者Windows10,那么首先在PC端放置linux.inf驱动文件,见发布包路径:01.software/pc/usb_tools。然后修改“linux.inf”驱动文件,在[RndisDevices.NTx86]、[RndisDevices.NTamd64]和[RndisDevices.NTia64]添加“USB\VID_3361&PID_0102&REV_0318”,其中3361对应的是VID,0102对应的是PID,0318对应的是设备版本号,这3组数字是通过调用usbd_set_device_info接口来设置,请根据实际设备进行修改,要和设置值保持一致。
通过USB数据线将单板与Host端相连。
如果Host端为windows7系统,通常自动加载驱动,一般会失败,需要自行安装驱动,具体操作为:
右击计算机,进入管理界面。
打开设备管理器。
点开其他设备,会看到“Linux USB Ethernet/RNDIS Gadget #3“”,双击。
打开驱动程序界面,点击更新驱动程序,进入浏览计算机以查找驱动程序软件(R)。
把路径指向“linux.inf”所在的目录,点击下一步,计算机会自动进行安装驱动程序,安装成功后,关闭界面。
如果Host端为Windows10系统,通常会误识别为虚拟串口,需要手动更新驱动,具体操作为:
右击计算机,进入管理界面。
打开设备管理器。
点开端口设备,会看到USB串行设备(COMX)。
打开驱动程序界面,点击更新驱动程序,进入浏览计算机以查找驱动程序软件(R)。
把路径指向“linux.inf”所在的目录,点击下一步,计算机会自动进行安装驱动程序,安装成功后,会生成虚拟网口设备,对应虚拟串口设备消失,关闭界面。如果安装过程中提示第三方INF不包含数字签名信息,此关闭方法可在网上搜索,此处不再赘述。
在单板端,配置IP并添加路由。
当单板和PC通过USB数据线相连时,会在PC端生成USB网络节点,具体位置:打开网络和共享中心--->更改适配器设置--->Linux USB Ethernet /RNDIS Gadget #x。
建立网桥:
右键“Linux USB E thernet /RNDIS Gadget #x”节点,点击添加到桥选项;
PC端连接大网的本地连接节点右键,选择添加到桥选项;
等待网桥建立完成,如图1所示。
图 1 成功建立网桥

此时在单板端,能正常访问大网,可作为真正的网口操作使用。
须知:
在Win10 PC桥接网络,若网桥无法获取到IP地址时,请确定一下桥接顺序,桥接时请先选择物理网卡,再选择虚拟网卡,然后桥接。若配置网桥失败,请单击确定,然后再选择物理网卡右键添加到桥(这个步骤很重要),因为造成配置网桥失败的原因就是物理网卡没有添加到桥。
当访问桥接的网络环境时需要权限,若出现能ping通IP地址但ping不通网关和服务器的问题,请排查单板端的MAC地址(IP和MAC地址有关联性)。
第二种初始化RNDIS的方式,就是在关闭LWIP的情况下,对接其他网络协议栈,步骤如下:
在初始化函数中进行USB驱动注册之前,需要调用RNDIS初始化其他网络协议栈钩子的函数。
struct usb_eth_operations { void (*eth_register)(void);// 加载时调用,注册网卡 void (*eth_unregister)(void);// 卸载时调用,注销网卡 void (*eth_link_change)(bool connected);// 连接状态改变时调用,通知网络协议栈断连状态 void (*eth_rx)(const uint8_t *buf, uint32_t buflen);// 收到数据时调用,将数据传入网络协议栈 };// 此结构体定义在f_eth.h头文件中,需要包含此头文件
创建struct usb_eth_operations类型ops,并使用其他网络协议栈api实现上述四个功能类型的函数,并将对应函数赋值给ops的各个函数指针成员。然后调用feth_init_ops函数初始化RNDIS内部的对应函数。
// 默认其他网络协议栈已经实现相应函数指针,参考self_src/drivers/usb/gadget/usbd_eth_common.c文件中lwip实现对应函数指针的方式 struct usb_eth_operations ops; ops.eth_register = other_net_reg; ops.eth_unregister = other_net_unreg; ops.eth_link_change = other_net_link; ops.eth_rx = other_net_rx; if (feth_init_ops(&ops) != 0) { dprintf("RNDIS init ops faild.\n"); return; } dprintf("RNDIS init ops success.\n");// 执行到这里说明已经初始化RNDIS与网络协议栈对接的钩子
调用以下接口进行USB驱动注册。
usbd_set_device_info(DEV_ETHERNET, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_ETHERNET);
至此已完成RNDIS的加载,其他网络协议栈需要发送数据到host,则调用feth_send_data接口即可。
feth_send_data(buf, ETH_SEND_BUF);// 在需要处直接调用发送接口发送即可与host对接后,如何识别问题,参照第一种RNDIS初始化方式中的方法即可。
须知: 当没开启lwip时,要求用feth_init_ops函数注册钩子函数,来对接一个网络协议栈,若没有注册钩子函数,RNDIS在加载后,只是一个无任何网络通信功能的虚拟网口,无法解析接收到的网络数据,也无法发送符合网络协议的网络数据。同时,RNDIS虽然也可以直接调用发送接口发送数据,但由于无法自己构造符合网络协议的网络数据,在host端将无任何反应。
Device虚拟网口ECM¶
虚拟网口ECM和虚拟网口RNDIS的加载过程是一样的,在与host对接之前的初始化加载流程可以参考RNDIS的加载过程,其中需要调整的代码如下,只是入参有区别,其他与RNDIS一致。
usbd_set_device_info(DEV_ECM, &str_manufacturer, &str_product, &str_serial_number, dev_id);
usb_init(DEVICE, DEV_ECM);
须知:
ECM对接的host是linux,要求linux开启支持ECM虚拟网口的配置。
ECM对接linux之后,需要先在linux系统中将对应网卡设置IP地址,然后再设置device端的ECM网卡IP地址,否则device端设置IP会失败。
ECM对接网络协议层的方式也与RNDIS一致,可以参考RNDIS。
Device复合设备(串口&网口)¶
复合设备支持单串口与网口复合以及双串口与网口复合,在配置项里,打开串口与网口复合的配置项后,默认是单串口与网口复合,若要双串口与网口复合,需要在串口模式中选择双串口即可。
以上两种复合设备的初始化过程一样,具体过程如下:
tcpip_init(NULL, NULL); // TCP/IP协议栈初始化
usbd_set_device_info(DEV_SER_ETH, &str_manufacturer, &str_product, &str_serial_number, dev_id);
usb_init(DEVICE, DEV_SER_ETH);
virtual_serial_init("/dev/ttyGS0");
system_console_init("/dev/serial");
复合设备中串口和网口的使用同上面“Device虚拟串口”和”Device虚拟网口RNDIS”使用示例。
须知:
虚拟串口功能不能与UART串口同时使用。
由于网口分为RNDIS网口和ECM网口,复合设备初始化的区别在于传入的设备枚举值不同,RNDIS与串口复合时,传入DEV_SER_ETH;ECM与串口复合时,传入DEV_SER_ECM。最终生成的复合设备,用法上与单个的串口或者网口用法一致,可以参考虚拟串口和虚拟网口的介绍
Device DFU¶
目前DFU支持两种升级方式,默认使用方式一。
方式一:通过分配足够大的空间来保存升级文件。
方式二:无需分配足够大的空间,边下载边处理数据。
方式一的升级步骤如下:
在初始化函数中调用以下接口实现USB驱动注册。
usbd_set_device_info(DEV_DFU, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_DFU);
升级开始前,调用usb_dfu_init_env_entities接口设置升级接口类型及环境变量。
调用usb_dfu_update_status,等待升级事件。
等到有升级事件后,调用usb_dfu_update_size_get获取升级文件大小,并对升级文件进行必要的校验,检查升级文件是否携带后缀字段(校验和、标志等关键字),如携带后缀比对校验和。
根据上述升级文件大小,为DFU缓存升级文件提供足够的内存空间,通过usb_dfu_read获取实际升级文件。
获取到实际升级文件后,即可对固件执行升级动作,写入指定Flash地址段。
最后需要调用usb_dfu_free_entities接口释放相关资源。
说明:
给升级的文件提供足够的内存空间主要与厂商提供的升级镜像大小有关,请预留足够的升级的空间。
方式二的升级步骤如下:
在初始化函数中调用以下接口来实现USB驱动注册。
usbd_set_device_info(DEV_DFU, &str_manufacturer, &str_product, &str_serial_number, dev_id); usb_init(DEVICE, DEV_DFU);
业务层需要自行实现弱函数usb_dfu_download_callback,该函数会在“控制传输完成回调函数”里被调用,将host端传来的数据作为入参传入该弱函数,在业务层直接处理每次传输进来的升级数据。
void usb_dfu_download_callback(constuint8_t *buf, uint32_t len); // 弱函数usb_dfu_download_callback的声明最后调用usb_dfu_wait_transmit_finish函数,等待传输完成。
说明:
弱函数usb_dfu_download_callback在中断里被调用,故自行实现的处理数据的代码中,不可使用引起调度的函数,如mutex锁的使用;
由于是边下载边升级,升级过程中需要确保USB中断不受影响,建议升级过程中关闭其他可能影响USB中断的模块或功能。
Device HID¶
HID上层应用在USB初始化之前,先调用usbd_set_device_info接口来设置厂商ID、产品ID、设备序列号以及字符串信息,然后调用hid_report_descriptor_info或者hid_add_report_descriptor接口来配置报告描述符信息,让USB主机识别为具体的HID设备,如键盘、鼠标、带有鼠标功能的键盘等
操作示例
方式1:打开Device HID注册的设备节点,通过调用VFS模块的接口进行HID数据流传输。
调用usbd_set_device_info和hid_report_descriptor_info接口来填充各个描述符信息。
在初始化函数中调用usb_init接口实现USB驱动注册。
usb_init(DEVICE, DEV_HID); // dtype设置为 DEV_HID在上层应用中,调用open函数来打开指定的HID设备。
fd = open("/dev/hid", O_RDWR, 0);在上层应用中,按报告描述符规定的INPUT数据格式写到write函数中的入参buffer,长度也是按报告描述符规定的INPUT长度。若报告描述符定义了OUTPUT项目,则调用read函数去读取USB主机发给设备的数据,读取的大小是按报告描述符规定的OUTPUT长度,若要提高任务执行效率,可以调用poll机制,两者区别是read是永久阻塞模式,而poll机制是定时阻塞模式。
数据传输结束之后,若不使用设备,便调用close函数关闭设备。
方式2:直接调用非VFS模块的对外接口进行HID数据流传输。
调用usbd_set_device_info和hid_add_report_descriptor接口来填充各个描述符信息。
在初始化函数中调用usb_init接口实现USB驱动注册。
usb_init(DEVICE, DEV_HID); // dtype设置为 DEV_HID在上层应用中,按报告描述符规定的INPUT数据格式和长度信息,调用fhid_send_data接口发送HID数据。若报告描述符定义了OUTPUT项目,则调用fhid_recv_data接口去读取USB主机发给设备的数据,读取的HID数据长度与报告描述符规定的OUTPUT长度一致。
须知:
hid_report_descriptor_info接口只能设置一个Report,并和VFS模块的接口配套使用;hid_add_report_descriptor接口可以最多设置两个Report(循环调用接口进行设置,设置前需要配置LOSCFG_DRIVERS_USB_HID_REPORT_MAP_NUM),并和非VFS模块的接口配套使用。这两种方式不允许混用。如果需要重新设置Report,则需要加载并卸载Device HID协议才能设置。
如果报告描述符中没有Report ID项目(鼠标或者键盘报告表),默认的ID值是0,在数据传输过程中,第一个字节不需要填充0,若定义一个以上的Report ID(带有鼠标功能的键盘报告表),此时在传输报表中的第一个字节填充相应的Report ID,输入报表、输出报表与特征报表可以分享同一个Report ID。
如果设置的报告描述符含有Output Report特性,则需要打开LOSCFG_DRIVERS_USB_HID_OUTPUT_REPORT配置才能进行Output传输,当USB主机发给设备数据的时候,建议及时把数据读走,否则USB主机第二次发来的数据会覆盖第一次的数据。
若调用了open函数,此时无法卸载协议,只有调用close函数,才能卸载协议。当前Device HID只允许open一次,再次open就会失败。
上层应用中调用VFS模块的read和write接口或者非VFS模块的fhid_send_data和fhid_recv_data接口时,请确保传进来的buffer和len大小要一致,否则会造成发送或者读取的数据有误。在进行数据流传输前需要配置LOSCFG_DRIVERS_USB_HID_INPUT_REPORT_LEN,即发送hid数据的最大长度,该值必须要做对齐操作,否则会出现数据传输错误,当前对齐策略有两种,
如果传输的内存是non-cache属性,该值必须满足四字节对齐;
如果传输的内存是cache属性,该值必须满足USB_CACHE_ALIGN_SIZE对齐。
USB2.0控制器支持通过SOF中断发送hid数据(需打开配置项LOSCFG_DRIVERS_USB_HID_POLLING_REPORTS),在高速模式下其中断是每隔125μs上报,其回调弱函数void usb_sof_intr_callback(void)需要在业务层实现具体操作,该接口操作时间不能超过125μs,否则会影响传输速率和其他中断操作。
方式1和方式2不能同时使用,若使用方式1,需要打开VFS模块功能。HID业务运行过程中,不允许卸载HID协议,必须要停止应用层的业务操作后,才能卸载协议。
方式1和方式2的接收HID数据接口只能在任务中调用,不能在中断回调调用,发送HID数据接口可以在中断回调调用。
Device UAC&HID&串口复合设备¶
UAC&HID&串口复合设备是最多同时支持麦克风、扬声器、键盘和鼠标、串口功能,扬声器功能可以在配置项里打开或者关闭,而键盘和鼠标功能则是通过调用hid_report_descriptor_info或者hid_add_report_descriptor接口来配置报告描述符信息。相关操作流程,参见上面UAC、HID使用示例。区别在于初始化时,传入的协议类型:
usbd_set_device_info(DEV_UAC_HID, &str_manufacturer, &str_product, &str_serial_number, dev_id);
usb_init(DEVICE, DEV_UAC_HID); // dtype设置为 DEV_UAC_HID
串口功能可裁剪,默认关闭。通过打开LOSCFG_DRIVERS_USB_UAC_HID_EXT_SER_FUNC可开启额外的单串口功能。
API参考¶
该功能模块提供以下接口:
接口名 |
描述 |
|---|---|
usbd_set_device_info |
设置device设备的VID、PID、设备版本号和厂商、产品、序列号字符串信息。 |
usb_init |
初始化USB模块。 |
usb_deinit |
卸载USB模块。 |
usb_is_devicemode |
判断当前USB Device是否已连接USB Host。 |
usb_device_is_host_suspended |
检测USB Host是否进入休眠状态。 |
usb_device_remote_wakeup |
远程唤醒已进入休眠状态的USB Host |
fmass_register_notify |
注册Device notify回调函数。 |
fmass_unregister_notify |
注销Device notify回调函数。 |
fmass_partition_startup |
启动被PC端识别的SD卡、MMC介质或RAM介质分区节点。 |
ram_mass_storage_init |
使能Fat32文件系统镜像内存映射,并注册/dev/uram设备节点。 |
ram_mass_storage_deinit |
非使能Fat32文件系统镜像内存映射。 |
fuvc_frame_descriptors_get |
设置UVC设备视频传输能力集。 |
uvc_open_device |
打开UVC设备。 |
uvc_close_device |
关闭UVC设备。 |
uvc_wait_host |
UVC等待USB HOST连接。 |
uvc_get_state |
获取UVC设备的运行状态。 |
uvc_recv_pack |
启动UVC的视频数据传输,拷贝方式。 |
uvc_video_stop |
停止UVC视频传输。 |
uvc_format_info_get |
获取新码流格式详细信息。 |
uvc_format_status_check |
码流格式切换事件。 |
hi_camera_register_cmd |
应用注册UVC扩展命令,提供底层驱动调用。 |
uac_wait_host |
UAC等待USB HOST连接。 |
fuac_reqbuf_init |
初始化UAC(音频)内存池。 |
fuac_reqbuf_deinit |
释放UAC(音频)内存池。 |
fuac_reqbuf_get |
获取空闲的UAC(音频)内存。 |
fuac_send_message |
音频数据传输。 |
fuac_receive_message |
音频数据传输接收。 |
fuac_opts_set |
设置uac1.0音频参数,包括通道数等。 |
fuac_get_opts |
获取uac1.0音频参数,包括通道数等。 |
fuac_rate_get |
获取uac1.0音频采样率。 |
fuac2_get_opts |
获取音频2.0协议的参数,包括通道数等。 |
fuac2_set_opts |
设置音频2.0协议的参数,包括通道数等。 |
usb_dfu_init_env_entities |
初始化配置DFU接口功能,以及配置环境变量。 |
usb_dfu_update_status |
获取升级事件。 |
usb_dfu_update_size_get |
获取升级文件实际大小。 |
usb_dfu_get_entity |
获取DFU列表中指定的DFU结构体变量。 |
usb_dfu_read |
获取指定DFU列表中的一定大小的缓冲数据。 |
usb_dfu_free_entities |
释放DFU相关资源。 |
usb_dfu_wait_transmit_finish |
等待DFU传输完成。 |
hid_report_descriptor_info |
配置报告描述符信息,设置报告数据格式和功能。 |
hid_add_report_descriptor |
配置多个报告表描述符,设置报告表数据格式、功能和接口协议 |
fhid_send_data |
HID数据传输发送。 |
fhid_recv_data |
HID数据传输接收。 |
uvc_get_frame_buf |
获取帧缓冲区的地址(自研接口)。 |
usb_serial_ioctl |
往USB虚拟串口发送ioctl命令。 |
usb_serial_read |
读取主机往USB虚拟串口发送的数据。 |
usb_serial_write |
往USB虚拟串口发送数据。 |
feth_send_data |
往USB虚拟网口发送的数据。 |
feth_init_ops |
初始化USB虚拟网口与网络协议栈对接的钩子。 |
feth_change_connect_status |
控制虚拟网口断连重连接口 |
usbd_set_device_info¶
【描述】
设置device设备的VID、PID、设备版本号和厂商、产品、序列号字符串信息。
【语法】
uint32_t usbd_set_device_info(device_type dtype,
const struct device_string *str_manufacturer,
const struct device_string *str_product,
const struct device_string *str_serial_number,
struct device_id dev_id);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
dtype |
device 类型:
|
输入 |
str_manufacturer |
厂商字符串信息,结构体第一个成员变量是字符串指针,第二个成员变量是字符串的总长度。 |
输入 |
str_product |
产品字符串信息,结构体成员同上。 |
输入 |
str_serial_number |
产品序列号字符串信息,结构体成员同上。 |
输入 |
dev_id |
设备ID号,结构体第一个成员变量是VID(厂商ID号),第二个成员变量是PID(产品ID号),第三个成员变量是release_num,即设备版本号。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
LOS_OK |
成功 |
LOS_NOK |
失败 |
【需求】
头文件:usb_init.h
库文件:libusb_base.a
【注意】
厂商ID号不可随意设置,需向USB协会申请,产品ID号和设备版本号可根据实际产品进行设置,如果同一个单板切换为不同的USB设备需要更换PID号,否则USB主机识别到的设备驱动会有问题。
字符串长度要和字符串大小一致,且长度范围2~252,传入的入参长度必须是偶数,否则USB主机无法识别字符串信息。
【举例】
须知: 以下示例仅供参考,不可直接使用,特别是VID号,请务必修改为客户自己的厂商信息。
const char manufacturer[38] = {
'H', 0, 'i', 0, 'S', 0, 'i', 0, 'l', 0, 'i', 0, 'c', 0, 'o', 0, 'n', 0,
'(', 0, 'S', 0, 'h', 0, 'a', 0, 'n', 0, 'g', 0, 'h', 0, 'a', 0, 'i', 0, ')', 0
};
struct device_string str_manufacturer = {
.str = manufacturer,
.len = 38
};
const char product[18] = {
'U', 0, 'S', 0, 'B', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0,
'C', 0, 'E', 0
};
struct device_string str_product = {
.str = product,
.len = 18
};
const char serial[16] = {
'2', 0, '0', 0, '2', 0, '0', 0, '0', 0, '6', 0, '2', 0,
'4', 0
};
struct device_string str_serial_number = {
.str = serial,
.len = 16
};
struct device_id dev_id = {
.vendor_id = 0x3361,
.product_id = 0x0102,
.release_num = 0x0318
};
uint32_t ret;
ret = usbd_set_device_info(DEV_UVC, &str_manufacturer, &str_product, &str_serial_number, dev_id);
if (ret != 0) {
dprintf("set fail!\n");
}
【相关主题】
无。
usb_init¶
【描述】
初始化USB模块。
【语法】
uint32_t usb_init(controller_type ctype, device_type dtype);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
ctype |
枚举类型,描述了usb是host类型或device类型:
|
输入 |
dtype |
枚举类型,
|
输入 |
【返回值】
返回值 |
描述 |
|---|---|
LOS_OK |
成功。 |
LOS_NOK |
失败。 |
【需求】
头文件:usb_init.h
库文件:libusb_base.a
【注意】
USB各协议使用时与其他模块的依赖关系,参考“编程实例”。
不支持重复执行,切换协议需要调用usb_deinit卸载当前协议。
【举例】
参考“编程实例”。
【相关主题】
usb_deinit¶
【描述】
卸载USB模块。
【语法】
uint32_t usb_deinit(void);
【参数】
无
【返回值】
返回值 |
描述 |
|---|---|
LOS_OK |
成功。 |
LOS_NOK |
失败。 |
【需求】
头文件:usb_init.h
库文件:libusb_base.a
【注意】
无
【举例】
参考“编程实例”。
【相关主题】
usb_is_devicemode¶
【描述】
判断当前USB Device是否已连接USB Host。
【语法】
bool usb_is_devicemode(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
TRUE |
当前USB Device已连接USB Host。 |
FALSE |
当前USB Device未连接USB Host。 |
【需求】
头文件:usb_init.h
库文件:libusb_base.a
【注意】
此接口要在加载USB Device协议后再去调用。
【举例】
无。
【相关主题】
无。
usb_device_is_host_suspended¶
【描述】
检测USB Host是否进入休眠状态。
【语法】
bool usb_device_is_host_suspended(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
true |
当前USB Host已进入休眠状态。 |
false |
当前USB Host不在休眠状态。 |
【需求】
头文件:usb_init.h
库文件:libusb_base.a
【注意】
此接口要在加载USB2.0 控制器后再去调用。
【举例】
无
【相关主题】
usb_device_remote_wakeup¶
【描述】
远程唤醒已进入休眠状态的USB Host。
【语法】
int usb_device_remote_wakeup(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
0 |
表示远程唤醒操作成功。 |
-1 |
表示远程唤醒操作失败。 |
【需求】
头文件:usb_init.h
库文件:libusb_base.a
【注意】
此接口要在加载USB2.0 控制器后再去调用。
【举例】
无
【相关主题】
fmass_register_notify¶
【描述】
注册Device notify回调函数。
【语法】
int fmass_register_notify(void (*notify)(void *context, int status), void *context);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
notify |
Notify回调函数指针。 |
输入 |
context |
需要传入Notify回调函数的私有数据指针,该指针会传入给Notify回调函数的context。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 ~ MAX_NOFIFY_NUM-1 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_mass_storage.h
库文件:libusb_base.a
【注意】
允许注册多个notify回调函数,USB2.0 Device拔插事件发生时驱动会调用每一个notify函数。
【举例】
参考编程实例。
【相关主题】
fmass_unregister_notify¶
【描述】
注销Device notify回调函数。
【语法】
int fmass_unregister_notify(int handle);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
handle |
fmass_register_notify返回的句柄值。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_mass_storage.h
库文件:libusb_base.a
【注意】
无
【举例】
参考“编程实例”。
【相关主题】
fmass_partition_startup¶
【描述】
启动被PC端识别的SD卡、MMC介质或RAM介质分区节点。
【语法】
int fmass_partition_startup(const char *path);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
path |
SD卡、MMC介质或RAM介质分区节点,该设备节点必须是有效的。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_mass_storage.h
库文件:libusb_base.a
【注意】
无
【举例】
参考编程实例。
【相关主题】
ram_mass_storage_init¶
【描述】
使能FAT32文件系统镜像内存映射,并注册/dev/uram设备节点。
【语法】
int ram_mass_storage_init(void);
【参数】
无
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_mass_storage_ram.h
库文件:libusb_base.a
【注意】
无。
【举例】
参考“编程实例”。
【相关主题】
ram_mass_storage_deinit¶
【描述】
禁用FAT32文件系统镜像的内存映射。
【语法】
int ram_mass_storage_deinit(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_mass_storage_ram.h
库文件:libusb_base.a
【注意】
无
【举例】
参考“编程实例”。
【相关主题】
fuvc_frame_descriptors_get¶
【描述】
设置UVC设备视频传输能力集。
【语法】
void fuvc_frame_descriptors_get(const struct fuvc_format_info *format_info, uint32_t frames_count);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
format_info |
UVC支持的视频码流格式以及分辨率。 |
输入 |
frames_count |
视频格式对应的分辨率的总个数。 |
输入 |
【返回值】
无。
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
参考“编程实例”。
【举例】
参考“编程实例”。
【相关主题】
无
uvc_open_device¶
【描述】
打开UVC设备。
【语法】
int uvc_open_device(uvc_t *hdl, const struct uvc_open_param *param);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
hdl |
用于存储UVC设备句柄。 |
输出 |
param |
打开UVC设备的初始能力,使用中根据实际需求可变化修改。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_NOMATCH |
UVC状态不匹配。 |
UVC_ERROR_HANDLE |
UVC句柄无效。 |
UVC_ERROR_PTR |
指针非法。 |
UVC_ERROR_MEMORY |
内存不足。 |
UVC_ERROR_VALUE |
取值非法。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
参考“编程实例”。
【举例】
参考“编程实例”。
【相关主题】
uvc_close_device¶
【描述】
关闭UVC设备。
【语法】
int uvc_close_device(uvc_t hdl);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
hdl |
用于存储UVC设备句柄。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_FATAL |
致命错误。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
参考“编程实例”。
【举例】
参考“编程实例”。
【相关主题】
uvc_wait_host¶
【描述】
等待USB HOST连接。
【语法】
int uvc_wait_host(uvc_t hdl, int wait_option, int *connected);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
hdl |
UVC设备句柄。 |
输入 |
wait_option |
等待连接时的状态:
|
输入 |
connected |
当前连接状态:
|
输出 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_NOMATCH |
UVC状态不匹配。 |
UVC_ERROR_HANDLE |
UVC句柄无效。 |
UVC_ERROR_PTR |
指针非法。 |
UVC_ERROR_VALUE |
取值非法。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
参考“编程实例”。
【举例】
参考“编程实例”。
【相关主题】
uvc_get_state¶
【描述】
获取UVC设备的运行状态。
【语法】
int uvc_get_state(uvc_t hdl, uint32_t *state);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
hdl |
UVC设备句柄。 |
输入 |
state |
UVC设备运行状态:
|
输出 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_HANDLE |
UVC句柄无效。 |
UVC_ERROR_PTR |
指针非法。 |
UVC_ERROR_VALUE |
取值非法。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
无。
【举例】
无。
【相关主题】
无。
uvc_recv_pack¶
【描述】
UVC零拷贝模式发送数据包。
【语法】
int uvc_recv_pack(const struct uvc_pack *pack);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
pack |
指向UVC数据包的指针。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_MEMORY |
内存申请失败。 |
UVC_ERROR_HANDLE |
UVC句柄无效。 |
UVC_ERROR_PTR |
指针非法。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
参考“编程实例”。
【举例】
参考“编程实例”。
【相关主题】
uvc_video_stop¶
【描述】
停止UVC视频传输。
【语法】
int uvc_video_stop(uvc_t hdl);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
hdl |
UVC设备句柄。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_NOMATCH |
UVC状态不匹配。 |
UVC_ERROR_HANDLE |
UVC句柄无效。 |
UVC_ERROR_PTR |
指针非法。 |
UVC_ERROR_VALUE |
取值非法。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
参考“编程实例”。
【举例】
参考“编程实例”。
【相关主题】
uvc_format_info_get¶
【描述】
获取新码流格式详细信息。
【语法】
int uvc_format_info_get(struct uvc_format_info *info);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
info |
struct uvc_format_info{
uint32_t width; // 分辨率,宽
uint32_t height; // 分辨率,高
uint32_t format; // 码流格式
uint32_t status; // 1-格式切换中;0-切换完成
} __attribute__((packed));
|
输出 |
【返回值】
返回值 |
描述 |
|---|---|
UVC_OK |
成功。 |
UVC_ERROR_NOMATCH |
UVC状态不匹配。 |
UVC_ERROR_PTR |
指针非法。 |
UVC_ERROR_FATAL |
致命错误。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
无。
【举例】
无。
【相关主题】
uvc_format_status_check¶
【描述】
码流格式切换事件。
【语法】
enum format_switch_status uvc_format_status_check(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
FORMAT_SWITCH_FINISH |
格式切换完成。 |
FORMAT_SWITCH_PENDING |
格式切换中。 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
无。
【举例】
无。
【相关主题】
hi_camera_register_cmd¶
【描述】
应用注册UVC扩展命令,提供底层驱动调用。
【语法】
int hi_camera_register_cmd(const struct uvc_camera_cmd *cmd);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
cmd |
UVC扩展命令: struct uvc_camera_cmd {
uint32_t id; // 事件码
uint8_t name[32]; // 名称
camera_control_func uvc_control_func;
// 处理函数指针信息的数组
};
typedef uint32_t (*camera_control_func)(void *buf, uint32_t len, uint32_t event_id, uint32_t cmd_type); |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功 |
-1 |
失败 |
【需求】
头文件:f_uvc.h
库文件:libusb_base.a
【注意】
无。
【举例】
无。
【相关主题】
无。
uac_wait_host¶
【描述】
等待USB HOST连接。
【语法】
int uac_wait_host(int wait_option);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
wait_option |
等待连接时的状态:
|
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UAC_OK |
成功。 |
UAC_ERROR_NOMATCH |
状态不匹配。 |
UAC_ERROR_PTR |
指针非法。 |
UAC_ERROR_VALUE |
参数非法。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
fuac_reqbuf_init¶
【描述】
初始化UAC(音频)内存池。
【语法】
int fuac_reqbuf_init(uint32_t buf_count, uint32_t data_len);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
buf_count |
音频内存缓存的个数。 |
输入 |
data_len |
每个音频内存的长度。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UAC_OK |
成功。 |
UAC_ERROR_MEMORY |
内存不足。 |
UAC_ERROR_PTR |
参数非法。 |
UAC_ERROR_VALUE |
不正确的值。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
不能重复初始化,已初始化的内存池必须先调用释放接口再去初始化。
【举例】
无。
【相关主题】
fuac_reqbuf_deinit¶
【描述】
释放UAC(音频)内存池。
【语法】
int fuac_reqbuf_deinit(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
UAC_OK |
成功。 |
UAC_ERROR_VALUE |
不正确的值。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
释放之前必须先把申请到的内存全部归还给USB层,才能去释放,否则失败。一旦出现采样率或者声道数或者位深出现切换,则必须要释放内存然后重新初始化内存池。
【举例】
无。
【相关主题】
fuac_reqbuf_get¶
【描述】
获取空闲的UAC(音频)内存。
【语法】
uint8_t *fuac_reqbuf_get(uint32_t *buf_index);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
buf_index |
获取到音频内存对应的索引。 |
输出 |
【返回值】
返回值 |
描述 |
|---|---|
UINTPTR |
音频内存对应的地址。 |
NULL |
空指针。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
不使用音频内存时,必须调用fuac_send_message接口,把获取到的内存归还给USB。
【举例】
无。
【相关主题】
fuac_send_message¶
【描述】
音频数据传输发送。
【语法】
int fuac_send_message(const uint8_t *buf, uint32_t len, uint32_t buf_index);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
buf |
待传输的音频数据。 |
输入 |
len |
待传输的音频数据长度。 |
输入 |
buf_index |
待传输的音频数据buf的索引号。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
UAC_OK |
成功 |
UAC_ERROR_PTR |
指针非法 |
UAC_ERROR_VALUE |
不正确的值 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
入参buf_index是通过调用fuac_reqbuf_get接口返回值的buf_index。fuac_reqbuf_get和fuac_send_message接口一一对应,顺序申请和顺序发送,不要乱序发送,否则Host出现声音不正常。
【举例】
无。
【相关主题】
fuac_receive_message¶
【描述】
音频数据传输接收。
【语法】
int fuac_receive_message(void *buf, int len, int timeout);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
buf |
待接收的音频数据缓存。 |
输入 |
len |
接收音频数据缓存大小。 |
输入 |
timeout |
接收音频数据的超时时间。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
int |
接收音频数据的实际大小。 |
-UAC_ERROR_MEMORY |
内存不足。 |
-UAC_ERROR_PTR |
指针非法。 |
-UAC_ERROR_DISCONNECT |
设备已断开。 |
-UAC_ERROR_TIMEOUT |
等待超时。 |
-UAC_ERROR_EVENT |
错误事件。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
fuac_opts_set¶
【描述】
设置UAC1.0音频参数。
【语法】
int fuac_opts_set(const struct uac_opts *opts);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
opts |
音频参数结构体。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
空指针。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
fuac_get_opts¶
【描述】
获取UAC1.0音频参数。
【语法】
int fuac_get_opts(struct uac_opts *opts);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
opts |
音频参数结构体。 |
输出 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-UAC_ERROR_PTR |
指针非法。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
fuac_rate_get¶
【描述】
获取uac1.0音频采样率。
【语法】
uint32_t fuac_rate_get(void);
【参数】
无
【返回值】
返回值 |
描述 |
|---|---|
uint32_t类型数值 |
UAC1.0当前音频采样率。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
fuac2_get_opts¶
【描述】
获取音频2.0协议的参数,包括通道数等。
【语法】
int fuac2_get_opts(struct uac2_opts *opts);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
opts |
当前的音频参数结构体。 |
输出 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-UAC_ERROR_PTR |
空指针。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
fuac2_set_opts¶
【描述】
设置UAC2.0音频参数。
【语法】
int fuac2_set_opts(const struct uac2_opts *opts);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
opts |
音频参数结构体。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-UAC_ERROR_PTR |
空指针。 |
-UAC_ERROR_VALUE |
不支持的参数设置。 |
【需求】
头文件:f_uac.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
usb_dfu_init_env_entities¶
【描述】
初始化配置DFU接口功能,配置环境变量(目前只支持RAM)。
【语法】
int usb_dfu_init_env_entities(const char *type, char *envstr, const char *devstr);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
type |
DFU功能,只支持RAM。 |
输入 |
envstr |
DFU环境参数。 |
输入 |
devstr |
未使用。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_dfu_update_status¶
【描述】
获取升级事件。
【语法】
uint32_t usb_dfu_update_status(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
0 |
未启动升级。 |
1 |
升级中。 |
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_dfu_update_size_get¶
【描述】
获取升级文件实际大小。
【语法】
uint64_t *usb_dfu_update_size_get(void);
【参数】
无。
【返回值】
返回值 |
描述 |
|---|---|
uint64_t * |
升级文件实际大小的保存地址。 |
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_dfu_get_entity¶
【描述】
获取列表中指定的DFU结构体变量。
【语法】
struct usb_dfu_entity *usb_dfu_get_entity(int alter);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
alter |
获取列表中存储变量的序列号。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
struct usb_dfu_entity * |
DFU结构体变量,存储当前使用的DFU各个参数值,参见结构体声明。 |
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_dfu_read¶
【描述】
获取指定DFU列表中的一定大小的缓冲数据。
【语法】
int usb_dfu_read(struct usb_dfu_entity *dfu, void *buf, int size, uint32_t blk_seq_num);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
dfu |
获取缓冲数据所在的DFU列表结构信息,通过usb_dfu_get_entity获取。 |
输入 |
buf |
接收缓存buffer。 |
输入 |
size |
接收数据大小。 |
输入 |
blk_seq_num |
获取的次数计数,须是连续自增值。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
int |
获取到的数据实际长度。 |
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_dfu_free_entities¶
【描述】
释放DFU相关资源。
【语法】
void usb_dfu_free_entities(void)
【参数】
无。
【返回值】
无。
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_dfu_wait_transmit_finish¶
【描述】
等待DFU传输完成。
【语法】
int usb_dfu_wait_transmit_finish(uint32_t timeout);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
timeout |
等待超时时间。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
传输未完成。 |
-1 |
协议未加载或传输错误。 |
1 |
传输完成。 |
【需求】
头文件:f_dfu.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
hid_report_descriptor_info¶
【描述】
配置报告描述符信息,设置报告数据格式和功能。
【语法】
int hid_report_descriptor_info(const void *report_desc, size_t report_desc_len);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
report_desc |
报告描述符信息。 |
输入 |
report_desc_len |
报告描述符总大小。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
成功。 |
-1 |
失败。 |
【需求】
头文件:f_hid.h
库文件:libusb_device.a
【注意】
报告描述符的定义要遵循标准HID协议,与VFS模块接口配套使用。
【举例】
请参考HID官方文档:《Device Class Definition for Human interface Devices》和《HID Usage Tables》。
【相关主题】
无。
hid_add_report_descriptor¶
【描述】
配置多个报告表描述符,设置报告表数据格式、功能和接口协议。
【语法】
int hid_add_report_descriptor(const uint8_t *report_desc, size_t report_desc_len, uint8_t protocol);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
report_desc |
报告描述符信息。 |
输入 |
report_desc_len |
报告描述符总大小。 |
输入 |
protocol |
默认值为0,如果需要支持启动接口,需要传入1或者2,1为键盘;2为鼠标。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
report_index |
成功(对应的报表索引值)。 |
-1 |
失败。 |
【需求】
头文件:f_hid.h
库文件:libusb_device.a
【注意】
报告描述符的定义要遵循标准HID协议,与fhid_send_data和fhid_recv_data接口配套使用。
【举例】
请参考HID官方文档:《Device Class Definition for Human interface Devices》和《HID Usage Tables》。
【相关主题】
无。
fhid_send_data¶
【描述】
HID数据传输发送。
【语法】
ssize_t fhid_send_data (uint8_t report_index, const char *buf, size_t buflen);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
report_index |
报表索引值,通过hid_add_report_descriptor接口返回值得到。 |
输入 |
buf |
需要发送的HID数据。 |
输入 |
buflen |
HID数据的总大小。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
ssize_t |
已发送HID数据的实际大小。 |
-1 |
失败。 |
【需求】
头文件:f_hid.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
fhid_recv_data¶
【描述】
HID数据传输接收。
【语法】
ssize_t fhid_recv_data(uint8_t report_index, char *buf, size_t buflen);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
report_index |
报表索引值,通过hid_add_report_descriptor接口返回值得到。 |
输入 |
buf |
需要接收HID数据的缓存。 |
输入 |
buflen |
接收HID数据缓存的长度。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
ssize_t |
获取到的数据实际长度。 |
-1 |
失败。 |
【需求】
头文件:f_hid.h
库文件:libusb_device.a
【注意】
由于此接口存在读数据阻塞机制,所以只能在任务中调用接口,不能在中断回调调用。
【举例】
无。
【相关主题】
无。
uvc_get_frame_buf¶
【描述】
获取帧缓冲区的地址。
【语法】
uint8_t *uvc_get_frame_buf(struct file *filep, uint32_t index, uint32_t offset);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
filep |
字符设备文件句柄,通过文件描述符fd进行获取。 |
输入 |
index |
当前缓存帧的索引号。 |
输入 |
offset |
帧数据内容的起点,通过VIDIOC_QUERYBUF查询申请。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
uint8_t * |
返回帧缓冲区的地址。 |
【需求】
头文件:usb_v4l2.h
库文件:libusb_base.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_serial_ioctl¶
【描述】
往USB虚拟串口发送ioctl命令。
【语法】
int usb_serial_ioctl(uint32_t index, int cmd, unsigned long arg);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
index |
虚拟串口索引,单串口时只能是0,双串口时可以输入0或1。 |
输入 |
cmd |
待发送命令,目前只支持值为0x104的命令,发送后此命令后可以控制read接口是否挂起等待的读。 |
输入 |
arg |
发送命令附带对应的参数,值为0x104命令附带的参数值,目前可以传入0和非0数,非0为控制读接口挂起等待读取数据;传0则是不挂起等待的读取数据。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
发送命令成功。 |
-25 |
入参cmd为未知命令。 |
-22 |
USB虚拟串口正在卸载或者入参索引无效。 |
【需求】
头文件:usbd_acm.h
库文件:libusb_device.a
【注意】
无
【举例】
usb_serial_ioctl(0, 0x104, 1); // 建议使用值为0x104的宏表示cmd命令,最后一个入参传1,表示控制虚拟串口读取数据时,等待挂起,直到接收到数据为止
usb_serial_ioctl(0, 0x104, 0); // 最后一个入参传0,表示控制虚拟串口调用读接口读取数据时,不管有没有收到数据,立即返回读结果
【相关主题】
无。
usb_serial_read¶
【描述】
读取来自主机往USB虚拟串口发送的数据。
【语法】
ssize_t usb_serial_read(uint32_t index, char *buffer, size_t buflen);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
index |
虚拟串口索引,单串口时只能是0,双串口时可以输入0或1。 |
输入 |
buffer |
需要接收串口数据的缓存。 |
输入 |
buflen |
接收到的串口数据的缓存长度。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
ssize_t |
已接收串口数据的缓存的实际大小。 |
-1 |
接收失败。 |
-22 |
入参有误。 |
【需求】
头文件:usbd_acm.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
usb_serial_write¶
【描述】
往USB虚拟串口发送的数据。
【语法】
ssize_t usb_serial_write(uint32_t index, const char *buffer, size_t buflen);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
index |
虚拟串口索引,单串口时只能是0,双串口时可以输入0或1。 |
输入 |
buffer |
需要发送的USB虚拟串口数据。 |
输入 |
buflen |
已发送的USB虚拟串口数据大小。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
ssize_t |
已发送USB虚拟串口数据大小。 |
-1 |
发送失败。 |
-22 |
入参有误。 |
【需求】
头文件:usbd_acm.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
feth_send_data¶
【描述】
往USB虚拟网口发送的数据。
【语法】
void feth_send_data(const uint8_t *buf, uint32_t buflen);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
buf |
需要发送给host网络数据。 |
输入 |
buflen |
需要发送给host网络数据长度。 |
输入 |
【返回值】
无。
【需求】
头文件:f_eth.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
feth_init_ops¶
【描述】
初始化USB虚拟网口与网络协议栈对接的钩子。
【语法】
int feth_init_ops(struct usb_eth_operations* ops);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
ops |
与网络协议栈对接函数指针结构体。 |
输入 |
【返回值】
返回值 |
描述 |
|---|---|
0 |
初始化成功。 |
-1 |
初始化失败。 |
【需求】
头文件:f_eth.h
库文件:libusb_device.a
【注意】
无。
【举例】
无。
【相关主题】
无。
feth_change_connect_status¶
【描述】
控制虚拟网口断连重连接口。
【语法】
void feth_change_connect_status(bool connect);
【参数】
参数名称 |
描述 |
输入/输出 |
|---|---|---|
connect |
设置虚拟网口断开或连接的参数,true表示连接,false表示断开。 |
输入 |
【返回值】
无。
【需求】
头文件:f_eth.h
库文件:libusb_device.a
【注意】
此接口只是通知host,虚拟网口需要断开或连接,至于host端有没有真的将虚拟网口断开或连接,属于host的行为,device端并不知晓。
【举例】
无。
【相关主题】
无。
UART驱动开发¶
概述¶
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器),在嵌入式设计中,UART用来与PC进行通信,包括与监控调试器和其它器件,如EEPROM通信。
Huawei_LiteOS UART驱动模块的基本框架包括文件适配层、驱动抽象层、驱动实现层。
文件适配层:采用LOSCFG_FS_VFS管控,可裁剪,提供通过文件方式控制uart。
驱动抽象层:提供对外接口,提供通过struct LosDevice设备文件方式控制uart。
驱动实现层:uart控制器硬件实现层,不提供对外接口。
图 1 UART驱动基本框架

运作机制
UART驱动主要开发流程如下:
UART驱动加载:
UART驱动的加载由驱动抽象层提供加载接口,系统在启动过程中注册驱动。
UART设备加载:
UART设备由系统根据需要及实际情况进行加载。
UART数据收发:
驱动抽象层提供裸读写接口,文件适配层提供VFS文件方式读写。
开发指导¶
裸读写UART¶
UART驱动抽象层提供如下的接口:
接口名 |
描述 |
|---|---|
struct LosDevice *UartDevGetByPort(int num) |
通过串口号获取UART设备。 |
int UartOpen(struct LosDevice *dev) |
打开指定的UART设备。 |
int UartClose(struct LosDevice *dev) |
关闭指定的UART设备。 |
ssize_t UartRead(struct LosDevice *dev, char *buf, size_t count) |
读取指定的UART设备的数据。 |
ssize_t UartWrite(struct LosDevice *dev, const char *buf, size_t count) |
向指定的UART设备写数据。 |
int UartIoctl(struct LosDevice *dev, int cmd, unsigned long arg) |
控制指定的UART设备(具体支持的控制方式由UART控制器的驱动实现层提供)。 |
int UartSuspend(struct LosDevice *dev) |
挂起指定的UART设备。 |
int UartResume(struct LosDevice *dev) |
恢复指定的UART设备。 |
VOID UartEarlyInit(VOID); |
串口初始化。 |
VOID UartPuts(const CHAR *s, UINT32 len, BOOL isLock); |
串口输出(系统默认)。 |
VOID UartPutsReg(UINTPTR regBase, const CHAR *s, UINT32 len, BOOL isLock); |
串口输出(用户指定基地址)。 |
文件方式读写UART¶
参考“VFS”,支持open、close、read、write、ioctl操作。
模块配置¶
打开菜单,进入Driver → Uart Type菜单,完成UART模块的配置。
配置项 |
描述 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_DRIVERS_SIMPLE_UART |
简单的UART,只支持输出。 |
YES/NO |
YES |
无 |
LOSCFG_DRIVERS_UART |
完整的UART,支持输入输出。 |
YES/NO |
NO |
LOSCFG_DRIVERS_BASE |
进入Driver → Main Uart Driver菜单,选择用于串口输出的默认串口驱动,可以选择如下配置:
配置项 |
描述 |
依赖 |
|---|---|---|
LOSCFG_DRIVERS_UART_MAIN_ARM_PL011 |
使用ARM PL011作为默认串口 |
LOSCFG_DRIVERS_UART_ARM_PL011 |
LOSCFG_DRIVERS_UART_MAIN_HQ_UART |
使用HQ UART系列作为默认串口 |
LOSCFG_DRIVERS_UART_HQ_UART |
LOSCFG_DRIVERS_UART_MAIN_VUART_RPQ |
使用虚拟UART作为默认串口 |
LOSCFG_DRIVERS_UART_VUART_RPQ |
编程实例¶
simple uart¶
使能LOSCFG_DRIVERS_SIMPLE_UART,simple uart只支持输出。
general uart¶
使能LOSCFG_DRIVERS_UART,general uart既支持输出,也支持输入。
裸读写UART¶
实例描述
通过串口号获取UART设备。
打开串口。
写串口。
挂起串口。
恢复串口。
关闭串口。
编程示例
#define BUF_SIZE 100
UINT32 TestCase(VOID)
{
struct LosDevice *uartDev = NULL;
int ret;
ssize_t rtRet;
char dataBuf[BUF_SIZE] = {"test uart\n"};
uartDev = UartDevGetByPort(0);
if (uartDev == NULL) {
return LOS_NOK;
}
ret = UartOpen(uartDev);
if (ret != 0) {
return LOS_NOK;
}
rtRet = UartWrite(uartDev, dataBuf, BUF_SIZE);
if (rtRet <= 0) {
goto OUT_CLOSE;
}
ret = UartSuspend(uartDev);
if (ret != 0) {
goto OUT_CLOSE;
}
ret = UartResume(uartDev);
if (ret != 0) {
goto OUT_CLOSE;
}
ret = UartClose(uartDev);
if (ret != 0) {
return LOS_NOK;
}
return LOS_OK;
OUT_CLOSE:
(VOID)UartClose(uartDev);
return LOS_NOK;
}
文件读写UART¶
实例描述
使能LOSCFG_FS_VFS。
打开串口。
写串口。
挂起串口。
恢复串口。
关闭串口。
编程示例
#define BUF_SIZE 100
#define UART_DEV_PATH "/dev/uartdev-0"
UINT32 TestCase(VOID)
{
int ret;
int fd;
char dataBuf[BUF_SIZE] = {"test uart\n"};
ssize_t writeLen;
fd = open(UART_DEV_PATH, O_RDWR);
if (fd == -1) {
return LOS_NOK;
}
writeLen = write(fd, dataBuf, BUF_SIZE);
if (writeLen <= 0) {
goto OUT_CLOSE;
}
ret = ioctl(fd, Pl011_IOCTL_SUSPEND, 0);
if (ret != 0) {
goto OUT_CLOSE;
}
ret = ioctl(fd, Pl011_IOCTL_RESUME, 0);
if (ret != 0) {
goto OUT_CLOSE;
}
ret = close(fd);
if (ret != 0) {
return LOS_NOK;
}
return LOS_OK;
OUT_CLOSE:
(VOID)close(fd);
return LOS_NOK;
}
配置工具¶
概述¶
LiteOS使用Kconfig文件配置系统。所用的Kconfig语言是一种菜单配置语言,config.in和Kconfig都由该语言编写而成。LiteOS使用python kconfiglib来解析、展示Kconfig文件。解析Kconfig文件后,会在根目录下生成/更新“.config”文件,同时在开发板的include文件夹下生成“menuconfig.h”。使用menuconfig前,需要先安装python和kconfiglib。
安装工具¶
安装python 2.7/3.2+。
下面以python3.10为例介绍安装方法。
命令行方式安装:
sudo apt-get install python3.10源码包编译安装:
通过官网下载python源码包。
解压源码包。
参考如下命令完成解压,将压缩包名替换为实际下载的源码包名:
tar -xf Python-3.10.2.tgz检查依赖。
解压后进入到目录中,执行“./configure”命令以检查编译与安装python所需的依赖:
cd Python-3.10.2 ./configure
如果没有报错就继续下一步操作,如果存在报错就根据提示安装依赖。
编译&安装python。
sudo make sudo make install
检查python版本并正确链接python命令。
python --version如果显示的不是刚刚安装的python版本,则需要执行以下命令来正确链接python命令。
获取python目录,例如对于python 3.10.2,执行如下命令。
which python3.10链接python命令到刚刚安装的python包。
将以下命令中的“python3.10-path“替换为“which python3.10”命令执行后的回显路径:
cd /usr/bin sudo rm python3 && sudo ln -s "python3.10-path" python3 sudo rm python && sudo ln -s python3 python
再次检查python版本。
python --version python3 --version
安装pip包管理工具。
如果pip命令不存在,可以下载pip源码包进行安装。pip依赖setuptools,如果setuptools不存在,也需要安装。
命令行方式安装:
sudo apt-get install python3-setuptools python3-pip -y sudo pip3 install --upgrade pip
源码包方式安装:
安装setuptools。
点击setuptools源代码包下载地址,可以参考下面的命令进行安装:
sudo unzip setuptools-50.3.2.zip cd setuptools sudo python setup.py install
须知:
setuptools最新版本不支持python 2.7,如果使用python 2.7,请下载setuptools 45.0.0版本以支持python 2.7。安装pip。
点击pip源代码包下载地址,可以参考下面的命令进行安装:
sudo tar -xf pip-20.2.4.tar.gz cd pip-20.2.4 sudo python setup.py install
安装kconfiglib库。
对于服务器可以联网的情况。
可以直接使用如下命令安装kconfiglib:
sudo pip install kconfiglib对于服务器不能联网的情况。
可以采用离线的方式安装。首先在其他能联网的环境上下载kconfiglib,可以下载kconfiglib的wheel文件“kconfiglib-14.1.0-py2.py3-none-any.whl”或源代码文件“kconfiglib-14.1.0.tar.gz”,这里以14.1.0版本为例。
wheel文件的安装,可以参考如下命令:
sudo pip install kconfiglib-14.1.0-py2.py3-none-any.whl源代码文件的安装,可以参考如下命令:
sudo tar -zxvf kconfiglib-14.1.0.tar.gz cd kconfiglib-14.1.0 sudo python setup.py install
使用指导¶
使用menuconfig配置工具前,请确保已经安装编译LiteOS的交叉编译器工具链,并加入环境变量。
为满足不同的使用场景,配置工具支持下述命令。根据使用场景,在根目录下执行下述其中一个命令即可。在执行命令前,先根据开发板拷贝“self_src/tools/build/config/”目录下的默认配置文件“${platform}.config”到根目录,并重命名为“.config”。除了“.config”文件外,还有一个重要的配置文件“build/menuconfig/config.in”文件(该文件包含了各个模块的Kconfig文件)。“.config”文件决定了各个配置项的默认值,“config.in”文件决定了展示图形化界面的配置项。
make defconfig¶
在LiteOS根目录下执行“make defconfig”命令会解析根目录下的“.config”文件(cmake当前不支持该功能),使所有的配置项尽可能使用其默认配置,并更新“.config”,同时在对应的LiteOS开发板工程的子目录include下生成“menuconfig.h”。
make allyesconfig¶
在LiteOS根目录下执行“make allyesconfig”命令会解析根目录下的“.config”文件(cmake当前不支持该功能),使所有的配置项尽可能使能,即设置为Y,并更新“.config”,同时在对应的LiteOS开发板工程的子目录include下生成“menuconfig.h”。
make allnoconfig¶
在LiteOS根目录下执行“make allnoconfig”命令会解析根目录下的“.config”文件(cmake当前不支持该功能),使所有的配置项尽可能禁用,即设置为“is not set”,并更新“.config”,同时在对应的LiteOS开发板工程的子目录include下生成“menuconfig.h”。
配置项说明¶
配置项简介¶
运行“make menuconfig”,进入LiteOS Configuration主界面,目前有Compiler、Targets、Kernel、Debug、SSP等选项。下面对Compiler,Targets和SSP选项进行介绍,其余选项在各个模块章节中均有介绍,这里不再赘述。
Compiler配置¶
Compiler选项中包含了两个子菜单:Compiler交叉编译器工具链和Optimize Option编译优化选项。

现有多种交叉编译器可选:包括“arm-himix410-linux”和“arm-none-eabi-”等,请选择实际使用的编译器。
有三种编译优化选项可选:Optimize None(不做编译优化)、Optimize Speed(优化编译速度)和Optimize Size(优化编译体积)。
Targets配置¶
Targets选项中包含Family(产品)、Board(芯片)、Enable Floating Pointer Unit(使能浮点数)、Enable Access Permission Control(使能访问权限控制)。

Family:选择产品,其中BVT、DPT、AIOT等都是产品名,SECRET是保密项目,QEMU和STM32是LiteOS内部使用的。
Board:选择具体芯片,目前支持Hi3161、Hi3372、Hi3322,Hi1A21等。
Enable Access Permission Control:使能访问权限控制,可以根据不同架构使能对应的内存管理单元,包含RiscV下的PMP。
配置项描述
打开菜单,进入Targets菜单,相关配置项介绍如下,可根据配置项名称搜到其具体所在位置。
配置项 |
含义 |
取值范围 |
默认值 |
架构 |
|---|---|---|---|---|
LOSCFG_ARCH_SECURE_MONITOR_MODE |
令OS处于EL3运行 |
YES/NO |
NO |
ARM64 |
LOSCFG_ARCH_NON_SECURE_OS_MODE |
令OS处于非安全Guest OS模式下运行(EL1-NS) |
YES/NO |
NO |
|
LOSCFG_ARCH_SECURE_OS_MODE |
令OS处于安全Guest OS模式下运行(EL1-S) |
YES/NO |
NO |
|
LOSCFG_ARCH_ARM_AARCH64_LSE_ATOMIC |
使能ARMv8.1-LSE Atomic指令集扩展 |
YES/NO |
YES |
|
LOSCFG_ARCH_ARM_SVE2 |
使能ARMv8.2 SVE并行化功能 |
YES/NO |
NO |
ARM64 |
须知: OS运行模式需要target进行适配,具体可以使用的模式以交付的模式为准。
SSP配置¶
选择“Stack Smashing Protector (SSP) Compiler Feature”选项,该选项可以打开和关闭堆栈保护功能。
-fno-stack-protector:关闭堆栈保护。
-fstack-protector:启用堆栈保护,若C或C++函数的局部变量中含有字符(char)数组(数组大小大于等于4个字节的字符数组),并且不会被优化掉,编译器会为函数插入保护代码。
-fstack-protector-strong:启用堆栈保护,若C或C++函数中有数组定义并且不会被优化掉,或者存在局部变量的地址引用时,编译器会为函数插入保护代码(默认选项)。
-fstack-protector-all:为所有C或C++函数插入保护代码,相比上一个,可能会大幅增加性能开销。
推荐使用“-fstack-protector”选项,在兼顾性能的同时,增强了安全性。
ArchStackGuardInit:liteos值提供伪随机数金丝雀值,需要厂商适配真随机,以保证安全性。

须知: LiteOS提供弱函数ArchStackGuardInit,采用伪随机数方式初始化“__stack_chk_guard”,如果用户需要达到真随机数,需重定义ArchStackGuardInit。另外部分架构没有随机种子,无法达成伪随机数,因此,用户必须自行验证“__stack_chk_guard”的随机性。
NX配置¶
“Enable Data Sec NX Feature”选项可以打开和关闭数据段不可执行功能。
选择该选项建议配合使用系统提供的链接脚本,不使用系统链接脚本时需要参考系统链接脚本编写私有链接脚本。
该选项只在支持MMU的架构上起效,不支持MMU的架构请在初始化过程中自行实现NX。

可维可测¶
Shell¶
概述¶
基本概念
LiteOS提供Shell命令行,它能够以命令行交互的方式访问操作系统的功能或服务:它接收并解析用户输入的命令,并处理操作系统的输出结果。
LiteOS Shell
LiteOS提供的Shell作为在线调试工具,支持常用的基本调试功能,包含系统、文件、网络、动态加载和Trace等相关命令。同时LiteOS的Shell可以根据需求新增定制命令。
系统相关命令:可以查询系统任务、内核信号量、系统软件定时器、CPU占用率、当前中断等相关信息,详见“系统命令参考”。
文件相关命令:除了支持基本的ls、cd等功能外,还支持sync。sync用来同步缓存数据(文件系统的数据)到SD卡或Flash中,详见“文件命令参考”。
动态加载相关命令:加载obj文件或so文件,并通过查找已加载obj或so的函数地址,直接调用相关函数,同时用户也可以卸载obj文件或so文件。
Trace相关命令:可以开启/停止Trace、设置Trace事件、输出Trace缓冲区数据等,详见“Trace命令参考”。
开发指导¶
使用场景
Shell命令可以通过串口,Telnet或者自定义工具输入。新增定制的命令,需重新编译链接后才能执行。
功能
LiteOS提供的Shell命令参见后面“系统命令参考”章节。
LiteOS的Shell模块为用户提供下面几个接口,接口详细信息可以查看API参考。
功能分类
接口名
描述
注册命令
SHELLCMD_ENTRY
静态注册命令
osCmdReg
动态注册命令
说明:
静态注册命令方式一般用于注册系统常用命令,动态注册命令方式一般用于注册用户命令。
静态注册命令有5个入参,动态注册命令有4个入参。下面除去第一个入参是静态注册独有的,剩余的四个入参两个注册命令是一致的。第一个入参:命令变量名,用于设置链接选项(“build/make/liteos_tables_ldflags.mk”的LITEOS_TABLES_LDFLAGS变量)。例如变量名为ls_shellcmd,链接选项就应该设置为:LITEOS_TABLES_LDFLAGS += -uls_shellcmd。这个入参是静态注册独有的,动态注册中没有这个入参。
第二个入参:命令类型,目前支持两种命令类型。
CMD_TYPE_EX:不支持标准命令参数输入,会把用户填写的命令关键字屏蔽掉。例如:输入“ls /ramfs”,传入给命令处理函数的参数只有“/ramfs”,对应于命令处理函数中的argv[0],而ls命令关键字并不会被传入。
CMD_TYPE_STD:支持的标准命令参数输入,所有输入的字符都会通过命令解析后被传入。例如:输入“ls /ramfs”,“ls”和“/ramfs”都会被传入命令处理函数,分别对应于命令处理函数中的argv[0]和argv[1]。
第三个入参:命令关键字,是命令处理函数在Shell中对应的名称。命令关键字必须唯一,即两个不同的命令项不能有相同的命令关键字,否则只会执行其中一个。Shell在执行用户命令时,如果存在多个命令关键字相同的命令,只会执行在“help”命令中排在最前面的那个。
第四个入参:命令处理函数的入参最大个数。
静态注册命令暂不支持设置。
动态注册命令支持设置不超过32的入参最大个数,或者设置为XARGS(其在代码中被定义为0xFFFFFFFF)表示不限制参数个数。
第五个入参:命令处理函数名,即在Shell中执行命令时被调用的函数。
配置项
打开menuconfig,进入Debug ---> Enable a Debug Version ---> Enable Shell菜单,完成Shell的配置。
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_SHELL |
Shell功能的裁剪开关 |
YES/NO |
YES |
LOSCFG_DEBUG_VERSION(=y) && LOSCFG_DRIVERS_UART(=y) |
LOSCFG_SHELL_CONSOLE(开源版本无该配置项) |
设置Shell直接与Console交互 |
YES/NO |
YES |
LOSCFG_SHELL(=y) |
LOSCFG_SHELL_UART |
设置Shell直接与uart驱动交互 |
YES/NO |
NO |
LOSCFG_DRIVERS_UART(=y) |
LOSCFG_SHELL_VENDOR |
设置Shell使用自定义方式交互 |
YES/NO |
NO |
LOSCFG_SHELL(=y) |
新增命令开发流程
下面以注册系统命令ls为例,介绍新增Shell命令的典型开发流程。
定义Shell命令处理函数。
Shell命令处理函数用于处理注册的命令。例如定义一个命令处理函数osShellCmdLs,处理ls命令,并在头文件中声明命令处理函数原型。
int osShellCmdLs(int argc, const char **argv);
须知:
命令处理函数的参数与C语言中main函数的参数类似,包括两个入参:argc:Shell命令的参数个数。个数中是否包括命令关键字,和注册命令时的命令类型有关。
argv:为指针数组,每个元素指向一个字符串,该字符串就是执行shell命令时传入命令处理函数的参数。参数中是否包括命令关键字,和注册命令时的命令类型有关。
注册命令。
有静态注册命令和系统运行时动态注册命令两种注册方式。
静态注册ls命令:
#include "shcmd.h" SHELLCMD_ENTRY(ls_shellcmd, CMD_TYPE_EX, "ls", XARGS, (CMD_CBK_FUNC)osShellCmdLs);
动态注册ls命令:
#include "shell.h" osCmdReg(CMD_TYPE_EX, "ls", XARGS, (CMD_CBK_FUNC)osShellCmdLs);
对于静态注册命令方式,在“build/make/liteos_tables_ldflags.mk”中设置链接选项(LITEOS_TABLES_LDFLAGS变量)。
打开菜单使能Shell,详见配置项。
编译烧录系统后,可以执行新增的Shell命令。
注意事项¶
默认模式下支持英文输入。如果在UTF8格式下输入了中文字符,只能通过回退三次来删除。
支持Shell命令、文件名及目录名的Tab键联想补全,若有多个匹配项则补全共同字符,并打印多个匹配项。对于过多的匹配项(打印多于24行),将会进行打印询问(Display all num possibilities?(y/n)),可输入y选择全部打印,或输入n退出打印,选择全部打印并打印超过24行后,会进行“--More--”提示,此时回车键继续打印,q键退出(支持Ctrl+c退出)。
不建议使用Shell命令操作“/dev”目录下的设备文件,可能会引起不可预知的结果。
Shell属于调测功能,默认配置为关闭,正式商用产品中禁止使用该功能。
免责声明:华为不承担在正式商用产品中使用该功能的任何风险。
静态注册编程实例¶
实例描述
本实例演示如何使用静态注册命令方式新增一个名为test的Shell命令。编译工具以Make为例介绍 。
定义一个新增命令所要调用的命令处理函数cmd_test。
使用SHELLCMD_ENTRY函数添加新增命令项。
在liteos_tables_ldflags.mk中添加链接该新增命令项参数。
打开菜单使能Shell。
重新编译代码后运行。
编程示例
定义命令所要调用的命令处理函数cmd_test:
#include "shell.h" #include "shcmd.h" int cmd_test(void) { printf("hello everybody!\n"); return 0; }
添加新增命令项:
SHELLCMD_ENTRY(test_shellcmd, CMD_TYPE_EX, "test", 0, (CMD_CBK_FUNC)cmd_test);在链接选项中添加链接该新增命令项参数:
在“build/make/liteos_tables_ldflags.mk”中LITEOS_TABLES_LDFLAGS项下添加“-utest_shellcmd”。
打开菜单使能Shell,即设置LOSCFG_SHELL=y。
重新编译代码:
make clean;make
结果验证
烧录新系统镜像后,重启系统。使用help命令查看当前系统中所有注册的命令,可以看到test命令已经注册。
动态注册编程实例¶
实例描述
本实例演示如何使用动态注册命令方式新增一个名为test的Shell命令。编译工具以Make为例介绍 。
定义一个新增命令所要调用的命令处理函数cmd_test。
使用osCmdReg函数添加新增命令项。
打开菜单使能Shell。
重新编译代码后运行。
编程示例
定义命令所要调用的命令处理函数cmd_test:
#include "shell.h" #include "shcmd.h" int cmd_test(void) { printf("hello everybody!\n"); return 0; }
在app_init函数中调用osCmdReg函数动态注册命令:
void app_init(void) { .... .... osCmdReg(CMD_TYPE_EX, "test", 0,(CMD_CBK_FUNC)cmd_test); .... }
打开菜单使能Shell,即设置LOSCFG_SHELL=y。
重新编译代码:
make clean;make
结果验证
烧录新系统镜像后,重启系统。使用help命令查看当前系统中所有注册的命令,可以看到test命令已经注册。
系统命令参考¶
使能系统命令¶
使用Shell中的系统命令前,需要打开menuconfig使能Shell,详见“配置项”。
help¶
命令功能
help命令用于显示当前操作系统内所有的Shell命令。
命令格式
help
使用实例
举例:输入“help”。
输出说明
执行help,输出当前系统内的所有Shell命令:
LiteOS # help
*******************shell commands:*************************
cpup date dlock dmesg free help hwi
log memcheck mutex queue sem stack swtmr
systeminfo task uname watch
date¶
命令功能
date命令用于查询及设置系统时间。
命令格式
date
date --help
date +Format
date -s YY/MM/DD
date -s hh:mm:ss
date -r Filename(开源版本暂不支持该命令)
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
--help |
使用帮助 |
N/A |
+Format |
根据Format格式打印时间 |
--help中列出的占位符 |
-s YY/MM/DD |
设置系统时间,用“/”分割的年月日 |
>= 1970/01/01 |
-s hh:mm:ss |
设置系统时间,用“:”分割的时分秒 |
N/A |
-r Filename |
查询指定文件的修改时间,需要使能LOSCFG_FS_VFS |
N/A |
使用指南
该命令依赖于LOSCFG_SHELL_EXTENDED_CMDS,该宏开关可在menuconfig菜单项中开启“Enable Shell Ext CMDs”使能。
Debug ---> Enable a Debug Version ---> Enable Shell ---> Enable Shell Ext CMDsdate参数缺省时,默认显示当前系统时间。
--help、+Format、-s、-r不能混合使用。
使用实例
举例:
输入“date +%Y--%m--%d”。
输出说明
执行“date +%Y--%m--%d”,按其指定格式打印当前系统时间:
LiteOS # date +%Y--%m--%d
2021--01--20
uname¶
命令功能
uname命令用于显示操作系统的名称、系统编译时间、版本信息等。
命令格式
uname [-a | -s | -t | -v | --help]
参数说明
参数 |
参数说明 |
|---|---|
-a |
显示全部信息。 |
-s |
显示操作系统名称。 |
-t |
显示系统的编译时间。 |
-v |
显示版本信息。 |
--help |
显示uname命令的帮助信息。 |
使用指南
参数缺省时,默认显示操作系统名称。
uname的参数不能混合使用。
使用实例
举例:
输入“uname -a”。
输出说明
执行“uname -a”,获取系统信息:
LiteOS # uname -a
LiteOS V200R002C00SPC050B011 LiteOS 3.2.2 Mar 30 2019 16:09:28
task¶
命令功能
task命令用于查询系统任务信息。
命令格式
task_ _[ID]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
ID |
任务ID号,输入形式以十进制表示或十六进制表示皆可。 |
[0, 0xFFFFFFFF] |
使用指南
参数缺省时,默认打印全部运行任务信息。
task后加ID,当ID参数在[0, 64]范围内时,返回指定ID号任务的任务名、任务ID、任务的调用栈信息(最大支持15层调用栈),其他取值时返回参数错误的提示。如果指定ID号对应的任务未创建,则提示。
如果在task命令中,发现任务是Invalid状态,请确保pthread_create创建函数时有进行如下操作之一,否则资源无法正常回收。
选择的是阻塞模式应该调用pthread_join()函数。
选择的是非阻塞模式应该调用pthread_detach()函数。
如果不想调用前面两个接口,就需要设置pthread_attr_t状态为PTHREAD_STATE_DETACHED,将attr参数传入pthread_create,此设置和调用pthread_detach函数一样,都是非阻塞模式。
使用实例
举例1:输入“task 6”。
举例2:输入“task”。
输出说明
执行“task 0xb”,查询ID号为b的任务信息:
LiteOS # task 0xb
TaskName = SerialEntryTask
TaskId = 0xb
*******backtrace begin*******
traceback 0 -- lr = 0x1d804 fp = 0xa86bc
traceback 1 -- lr = 0x1da40 fp = 0xa86e4
traceback 2 -- lr = 0x20154 fp = 0xa86fc
traceback 3 -- lr = 0x258e4 fp = 0xa8714
traceback 4 -- lr = 0x242f4 fp = 0xa872c
traceback 5 -- lr = 0x123e4 fp = 0xa8754
traceback 6 -- lr = 0x2a9d8 fp = 0xb0b0b0b
执行“task”,查询所有任务信息:
LiteOS # task
Name TaskEntryAddr TID Priority Status StackSize WaterLine StackPoint TopOfStack EventMask SemID CPUUSE CPUUSE10s CPUUSE1s MEMUSE
---- ------------ --- ------- -------- --------- -------- ---------- ---------- ------- ----- ------- --------- ------ -------
Swt_Task 0x40002770 0x0 0 QueuePend 0x6000 0x2cc 0x4015a318 0x401544e8 0x0 0xffff 0.0 0.0 0.0 0
IdleCore000 0x40002dc8 0x1 31 Ready 0x400 0x15c 0x4015a7f4 0x4015a550 0x0 0xffff 98.6 98.2 99.9 0
system_wq 0x400b80fc 0x3 1 Pend 0x6000 0x244 0x40166928 0x40160ab8 0x1 0xffff 0.0 0.0 0.0 0
SerialShellTask 0x40090158 0x5 9 Running 0x3000 0x55c 0x40174918 0x40171e70 0xfff 0xffff 1.2 1.7 0.0 48
SerialEntryTask 0x4008fe30 0x6 9 Pend 0x1000 0x2c4 0x40175c78 0x40174e88 0x1 0xffff 0.0 0.0 0.0 72
说明:
输出项说明:
Name:任务名。
TID:任务ID。
Priority:任务的优先级。
Status:任务当前的状态。
StackSize:任务栈大小。
WaterLine:该任务栈已经被使用的内存大小。
StackPoint:任务栈指针,表示栈的起始地址。
TopOfStack:栈顶的地址。
EventMask:当前任务的事件掩码,没有使用事件,则默认任务事件掩码为0(如果任务中使用多个事件,则显示的是最近使用的事件掩码)。
SemID:当前任务拥有的信号量ID,没有使用信号量,则默认0xFFFF(如果任务中使用了多个信号量,则显示的是最近使用的信号量ID)。
CPUUSE:系统启动以来的CPU占用率。
CPUUSE10s:系统最近10秒的CPU占用率。
CPUUSE1s:系统最近1秒的CPU占用率。
MEMUSE:截止到当前时间,任务所申请的内存大小,以字节为单位显示。MEMUSE仅针对系统内存池进行统计,不包括中断中处理的内存和任务启动之前的内存。 任务申请内存,MEMUSE会增加,任务释放内存,MEMUSE会减小,所以MEMUSE会有正值和负值的情况。
MEMUSE为0:说明该任务没有申请内存,或者申请的内存和释放的内存相同。
MEMUSE为正值:说明该任务中有内存未释放。
MEMUSE为负值:说明该任务释放的内存大于申请的内存。
系统任务说明(LiteOS系统初始任务有以下几种):
Swt_Task:软件定时器任务,用于处理软件定时器超时回调函数。
IdleCore000:系统空闲时执行的任务。
system_wq:系统默认工作队列处理任务。
SerialEntryTask:从底层buf读取用户的输入,初步解析命令,例如tab补全,方向键等。
SerialShellTask:接收命令后进一步解析,并查找匹配的命令处理函数,进行调用。
任务状态说明:
Ready:任务处于就绪状态。
Pend:任务处于阻塞状态。
PendTime:阻塞的任务处于等待超时状态。
Suspend:任务处于挂起状态。
Running:该任务正在运行。
Delay:任务处于延时等待状态。
SuspendTime:挂起的任务处于等待超时状态。
Invalid:非上述任务状态。
free¶
命令功能
free命令可显示系统内存的使用情况,同时显示系统的text段、data段、rodata段、bss段大小。
命令格式
free [_-k _| -m]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
无参数 |
以Byte为单位显示。 |
N/A |
-k |
以KByte为单位显示。 |
N/A |
-m |
以MByte为单位显示。 |
N/A |
使用指南
输入free显示内存使用情况,total表示系统动态内存池的总大小,used表示已使用的内存大小,free表示空闲的内存大小。text表示代码段大小,data表示数据段大小,rodata表示只读数据段大小,bss表示未初始化全局变量占用的内存大小。
free命令能够以三种单位来显示内存使用情况,包括Byte、KByte和MByte。
使用实例
举例:
分别输入“free”、“free -k”、“free -m”。
输出说明
以三种单位显示内存使用情况的输出:
LiteOS # free
total used free
Mem: 117631744 31826864 85804880
text data rodata bss
Mem: 4116480 423656 1204224 6659316
LiteOS # free -k
total used free
Mem: 114874 31080 83793
text data rodata bss
Mem: 4020 423 1176 6503
LiteOS # free -m
total used free
Mem: 112 30 81
text data rodata bss
Mem: 3 0 1 6
memcheck¶
命令功能
检查动态申请的内存块是否完整,是否存在内存越界造成的节点损坏。
命令格式
memcheck
使用指南
打开内存完整性检查开关。
目前只有bestfit内存管理算法支持该命令,需要使能LOSCFG_KERNEL_MEM_BESTFIT。
Kernel ---> Memory Management ---> Dynamic Memory Management Algorithm ---> bestfit该命令依赖于LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK,使用时需要在菜单项中开启“Enable integrity check or not”。
Debug ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable integrity check or not
当内存池所有节点完整时,输出“memcheck over, all passed!”。
当内存池存在节点不完整时,输出被损坏节点的内存块信息。
使用实例
举例:
输入“memcheck”。
输出说明
没有内存越界时,执行“memcheck”输出内容如下:
LiteOS # memcheck system memcheck over, all passed!
发生内存越界时,执行“memcheck”输出内容如下:
LiteOS # memcheck [ERR][OsMemIntegrityCheck], 1145, memory check error! freeNodeInfo.pstPrev:(nil) is out of legal mem range[0x80353540, 0x84000000] broken node head: (nil) (nil) (nil) 0x0, pre node head: 0x7fc6a31b 0x8 0x80395ccc 0x80000110 --------------------------------------------- dump mem tmpNode:0x80395df4 ~ 0x80395e34 0x80395df4 :00000000 00000000 00000000 00000000 0x80395e04 :cacacaca cacacaca cacacaca cacacaca 0x80395e14 :cacacaca cacacaca cacacaca cacacaca 0x80395e24 :cacacaca cacacaca cacacaca cacacaca --------------------------------------------- dump mem :0x80395db4 ~ tmpNode:0x80395df4 0x80395db4 :00000000 00000000 00000000 00000000 0x80395dc4 :00000000 00000000 00000000 00000000 0x80395dd4 :00000000 00000000 00000000 00000000 0x80395de4 :00000000 00000000 00000000 00000000 --------------------------------------------- cur node: 0x80395df4 pre node: 0x80395ce4 pre node was allocated by task:SerialShellTask cpu0 is in exc. cpu1 is running. excType:software interrupt taskName = SerialShellTask taskId = 8 task stackSize = 12288 system mem addr = 0x80353540 excBuffAddr pc = 0x80210b78 excBuffAddr lr = 0x80210b7c excBuffAddr sp = 0x803b2d50 excBuffAddr fp = 0x80280368 R0 = 0x59 R1 = 0x600101d3 R2 = 0x0 R3 = 0x8027a300 R4 = 0x1 R5 = 0xa0010113 R6 = 0x80395e04 R7 = 0x80317254 R8 = 0x803b2de4 R9 = 0x4 R10 = 0x803afca4 R11 = 0x80280368 R12 = 0x1 CPSR = 0x600101d3
以上各关键输出项含义如下:
“mem check error”,表示检测到了内存节点被破坏。
“cur node:”,表示该节点内存被踩,并打印内存地址。
“pre node:”,表示被踩节点前面的节点,并打印节点地址。
“pre node was allocated by task:SerialShellTask”,表示在SerialShellTask任务中发生了踩内存。
“taskName”和“taskId”,分别表示发生异常的任务名和任务ID。
memused¶
命令功能
memused命令用于查看当前系统used节点中保存的函数调用栈LR信息。通过分析数据可检测内存泄露问题。
命令格式
memused
使用指南
打开menuconfig菜单开启内存泄漏检测。
目前只有bestfit内存管理算法支持该功能,需要使能LOSCFG_KERNEL_MEM_BESTFIT。
Kernel ---> Memory Management ---> Dynamic Memory Management Algorithm ---> bestfit该命令依赖于LOSCFG_MEM_LEAKCHECK,可以在菜单项中开启“Enable Function call stack of Mem operation recorded”:
Debug ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable Function call stack of Mem operation recorded开启该菜单项后,会在内存操作时,将函数调用关系LR记录到内存节点中,若相同调用栈的内存节点随时间增长而不断增多,则可能存在内存泄露,通过LR可以追溯内存申请的位置。重点关注LR重复的节点。
须知:
此配置打开时,会影响内存占用和内存操作性能,因此仅在检测内存泄露问题时打开。
使用实例
举例:
输入“memused”。
输出说明
LiteOS # memused
node LR[0] LR[1] LR[2]
0x802d79e4: 0x8011d740 0x8011a990 0x00000000
0x802daa0c: 0x8011d5ec 0x8011d740 0x8011a990
0x802dca28: 0x8006e6f4 0x8006e824 0x8011d5ec
0x802e2a48: 0x8014daac 0x8014db4c 0x800f6da0
0x802e8a68: 0x8011d274 0x8011d654 0x8011d740
0x802e8a94: 0x8014daac 0x8014db4c 0x8011d494
0x802eeab4: 0x8011d4e0 0x8011d658 0x8011d740
0x802f4ad4: 0x8015326c 0x80152a20 0x800702bc
0x802f4b48: 0x8015326c 0x80152a20 0x800702bc
0x802f4bac: 0x801504d8 0x801505d8 0x80150834
0x802f4c08: 0x801504d8 0x801505d8 0x80150834
0x802f4c74: 0x801504d8 0x801505d8 0x80150834
0x802f4e08: 0x801504d8 0x801505d8 0x80150834
0x802f4e60: 0x801030e8 0x801504d8 0x801505d8
0x802f4e88: 0x80103114 0x801504d8 0x801505d8
0x802f4eb4: 0x801504d8 0x801505d8 0x80150834
0x802f4f7c: 0x801504d8 0x801505d8 0x80150834
0x802f5044: 0x801504d8 0x801505d8 0x80150834
0x802f510c: 0x800702bc 0x80118f24 0x00000000
hwi¶
命令功能
hwi命令用于查询当前中断信息。
命令格式
hwi
hwi status all
hwi status hwiNum [devId]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
status all |
打印所有已创建的中断状态。 |
无 |
hwiNum |
中断号,十进制。 |
[0,0xFFFFFFFF] |
devId |
共享中断设备号,十六进制,缺省时表示查询的中断为非共享中断。 |
[0,0xFFFFFFFF] |
使用指南
使能hwi命令,在菜单项打开宏开关LOSCFG_DEBUG_HWI:
Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Hwi Debugging输入“hwi”即显示当前中断号、中断次数及注册中断名称。
输入“hwi status all”显示当前所有已创建中断的状态。
输入“hwi status hwiNum [devId]”查询特定的中断状态;共享中断需要devId参数,非共享中断不需要参数devId。
若打开宏开关LOSCFG_CPUP_INCLUDE_IRQ,hwi命令还会显示各个中断的处理时间(cycles)、CPU占用率以及中断类型。该宏开关可在菜单项中开启“Enable Cpup include irq”使能:
Kernel ---> Enable Extend Kernel ---> Enable Cpup ---> Enable Cpup include irq关于该宏开关更详细的介绍,参见CPU占用率“开发流程”。
使用实例
举例:
输入“hwi”。
输入“hwi status hwiNum devId”。
输入“hwi status hwiNum”。
输入“hwi status all”。
输出说明
输入“hwi”,显示中断信息(LOSCFG_CPUP_INCLUDE_IRQ关闭)。
LiteOS # hwi InterruptNo Count Name 35: 1364: 36: 0: 40: 79: uart_pl011
输入“hwi”,显示中断信息(LOSCFG_CPUP_INCLUDE_IRQ打开)。
LiteOS # hwi InterruptNo Count Name CYCLECOST CPUUSE CPUUSE10s CPUUSE1s mode 3: 1333 122 0.0 0.0 0.0 normal 4: 0 0 0.0 0.0 0.0 normal 5: 59 uart_pl011 305 0.0 0.0 0.0 normal 12: 96 ETH 131 0.0 0.0 0.0 normal
输入“hwi status 3 0x1”,显示中断号3,设备号0x1共享中断的状态。
LiteOS # hwi status 3 0x1 InterruptNo DevId Priority Affinity Created Enable Pending ----------- ----- -------- -------- ------- ------ ------- 3 0x1 20 0x0 0 1 0
输入“hwi status 3”,显示中断号3的非共享中断的状态。
LiteOS # hwi status 3 InterruptNo DevId Priority Affinity Created Enable Pending ----------- ----- -------- -------- ------- ------ ------- 3 0x0 20 0x0 0 1 0
输入“hwi status all”,显示所有已创建中断的状态。
LiteOS # hwi status all InterruptNo DevId Priority Affinity Created Enable Pending ----------- ----- -------- -------- ------- ------ ------- 0 0x0 20 0x0 1 1 0 1 0x0 20 0x0 1 1 0 2 0x0 20 0x0 1 1 0 30 0x0 20 0x0 1 1 0 33 0x0 0 0x1 1 1 0
queue¶
命令功能
queue命令用于查看队列的使用情况。
命令格式
queue
使用指南
该命令依赖于LOSCFG_DEBUG_QUEUE,该宏开关可在菜单项中开启“Enable Queue Debugging”使能。
Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Queue Debugging
使用实例
举例:
输入“queue”。
输出说明
执行“queue”后得到队列的使用情况:
LiteOS # queue
used queues information:
Queue ID <0x0> may leak, queue len is 0x10, readable cnt:0x0, writeable cnt:0x10, TaskEntry of creator:0x0x80007d5, Latest operation time: 0x614271
以上各输出项含义如下:
输出项 |
说明 |
|---|---|
Queue ID |
队列编号。 |
queue len |
队列消息节点个数。 |
readable cnt |
队列中可读的消息个数。 |
writeable cnt |
队列中可写的消息个数。 |
TaskEntry of creator |
创建队列的接口地址。 |
Latest operation time |
队列最后操作时间。 |
sem¶
命令功能
sem命令用于查询系统内核信号量的相关信息。
命令格式
sem [ID]
sem fulldata
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
ID |
信号量ID号,输入形式以十进制表示或十六进制表示皆可。 |
[0, 0xFFFFFFFF] |
fulldata |
查询所有在用的信号量信息,打印信息包括如下:SemID、Count、Original Count、Creater (TaskEntry)、Last Access Time |
N/A |
使用指南
参数缺省时,显示所有信号量的使用数及信号量总数。
sem后加ID,当ID参数在[0, 1023]范围内时,返回指定ID号的信号量使用数(如果指定ID号的信号量未被使用则提示),其他取值时返回参数错误的提示。
sem的参数ID和fulldata不可以混用。
参数fulldata依赖LOSCFG_DEBUG_SEMAPHORE,该宏开关可在菜单项中开启“Enable Semaphore Debugging”使能。
Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Semaphore Debugging
使用实例
举例1:输入“sem 1”。
举例2:输入“sem”和“sem fulldata”。
输出说明
执行“sem 1”,查询指令结果:
LiteOS # sem 1
SemID Count
---------- -----
0x00000001 0x1
No task is pended on this semphore!
执行“sem”和“sem fulldata”,查询所有在用的信号量信息:
LiteOS # sem
SemID Count
---------- -----
0x00000000 1
SemID Count
---------- -----
0x00000001 1
SemID Count
---------- -----
0x00000002 1
SemUsingNum : 3
LiteOS # sem fulldata
Used Semaphore List:
SemID Count OriginalCout Creater(TaskEntry) LastAccessTime
------ ----- ------------ ----------------- --------------
0x2 0x1 0x1 0x80164d70 0x3
0x0 0x1 0x1 0x0 0x3
0x1 0x1 0x1 0x0 0x3
mutex¶
命令功能
mutex命令用于查看互斥锁的使用情况。
命令格式
mutex
使用指南
该命令依赖于LOSCFG_DEBUG_MUTEX,该宏开关可在菜单项中开启“Enable Mutex Debugging”使能。
Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Mutex Debugging
使用实例
举例:
输入“mutex”。
输出说明
执行“mutex”输出互斥锁使用情况:
LiteOS # mutex
used mutexs information:
Mutex ID <0x0> may leak, Owner is null, TaskEntry of creator: 0x8000711,Latest operation time: 0x0
以上各主要输出项含义如下:
输出项 |
说明 |
|---|---|
Mutex ID |
锁序号。 |
TaskEntry of creator |
创建锁的接口地址。 |
Latest operation time |
任务最后调度时间。 |
swtmr¶
命令功能
swtmr命令用于查询系统软件定时器的相关信息。
命令格式
swtmr [ID]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
ID |
软件定时器ID号,输入形式以十进制表示或十六进制表示皆可。 |
[0,0xFFFFFFFF] |
使用指南
使能swtmr命令,在菜单项打开宏开关LOSCFG_DEBUG_SWTMR:
Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Swtmr Debugging参数缺省时,默认显示所有软件定时器的相关信息。
swtmr后加ID号时,当ID在[0, 当前软件定时器个数-1]范围内时,返回对应ID的软件定时器的相关信息,其他取值时返回错误提示。
使用实例
举例:
输入“swtmr”和“swtmr 0”。
输出说明
执行“swtmr”查询软件定时器相关信息,输出如下:
LiteOS # swtmr
SwTmrID State Mode Interval Arg handlerAddr
---------- ------- ------- --------- ---------- --------
0x00000000 Ticking Period 1000 0x00000000 0x800442d
执行“swtmr 0”查询ID为0的软件定时器信息,输出如下:
LiteOS # swtmr 0
SwTmrID State Mode Interval Arg handlerAddr
---------- ------- ------- --------- ---------- --------
0x00000000 Ticking Period 1000 0x00000000 0x800442d
输出项 |
说明 |
|---|---|
SwTmrID |
软件定时器ID。 |
State |
软件定时器状态。 |
Mode |
软件定时器模式。 |
Interval |
软件定时器使用的Tick数。 |
Arg |
传入的参数。 |
handlerAddr |
回调函数的地址。 |
systeminfo¶
命令功能
systeminfo命令用于显示当前操作系统的资源使用情况,包括任务、信号量、互斥锁、队列、软件定时器等。
对于信号量、互斥锁、队列和软件定时器,如果在系统镜像中已经裁剪了这些模块,那么说明系统没有使用这些资源,该命令也就不会显示这些资源的情况。
命令格式
systeminfo
使用实例
举例:
输入“systeminfo”。
输出说明
执行“systeminfo”查看系统资源使用情况,输出如下:
LiteOS # systeminfo
Module Used Total
--------------------------------
Task 5 16
Sem 0 20
Mutex 1 20
Queue 1 10
SwTmr 1 16
以上各输出项含义如下:
输出项 |
说明 |
|---|---|
Module |
模块名称。 |
Used |
当前使用量。 |
Total |
最大可用量。 |
log¶
命令功能
log命令用于修改&查询系统的日志打印等级。
命令格式
log level [levelNum]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
levelNum |
设置日志打印等级。 |
[0x0,0x5] |
须知: log命令的帮助信息中还显示了另外两个参数module和path,但当前暂不支持这两个参数。
使用指南
该命令依赖于LOSCFG_SHELL_LK,可在菜单项中开启“Enable Shell lk”使能。
Debug ---> Enable a Debug Version ---> Enable Shell ---> Enable Shell lKlog level命令用于设置系统的日志打印等级,包括6个等级。
TRACE_EMG = 0
TRACE_COMMOM = 1
TRACE_ERROR = 2
TRACE_WARN = 3
TRACE_INFO = 4
TRACE_DEBUG = 5
若log level命令不加levelNum参数,则提示命令的使用方法。
使用实例
举例:
输入“log level 4”。
输出说明
执行“log level 4”,设置系统的日志打印等级为INFO级别:
LiteOS # log level 4
Set current log level INFO
dmesg¶
命令功能
dmesg命令用于控制内核dmesg缓存区。
命令格式
dmesg
dmesg [-c | -C | -D | -E | -L | -U]
dmesg -s <size>
dmesg -l <level>
dmesg > file(开源版本暂不支持该命令)
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
-c |
打印缓存区内容并清空缓存区,此打印不受-D、-L影响。 |
N/A |
-C |
清空缓存区。 |
N/A |
-D | -E |
关闭/开启控制台打印,开源版本暂不支持该参数。 |
N/A |
-L | -U |
关闭/开启串口打印,开源版本暂不支持该参数。 |
N/A |
-s <size> |
设置缓存区大小。 |
0-4294967279(0 - 0xffffffef) |
-l <level> |
设置缓存区的日志打印等级。 |
0 -- 5 |
> file |
将缓存区内容写入文件,开源版本暂不支持该参数。 |
N/A |
须知:
写入文件需确保已挂载文件系统。
关闭串口打印会影响Shell使用,建议先连接Telnet再尝试关闭串口。
使用指南
该命令依赖于LOSCFG_SHELL_DMESG,使用时可在菜单项中开启“Enable Shell dmesg”。
Debug ---> Enable a Debug Version---> Enable Shell---> Enable Shell dmesg参数缺省时,默认打印缓存区内容。
各参数均不能混合使用。
使用实例
举例:
输入“dmesg”后,接着输入“dmesg -C”和“dmesg”。
输出说明
第一次执行“dmesg”后,可以看到输出了缓存区内容。接着执行“dmesg -C”清空缓存区内容,紧接着再次执行“dmesg”可以看到之前缓存区中的内容已经被清空:
LiteOS # dmesg
LiteOS # log level 4
Set current log level INFO
LiteOS # dmesg
LiteOS # dmesg -C
LiteOS # dmesg
LiteOS # dmesg
须知: “dmesg -C”是日志维测命令,只支持串口输出,不支持通过console输出到其他输出端口(Telnet、USB等)。
stack¶
命令功能
stack命令用于显示当前操作系统内所有栈的信息。
命令格式
stack
使用指南
该功能默认开启。
使用实例
举例:
输入“stack”。
输出说明
执行“stack”命令查看系统内所有栈的信息:
LiteOS # stack
stack name cpu id stack addr total size used size
---------- ------ --------- -------- --------
udf_stack 3 0x3c800 0x28 0x0
udf_stack 2 0x3c828 0x28 0x0
udf_stack 1 0x3c850 0x28 0x0
udf_stack 0 0x3c878 0x28 0x0
abt_stack 3 0x3c8a0 0x28 0x0
abt_stack 2 0x3c8c8 0x28 0x0
abt_stack 1 0x3c8f0 0x28 0x0
abt_stack 0 0x3c918 0x28 0x0
fiq_stack 3 0x3ca40 0x40 0x0
fiq_stack 2 0x3ca80 0x40 0x0
fiq_stack 1 0x3cac0 0x40 0x0
fiq_stack 0 0x3cb00 0x40 0x0
svc_stack 3 0x3cb40 0x2000 0x524
svc_stack 2 0x3eb40 0x2000 0x524
svc_stack 1 0x40b40 0x2000 0x524
svc_stack 0 0x42b40 0x2000 0x528
irq_stack 3 0x3c940 0x40 0x0
irq_stack 2 0x3c980 0x40 0x0
irq_stack 1 0x3c9c0 0x40 0x0
irq_stack 0 0x3ca00 0x40 0x0
exc_stack 3 0x44b40 0x1000 0x0
exc_stack 2 0x45b40 0x1000 0x0
exc_stack 1 0x46b40 0x1000 0x0
exc_stack 0 0x47b40 0x1000 0x0
须知: stack是异常维测命令,只支持串口输出,不支持通过console输出到其他输出端口(USB等)。
cpup¶
命令功能
cpup命令用于查询系统CPU的占用率,并以百分比显示占用率。
命令格式
cpup [mode] [taskID]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
mode |
|
[0,0xFFFF] 或0xFFFFFFFF |
taskID |
任务ID号 |
[0,0xFFFF] 或0xFFFFFFFF |
使用指南
参数缺省时,显示系统最近10s的CPU占用率。
只输入一个参数时,该参数为mode,显示系统相应时间的CPU占用率。
输入两个参数时,第一个参数为mode,第二个参数为taskID,显示指定任务ID的任务在相应时间的CPU占用率。
该功能需要使能CPU占用率模块,菜单路径为:
Kernel ---> Enable Extend Kernel ---> Enable Cpup想更多的了解CPU占用率模块,参见“CPU占用率”。
使用实例
举例:
输入“cpup 1 1”。
输出说明
执行“cpup 1 1”,显示ID为1的任务最近1s的CPU占用率:
LiteOS # cpup 1 1
TaskId 1 CpuUsage in 1s: 78.7
watch¶
命令功能
watch命令用于周期性监测一个命令的运行结果。
命令格式
watch [-c | -n | -t | --count | --interval | -no-title] <command>
watch --over
参数说明
参数 |
参数说明 |
缺省值 |
取值范围 |
|---|---|---|---|
-c | --count |
命令执行的总次数 |
0xFFFFFF |
(0, 0xFFFFFF] |
-n | --interval |
命令周期性执行的时间间隔(s) |
1s |
(0, 0xFFFFFF] |
-t | -no-title |
关闭顶端的时间显示 |
N/A |
N/A |
command |
需要监测的Shell命令 |
N/A |
N/A |
--over |
关闭当前的监测 |
N/A |
N/A |
使用指南
该命令依赖于LOSCFG_SHELL_EXTENDED_CMDS,该宏开关可在菜单项中开启“Enable Shell Ext CMDs”使能。
Debug ---> Enable a Debug Version ---> Enable Shell ---> Enable Shell Ext CMDscommand参数必须是Shell命令,对于非Shell命令,会有错误提示“command is not fount”。
如果要监测命令,command是必填参数。
--over参数不能与其他参数混合使用。
使用实例
举例1:输入“watch -c 5 task 1”。
举例2:在不需要watch命令监测的情况下,执行“watch --over”。
输出说明
每个周期间隔1秒的执行“task 1”命令,共执行5次,watch命令监测到的结果如下所示:
LiteOS # watch -c 3 task 1
LiteOS # Thu Jan 1 16:26:26 1970
TaskName = Swt_Task
TaskId = 0x1
*******backtrace begin*******
traceback 1 -- lr = 0x08004006 -- fp = 0x0800045e
traceback 2 -- lr = 0x08004000 -- fp = 0x0800194c
traceback 3 -- lr = 0x080040da -- fp = 0x08003e50
traceback 4 -- lr = 0x080015c2 -- fp = 0x080040a8
traceback 5 -- lr = 0x0800396e -- fp = 0x08001598
Thu Jan 1 16:26:27 1970
TaskName = Swt_Task
TaskId = 0x1
*******backtrace begin*******
traceback 1 -- lr = 0x08004006 -- fp = 0x0800045e
traceback 2 -- lr = 0x08004000 -- fp = 0x0800194c
traceback 3 -- lr = 0x080040da -- fp = 0x08003e50
traceback 4 -- lr = 0x080015c2 -- fp = 0x080040a8
traceback 5 -- lr = 0x0800396e -- fp = 0x08001598
Thu Jan 1 16:26:28 1970
TaskName = Swt_Task
TaskId = 0x1
*******backtrace begin*******
traceback 1 -- lr = 0x08004006 -- fp = 0x0800045e
traceback 2 -- lr = 0x08004000 -- fp = 0x0800194c
traceback 3 -- lr = 0x080040da -- fp = 0x08003e50
traceback 4 -- lr = 0x080015c2 -- fp = 0x080040a8
traceback 5 -- lr = 0x0800396e -- fp = 0x08001598
schedstatstart¶
命令功能
schedstatstart命令用于开启调度统计功能。
命令格式
schedstatstart
使用指南
目前该功能仅支持有浮点运算的平台,且该命令依赖于LOSCFG_DEBUG_SCHED_STATISTICS,该宏开关可在菜单项中开启“Enable Scheduler Statistics Debugging”使能。
Debug ---> Enable a Debug Version ---> Enable DebugLiteOS Kernel Resource ---> Enable Scheduler Statistics Debugging。
schedstatstart、schedstatstop和schedstatinfo等3个命令一起配套使用。
使用实例
举例:输入“schedstatstart”。
输出说明
查看“输出说明”。
schedstatstop¶
命令功能
schedstatstop命令用于关闭调度统计功能,并输出各个CPU的调度统计信息。
命令格式
schedstatstop
使用指南
同“使用指南”。
使用实例
举例:输入“schedstatstop”。
输出说明
查看“输出说明”。
schedstatinfo¶
命令功能
schedstatinfo命令用于输出各个任务的CPU调度统计信息。
命令格式
schedstatinfo
使用指南
同“使用指南”。
使用实例
举例:
输入“schedstatinfo”。
输出说明
查看“输出说明”。
文件命令参考¶
使能文件系统命令¶
使用Shell中的文件命令前,需要打开menuconfig菜单使能Shell,详见“配置项”。
cd¶
命令功能
cd命令用于改变当前目录。
命令格式
cd [path]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
path |
目的目录。 |
用户必须具有目的目录的可执行权限。 |
使用指南
未指定目录参数时,会跳转至根目录。
cd后加目录时,跳转至该目录。
目录以 /(斜杠)开头时,表示根目录。
.(点)表示当前目录。
..(点点)表示父目录。
使用实例
举例:
输入“cd ..”。
输出说明
图 1 显示结果如下

pwd¶
命令功能
pwd命令用于显示当前路径。
命令格式
pwd
使用指南
pwd 命令将当前目录的全路径名称(从根目录开始的绝对路径)写入标准输出。全部目录使用 /(斜线)分隔,第一个 / 表示根目录,最后一个目录是当前目录。
使用实例
举例:
输入“pwd”。
输出说明
图 1 查看当前路径

mkdir¶
命令功能
mkdir命令用于创建一个目录。
命令格式
mkdir <dir>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
dir |
需要创建的目录。 |
N/A |
使用指南
mkdir后加所需要创建的目录名,则将在当前目录下创建目录。
mkdir后加带路径的目录名,则将在指定路径下创建目录。
使用实例
举例:
输入“mkdir share”。
输出说明
图 1 在当前目录下创建share目录

rmdir¶
命令功能
rmdir命令用于删除指定的目录。
命令格式
rmdir <dir>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
dir |
要删除的目录,该目录必须为空目录。 |
N/A |
使用指南
rmdir命令只能用来删除目录。
rmdir一次只能删除一个目录。
rmdir只能删除空目录。
使用实例
举例:输入“rmdir dir”。
输出说明
图 1 删除一个名为dir的目录

rm¶
命令功能
rm命令用于删除文件或者非空目录。
命令格式
rm -r <dir>
rm_ _<file>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
-r <dir> |
dir为要删除的目录,支持删除非空目录和空目录。 |
N/A |
file |
要删除文件。 |
N/A |
使用指南
rm命令一次只能删除一个文件。
rm -r命令可以删除非空目录。
须知: 如果使用rm删除系统关键资源(例如/dev),会造成系统死机等未知影响,需慎重使用该命令。
使用实例
举例:
输入“rm 1.c”。
输入“rm -r dir”。
输出说明
图 1 用rm命令删除文件1.c

图 2 用rm -r删除目录dir

touch¶
命令功能
touch命令用于在当前目录下创建一个空文件。
命令格式
touch <filename>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
filename |
需要创建的文件。 |
N/A |
使用指南
touch命令创建的空文件,其默认权限为可读写。
touch命令一次只能创建一个文件。
touch命令操作已存在的文件会成功,不会更新时间戳。
须知: 在系统重要资源路径下使用touch命令创建文件,会对系统造成死机等未知影响,如在“/dev”路径下执行“touch uartdev-0”,会产生系统卡死现象。
使用实例
举例:
输入“touch file.c”。
输出说明
图 1 在当前目录下创建一个名为file.c的文件

cp¶
命令功能
cp命令用于拷贝文件,创建一份副本。
命令格式
cp <source path> <dest path>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
source path |
源文件。 |
目前只支持文件,不支持目录。 |
dest path |
目的路径。 |
支持目录以及文件。 |
使用指南
同一路径下,源文件与目的文件不能重名。
源文件必须存在,且不为目录。
源文件路径支持“*”和“?”通配符,“*”代表任意多个字符,“?”代表任意单个字符。目的路径不支持通配符。当源文件路径可匹配多个文件时,目的路径必须为目录。
目的路径为目录时,该目录必须存在。此时目的文件以源文件名进行命名。
目的路径为文件时,所在目录必须存在。目的文件名就是目的路径中指定的文件名。
目前不支持多文件拷贝。参数大于2个时,只对前2个参数进行操作。
目的文件不存在时创建新文件,已存在则覆盖。
须知: 拷贝系统重要资源文件时,会造成死机等重大未知影响,如拷贝“/dev/uartdev-0”文件时,会产生系统卡死现象。
使用实例
举例:
输入“cp 100HSCAM/FILE0087.MP4 .”。
输出说明
图 1 显示结果如下

dd¶
命令功能
dd命令用于文件读写性能统计。
命令格式
dd file=[PATHNAME] mode=[OPS] bs=[SIZE] count=[N]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
PATHNAME |
带文件路径的文件名。 |
非空 |
OPS |
要测试的文件操作类型,包括读/写,取值1表示读,2表示写。 |
[1, 2] |
SIZE |
一次读/写的block字节数。 |
[0, 0xFFFFFFFF] |
N |
读/写的block个数。 |
[0, 0xFFFFFFFF] |
使用指南
dd命令可并发执行,一条dd命令对应一个线程,因此可以进行多线程的读写测试。
dd命令测试较大的文件读写时,存在耗时较长的情况,可能不会立即输出性能结果,这是正常现象。
bs字段和count字段可以不指定,系统将使用缺省值LOSCFG_DD_DEFAULT_BS 1024、LOSCFG_DD_DEFAULT_CNT 1。
bs字段的SIZE不宜过大,系统将申请SIZE大小的动态内存,过大的SIZE会导致内存申请失败,导致dd命令无法正常执行。
dd流程中仅会执行open、read/write、close操作,用户需自行保证操作dd前已正确挂载对应的文件系统,如有其他操作也需用户自行执行。
使用实例
举例,测试文件写性能如下:
dd file=/app/sd/testfile mode=2 bs=32768 count=32768
输出说明

输出写入文件的总字节数、总耗时以及速率。
ls¶
命令功能
ls命令用于显示当前目录的内容。
命令格式
ls [path]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
path |
|
|
使用指南
ls可以显示文件的大小。
proc下ls无法统计文件大小,显示为0。
使用实例
举例:
输入“ls”。
输出说明
图 1 查看当前系统路径下的内容显示

cat¶
命令功能
cat用于显示文本文件的内容。
命令格式
cat <pathname>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
pathname |
带文件路径的文件名,如果文件在当前路径下,可以直接输入文件名。 |
已存在的文件。 |
使用实例
举例:
输入“cat w“”。
输出说明
图 1 查看w文件的信息

lsfd¶
命令功能
lsfd命令用于显示当前已经打开的文件描述符及对应的文件名。
命令格式
lsfd
使用指南
lsfd命令显示当前已经打开文件的fd号以及文件的名字。
使用实例
举例:
输入“lsfd”。
输出说明
LiteOS # lsfd
fd filename
3 /dev/shell
format¶
命令功能
format命令用于格式化磁盘。
命令格式
format <dev_inodename> <sectors> <option> [label]
参数说明
参数 |
参数说明 |
|---|---|
dev_inodename |
要格式化的设备名。 |
sectors |
分配的单元内存或扇区大小,取值必须为2的幂数,FAT32下最大值为128,取其他值表示自动选择合适的簇大小。不同size的分区,可用的簇大小范围不同。 |
option |
格式化选项,用于选择文件系统的类型,有如下几种参数可供选择:
传入其他值皆为非法,将由系统自动选择格式化方式。若格式化U盘时低格位为1,会出现错误打印。 |
label(可选参数) |
该参数为可选参数,输入值应为字符串,用来指定卷标名。当输入字符串“null”时,则把之前设置的卷标名清空。 |
使用指南
format指令用于格式化磁盘,设备名可以在dev目录下查找。format时必须安装存储卡。
format只能格式化U盘、SD卡和EMMC卡,无法格式化Nand Flash和Nor Flash。
sectors参数必须传入合法值,传入非法参数可能引发异常。
使用实例
举例:
输入“format /dev/mmcblk0p0 128 2”。
输出说明
LiteOS # format /dev/mmcblk0p0 128 2
Format to FAT32, 128 sectors per cluster.
format /dev/mmcblk0p0 Success
mount¶
命令功能
mount命令用于将设备挂载到指定目录。
命令格式
mount <device> <path> <name>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
device |
要挂载的设备。 |
系统拥有的设备。 |
path |
设备要挂载到的目录,用户必须具有该目录的可执行权限。 |
N/A |
name |
要挂载的文件系统类型。 |
vfat,yaffs,ramfs,nfs,procfs,romfs |
使用指南
mount后加需要挂载的设备信息、指定目录以及设备文件格式,就能成功挂载文件系统到指定目录。
使用实例
举例:
输入“mount /dev/mmc0 /bin/vs/sd vfat”。
输出说明
将“/dev/mmc0”以VFAT文件系统类型挂载到“/bin/vs/sd”目录。
LiteOS # mount /dev/mmc0 /bin/vs/sd vfat
mount ok
umount¶
命令功能
umount命令用于卸载已经挂载的文件系统。
命令格式
umount <dir>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
dir |
需要卸载的文件系统对应的挂载目录。 |
系统已挂载的文件系统的目录。 |
使用指南
umount后加上需要卸载的文件系统对应的挂载目录,即将指定文件系统卸载。
使用实例
举例:
输入“umount /bin/vs/sd”。
输出说明
将挂载在“/bin/vs/sd”目录上的文件系统卸载。
LiteOS # umount /bin/vs/sd
umount ok
partinfo¶
命令功能
partinfo命令用于查看系统识别的硬盘、U盘和SD卡的分区信息。
命令格式
partinfo <dev_inodename>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
dev_inodename |
要查看的分区名。 |
合法的分区名。 |
使用实例
举例:
输入“partinfo /dev/sdap0”。
输出说明
LiteOS # partinfo /dev/sdap0
part info :
disk id : 3
part_id in system: 0
part no in disk : 0
part no in mbr : 1
part filesystem : 0C
part dev name : sdap0
part sec start : 2048
part sec count : 167794688
partition¶
命令功能
partition命令用于查看Flash分区信息。
命令格式
partition <nand | spinor>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
nand |
显示Nand Flash分区信息。 |
N/A |
spinor |
显示Spinor Flash分区信息。 |
N/A |
使用指南
当使能YAFFS文件系统时才可以查看Nand Flash分区信息。
当使能JFFS或ROMFS文件系统时才可以查看Spinor Flash分区信息。
使用实例
举例:
输入partition nand。
输出说明
查看Nand Flash统分区信息。
LiteOS # partition nand
nand partition num:0, blkdev name:/dev/nandblk0, mountpt:/yaffs0, startaddr:0x00e00000, length:0x00200000
statfs¶
命令功能
statfs命令用于打印文件系统的信息,如该文件系统类型、总大小、可用大小等信息。
命令格式
statfs <directory>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
directory |
文件系统的挂载路径。 |
必须是存在的文件系统,并且其支持statfs命令。 |
使用指南
打印信息因文件系统而异。
使用实例
以YAFFS文件系统为例:
输入“statfs /yaffs0”。
输出说明
statfs got:
f_type = 1497497427
cluster_size = 2048
total_clusters = 704
free_clusters = 640
avail_clusters = 640
f_namelen = 255
/yaffs0
total size: 9830400 Bytes
free size: 5890048 Bytes
virstatfs¶
命令功能
virstatfs命令用于打印虚拟分区下文件系统的信息,如总大小、可用大小等信息。
命令格式
virstatfs_ _<directory>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
directory |
虚拟分区入口目录的路径或者其子路径。 |
N/A |
使用指南
virstatfs命令只有开启了虚拟分区特性时才有效(LOSCFG_FS_FAT_VIRTUAL_PARTITION=y)。
virstatfs的输入路径,必须是已经成功应用虚拟分区的物理分区上的虚拟分区入口目录,或者其子目录。如果输入路径是该物理分区的根目录,或者该物理分区未成功应用虚拟分区特性,或者路径指向非FAT32文件系统分区,virstatfs命令均会被拒绝执行。
关于虚拟分区特性,详见FAT开发指导中“虚拟分区的接口说明”和“开发流程”。
使用实例
举例:
输入“virstatfs /bin/vs/sd/virpart0”。
输出说明

sync¶
命令功能
sync命令用于同步缓存数据(FAT文件系统中的数据)到SD卡。
命令格式
sync
使用指南
sync命令用来刷新缓存,当没有SD卡插入时不进行操作,有SD卡插入时缓存信息会同步到SD卡。成功时无显示信息。
使用sync命令前,需要使能FAT文件系统,即设置LOSCFG_FS_FAT = y。
使用实例
举例:
输入“sync”,有SD卡时同步到SD卡,无SD卡时不操作。
输出说明
无。
writeproc¶
命令功能
writeproc命令用于向proc文件系统中的文件写入内容。
命令格式
writeproc <val> <operational mark> <path>
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
val |
要写入文件的内容。 |
字符串。 |
operational mark |
操作符,当操作符为“>>”时,表示将内容追加写到指定的文件。 |
目前只允许“>>”。 |
path |
将要写入内容的文件。 |
必须为带绝对路径的文件。 |
使用指南
writeproc用于将指定内容写入proc文件系统中的文件,该文件必须是已经存在的文件,也可以是非用户创建的文件。
当操作符为“>>”,同时文件存在时,将指定内容写入该文件。
使用writeproc命令前,需要使能PROC文件系统,即设置LOSCFG_FS_PROC = y。
使用实例
举例:
输入“writeproc 'sys=2' >> /proc/umap/logmpp”。
输出说明
图 1 修改logmpp中的sys等级

Trace命令参考¶
使能Trace命令¶
使用Shell中的Trace命令前,需要先打开menuconfig菜单使能Shell,详见“配置项”。
同时配置Trace模块,详见Trace开发流程中的“开发指导”,下文中的“离线模式”在menuconfig中的菜单项为:Kernel ---> Enable Extend Kernel ---> Enable Trace Feature ---> Trace work mode ---> Offline mode”。关于Trace模块的详细介绍,详见“Trace”。
trace_mask¶
命令功能
设置事件过滤掩码。
命令格式
trace_mask [MASK]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
MASK |
Trace事件掩码。 |
[0, 0xFFFFFFFF] |
使用指南
如果不设置事件掩码,或者执行该命令时参数缺省,则默认仅开启任务和中断事件记录。
trace_mask后加MASK,则开启对应模块的事件记录。
具体的模块掩码MASK参见“los_trace.h”中定义的LOS_TRACE_MASK。
使用实例
举例:
输入“trace_mask 0”
输入“trace_mask 0xFFFFFFFF”
输出说明
执行“trace_mask 0”,设置所有模块的事件都不记录,命令执行成功后,不会输出信息。
LiteOS # trace_mask 0
LiteOS #
执行“trace_mask 0xFFFFFFFF”,设置所有模块的事件都进行记录,命令执行成功后,不会输出信息。
LiteOS # trace_mask 0xFFFFFFFF
LiteOS #
trace_start¶
命令功能
开启Trace。
命令格式
trace_start
使用指南
输入“trace_start”即开启系统Trace功能,离线模式下会记录系统发生的事件并保存在指定buffer中。
使用实例
举例:开启系统Trace功能,输入“trace_start”。
输出说明
在离线模式下,成功执行“trace_start”命令后,不会输出信息。
LiteOS # trace_start
LiteOS #
trace_stop¶
命令功能
停止Trace。
命令格式
trace_stop
使用指南
输入“trace_stop”即终止系统Trace功能, 停止记录事件。
使用实例
举例:停止系统Trace功能,输入“trace_stop”。
输出说明
成功执行“trace_stop”命令后,不会输出信息。
LiteOS # trace_stop
LiteOS #
trace_dump¶
命令功能
在离线模式下,dump出Trace buffer的信息。
命令格式
trace_dump [1 | 0]
参数说明
参数 |
参数说明 |
取值范围 |
|---|---|---|
1 |
将Trace数据输出到客户端。 |
NA |
0 |
将Trace数据格式化打印。 |
NA |
使用指南
只能在离线模式下使用trace_dump命令。
参数缺省时将格式化打印Trace数据。
trace_dump命令打印的是trace_start和trace_stop之间的数据,所以需要先执行trace_stop停止Trace后,再执行trace_dump打印Trace buffer的信息。
使用实例
举例:
输入“trace_dump”。
输出说明
执行“trace_dump”命令,格式化打印缓存中的数据。
LiteOS # trace_dump
*******TraceInfo begin*******
clockFreq = 180000000
CurEvtIndex = 19
Index Time(cycles) EventType CurTask Identity params
0 0x7da8da5180 0x45 0x5 0x2 0x9 0x20 0x1f
1 0x7dde8c6980 0x45 0x2 0x5 0x1f 0x4 0x9
2 0x7e1431df20 0x45 0x5 0x2 0x9 0x20 0x1f
3 0x7e49e3f720 0x45 0x2 0x5 0x1f 0x4 0x9
4 0x7e7f896cc0 0x45 0x5 0x2 0x9 0x20 0x1f
5 0x7eb53b84c0 0x45 0x2 0x5 0x1f 0x4 0x9
6 0x7eeae0fa60 0x45 0x5 0x2 0x9 0x20 0x1f
7 0x7f20931260 0x45 0x2 0x5 0x1f 0x4 0x9
8 0x7f56388800 0x45 0x5 0x2 0x9 0x20 0x1f
9 0x7f8beaa000 0x45 0x2 0x5 0x1f 0x4 0x9
10 0x7fc19015a0 0x45 0x5 0x2 0x9 0x20 0x1f
11 0x7ff7422da0 0x45 0x2 0x5 0x1f 0x4 0x9
12 0x802ce7a340 0x45 0x5 0x2 0x9 0x20 0x1f
13 0x806299bb40 0x45 0x2 0x5 0x1f 0x4 0x9
14 0x80983f30e0 0x45 0x5 0x2 0x9 0x20 0x1f
15 0x80cdf148e0 0x45 0x2 0x5 0x1f 0x4 0x9
……
24 0x6c560a8d00 0x24 0x2 0x2d 0x0 0x0 0x0
25 0x6c8baf7600 0x25 0x2 0x2d 0x0 0x0 0x0
……
36 0x71fe6f2000 0x24 0x2 0x2d 0x0 0x0 0x0
37 0x7234140900 0x25 0x2 0x2d 0x0 0x0 0x0
38 0x7269c20250 0x45 0x2 0x1 0x1f 0x4 0x0
39 0x734055a650 0x45 0x1 0x2 0x0 0x8 0x1f
40 0x7380b52450 0x45 0x2 0x1 0x1f 0x4 0x0
……
48 0x77a6d3b300 0x24 0x2 0x2d 0x0 0x0 0x0
49 0x77dc789c00 0x25 0x2 0x2d 0x0 0x0 0x0
50 0x7812269550 0x45 0x2 0x1 0x1f 0x4 0x0
……
*******TraceInfo end*******
trace_reset¶
命令功能
在离线模式下,清除Trace buffer中的事件数据。
命令格式
trace_reset
使用指南
只能在离线模式下使用trace_reset命令。
使用实例
举例:
输入“trace_reset”。
输出说明
执行“trace_reset”,清除事件数据:
LiteOS # trace_reset
LiteOS #
调度统计¶
功能说明
LiteOS提供调度统计维测功能,该功能用于统计CPU的一些调度信息,包括idle任务启动时间、idle任务运行时长、调度切换次数、中断次数、核间迁移次数(多核)等。
使用方法
在菜单项中使能Debug ---> Enable a Debug Version ---> Enable DebugLiteOS Kernel Resource ---> Enable Scheduler Statistics Debugging,可以开启调度统计维测功能(仅支持有浮点运算功能的平台)。
输入shell命令schedstatstart,开启调度统计功能。
输入shell命令schedstatstop,关闭调度统计功能,系统将输出各个CPU的调度统计信息。
输入shell命令schedstatinfo,系统将输出各个任务的CPU调度统计信息。
注意事项
shell指令执行顺序:schedstatstart -> schedstatstop -> schedstatinfo。
输出说明
调用schedstatstart后系统输出如下图所示:

调用schedstatstop后系统输出如下图所示:

参数描述如下表所示:
参数
描述
Passed Time
调度统计功能运行时长。
CPU
CPU ID。
Idle(%)
Idle任务运行时长百分比。
ContextSwitch
任务调度切换次数。
HwiNum
中断触发次数。
Avg Pri
切入任务不为idle任务的任务优先级平均值。
HiTask(%)
高优先级任务运行时长所占百分比,定义优先级小于16为高优先级。
HiTask SwiNum
切入新任务为高优先级的切换次数,定义优先级小于16为高优先级。
HiTask P(ms)
高优先级任务运行的平均时长,定义优先级小于16为高优先级。
MP Hwi
核间中断触发次数,仅用于多核。
MGR
核间迁移次数,仅用于多核。
调用schedstatinfo后系统输出如下图所示:

参数描述如下表所示:
参数
描述
Passed Time
调度统计功能运行时长。
Task
任务名称。
TID
任务ID。
Total Time
所有CPU的任务运行时长。
Total CST
所有CPU任务上下文切换次数。
Total MGR
所有CPU的核间迁移次数,仅用于多核。
CPU
任务运行的CPU ID。
Time
任务在某个CPU上的运行时长。
CST
任务在某个CPU上的上下文切换次数。
MGR
任务在某个CPU上的核间迁移次数,仅用于多核。
内存调测方法¶
概述¶
LiteOS提供内存维测功能,在菜单项中使能Debug ---> Enable a Debug Version ---> Enable MEM Debug,可以开启内存维测。
开启内存维测后,在LOS_MemFree和LOS_MemRealloc接口中会根据链表关系对目标节点及前后节点进行合法性校验,若检测到异常则立即挂起系统并抛出异常。除此之外,还提供了内存维测子功能,各功能可以独立使能,详见后续章节。
多内存池机制¶
使用场景
系统中使用多个动态内存池时,需对各内存池进行管理和使用情况统计。
功能说明
系统内存机制中通过链表实现对多个内存池的管理。内存池需回收时可调用对应接口进行去初始化。
通过多内存池机制,可以获取系统各个内存池的信息和使用情况,也可以检测内存池空间分配交叉情况,当系统两个内存池空间交叉时,第二个内存池会初始化失败,并给出空间交叉的提示信息。
功能分类 |
接口名 |
描述 |
|---|---|---|
初始化内存池 |
LOS_MemInit |
初始化一块指定的动态内存池,大小为size。 |
删除内存池 |
LOS_MemDeInit |
删除指定内存池,仅打开LOSCFG_MEM_MUL_POOL时有效。 |
显示系统内存池 |
LOS_MemPoolList |
打印系统中已初始化的所有内存池,包括内存池的起始地址、内存池大小、空闲内存总大小、已使用内存总大小、最大的空闲内存块大小、空闲内存块数量、已使用的内存块数量,仅打开LOSCFG_MEM_MUL_POOL时有效。 |
使用方法
打开菜单开启多内存池机制。
功能依赖于LOSCFG_MEM_MUL_POOL,使用时在菜单项中开启“Enable Memory multi-pool control”:
Debug ---> Enable a Debug Version---> Enable MEM Debug---> Enable Memory multi-pool control调用LOS_MemInit接口进行内存池初始化,内存池回收时调用LOS_MemDeInit接口进行去初始化。
调用LOS_MemInfoGet获取指定内存池的信息和使用情况。
调用LOS_MemPoolList获取系统所有内存池信息和使用情况。
注意事项
初始化内存池时,需保证各内存池空间无交叉,若交叉则会导致初始化失败。
malloc/free系列接口默认从OS系统内存池申请和释放内存,其它内存池的操作必须调用LiteOS内存接口(LOS_MemAlloc等),不能调用malloc/free系列接口及其相关封装接口。
内存池回收必须调用LOS_MemDeInit接口去初始化(回收前需确保池中内存块均已释放),否则二次初始化该内存池空间会失败,导致该内存池不能被重新使用。
内存池大小需根据业务实际情况合理分配。
编程实例
void test(void)
{
UINT32 ret = 0;
UINT32 size = 0x100000;
VOID *poolAddr1 = LOS_MemAlloc(OS_SYS_MEM_ADDR, size);
ret = LOS_MemInit(poolAddr1, size);
if (ret != 0) {
PRINTK("LOS_MemInit failed\n");
return;
}
VOID *poolAddr2 = LOS_MemAlloc(OS_SYS_MEM_ADDR, size);
ret = LOS_MemInit(poolAddr2, size);
if (ret != 0) {
PRINTK("LOS_MemInit failed\n");
return;
}
PRINTK("\n********step1 list the mem poll\n");
LOS_MemPoolList();
LOS_MemDeInit(poolAddr1);
if (ret != 0) {
PRINTK("LOS_MemDeInit failed\n");
return;
}
PRINTK("\n********step2 list the mem poll\n");
LOS_MemPoolList();
LOS_MemDeInit(poolAddr2);
if (ret != 0) {
PRINTK("LOS_MemDeInit failed\n");
return;
}
PRINTK("\n********step3 list the mem poll\n");
LOS_MemPoolList();
}
log:
********step1 list the mem poll
pool0 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8017b2c0 0x100000 0x2e1fc 0xd1d20 0xd1d20 0x2b 0x1
pool1 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8027b2c0 0x7d84d40 0x7070c8 0x767db94 0x767db94 0x1026 0x1
pool2 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8078244c 0x100000 0x10 0xfff0c 0xfff0c 0x1 0x1
pool3 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8088245c 0x100000 0x10 0xfff0c 0xfff0c 0x1 0x1
********step2 list the mem poll
pool0 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8017b2c0 0x100000 0x2e1fc 0xd1d20 0xd1d20 0x2b 0x1
pool1 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8027b2c0 0x7d84d40 0x7070c8 0x767db94 0x767db94 0x1026 0x1
pool2 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8088245c 0x100000 0x10 0xfff0c 0xfff0c 0x1 0x1
********step3 list the mem poll
pool0 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8017b2c0 0x100000 0x2e1fc 0xd1d20 0xd1d20 0x2b 0x1
pool1 :
pool addr pool size used size free size max free node size used node num free node num
--------------- -------- ------- -------- -------------- ------------- ------------
0x8027b2c0 0x7d84d40 0x7070c8 0x767db94 0x767db94 0x1026 0x1
内存合法性检查¶
使用场景
业务发生踩内存导致内存节点控制头被踩,长时间后才触发业务异常,业务逻辑复杂,难以定位发生踩内存的位置。
功能说明
开启该功能后,在动态内存申请接口中增加内存合法性检查,对动态内存池中所有节点控制头的合法性进行检查,若已发生动态内存节点被踩,及时触发异常,输出error信息,缩小问题定位范围。
使用方法
打开菜单开启内存合法性检查。
功能依赖LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK,使用时在菜单项中开启“Enable integrity check or not”:
Debug ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable integrity check or not发生踩内存后,下一次内存申请操作即触发异常,并给出被踩节点和前一节点信息,可初步分析定位是否是前一节点越界踩内存。踩内存发生范围为上一次内存申请和此次内存申请之间。检测到节点被踩会主动Panic并打印异常信息。
注意事项
该功能开启时,系统内存申请操作的性能下降明显,建议仅在定位问题时开启,默认关闭。
踩内存问题定位实例
通过构造超出内存长度的memset操作,构造越界踩内存,造成内存节点损坏,构造代码如下:
VOID SampleFunc(VOID *p)
{
memset(p, 0, 0x110); // 超出长度的memset,设置踩内存场景
}
UINT32 Test(UINT32 argc, CHAR **args)
{
void *p1, *p2;
p1 = LOS_MemAlloc((void*)OS_SYS_MEM_ADDR, 0x100);
p2 = LOS_MemAlloc((void*)OS_SYS_MEM_ADDR, 0x100);
dprintf("p1 = %p, p2 = %p \n", p1, p2);
SampleFunc(p1); // 因为p1和p2相邻,当memset的长度超过p1的内存大小,就会越界踩到p2内存
LOS_MemFree(OS_SYS_MEM_ADDR, (void *)p1);
LOS_MemFree(OS_SYS_MEM_ADDR, (void *)p2);
return 0;
}
执行上述代码后,执行Shell命令“memcheck”,其输出内容如下:

从上图可以看到打印了错误信息。
标记2所指“cur node:0x11b1ac”表示该节点内存被踩,“pre node:0x11b09c”表示被踩节点前面的节点。标记3所示“pre node was allocated by task:app_Task”表示在app_Task任务中发生了踩内存。
标记1打印的是p1和p2内存的起始地址,“p2 = 0x11b1bc”,减去控制头大小0x10,即p2-0x10=0x11b1ac,就是cur node打印出的地址,即p2内存被踩。从代码可以看到p1和p2是两个相邻的节点(这也可以从打印的p1和p2地址看出来,即p1+p1的size+控制头大小=p2,0x11b0ac+0x100+0x10=0x11b1bc),所以“pre node:0x11b09c”应该就是p1的地址,从标记1获取p1地址为“p1 = 0x11b0ac”,即pre node加上控制头大小0x10(0x11b09c+0x10=0x11b0ac)。
内存size检查¶
使用场景
memset和memcpy操作动态内存,发生越界踩内存问题。
功能说明
对于memset和memcpy操作,当入参为动态内存节点时,增加对内存节点实际大小与入参指定大小的检查,若指定大小大于节点实际大小时,输出error信息,并且取消该次memset或memcpy操作,所以能够防止操作越界。动态内存越界场景下,可开启该功能定位问题。
接口名 |
描述 |
|---|---|
LOS_MemCheckLevelSet |
设置内存检查级别。 |
LOS_MemCheckLevelGet |
获取内存检查级别。 |
LOS_MemNodeSizeCheck |
获取指定内存块的总大小和可用大小。 |
错误码
序号 |
定义 |
实际数值 |
描述 |
参考解决方案 |
|---|---|---|---|---|
1 |
LOS_ERRNO_MEMCHECK_PARA_NULL |
0x02000101 |
LOS_MemNodeSizeCheck的入参中存在空指针。 |
传入有效指针。 |
2 |
LOS_ERRNO_MEMCHECK_OUTSIDE |
0x02000102 |
内存地址不在合法范围内。 |
输入内存地址本身不在内存管理范围之内,不做处理。 |
3 |
LOS_ERRNO_MEMCHECK_NO_HEAD |
0x02000103 |
内存地址已经被释放或者是野指针。 |
检查传入的内存地址,保证该地址是有效地址。 |
4 |
LOS_ERRNO_MEMCHECK_WRONG_LEVEL |
0x02000104 |
内存检测等级不合法。 |
通过LOS_MemCheckLevelGet检查等级,并通过LOS_MemCheckLevelSet来配置合法的等级。 |
5 |
LOS_ERRNO_MEMCHECK_DISABLED |
0x02000105 |
内存检测被关闭。 |
通过LOS_MemCheckLevelSet使能内存检测。 |
须知: 错误码定义见“错误码简介”。8~15位代表的所属模块为动态内存模块,值为0x01。
使用方法
打开菜单开启内存size检查:
目前只有bestfit内存管理算法支持该功能,所以需要使能LOSCFG_KERNEL_MEM_BESTFIT。
Kernel ---> Memory Management ---> Dynamic Memory Management Algorithm ---> bestfit同时该功能依赖于LOSCFG_BASE_MEM_NODE_SIZE_CHECK,该宏开关可以通过在菜单项中开启“Enable size check or not”使能。
Debug ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable size check or not
注意事项
开启该功能后,memset和memcpy性能下降,建议仅在需要定位越界问题时开启,默认关闭。
内存越界问题定位实例
通过构造超出内存长度的memset和memcpy操作,构造越界踩内存问题,构造代码如下:
VOID test(VOID)
{
UINT32 size = 0x100;
VOID *poolAddr1 = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, size);
if (poolAddr1 == NULL) {
PRINTK("malloc poolAddr1 failed\n");
return;
} else {
PRINTK("malloc poolAddr1 %p successed\n", poolAddr1);
}
VOID *poolAddr2 = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, size);
if (poolAddr2 == NULL) {
PRINTK("malloc poolAddr2 failed\n");
return;
} else {
PRINTK("malloc poolAddr2 %p successed\n", poolAddr2);
}
LOS_MemCheckLevelSet(LOS_MEM_CHECK_LEVEL_LOW); // 开启对memset和memcpy的长度检测
PRINTK("memset poolAddr1 overflow\n");
memset(poolAddr1, 0x10, size * 2); // 超出长度的memset
PRINTK("memset poolAddr1\n");
memset(poolAddr1, 0x10, size); // 合理长度的memset
PRINTK("memcpy poolAddr2 overflow\n");
memcpy(poolAddr2, poolAddr1, size * 2); // 超出长度的memcpy
PRINTK("memcpy poolAddr2\n");
memcpy(poolAddr2, poolAddr1, size); // 合理长度的memcpy
LOS_MemCheckLevelSet(LOS_MEM_CHECK_LEVEL_DISABLE); // 关闭对memset和memcpy的长度检测
LOS_MemFree((VOID *)OS_SYS_MEM_ADDR, (VOID *)poolAddr1);
LOS_MemFree((VOID *)OS_SYS_MEM_ADDR, (VOID *)poolAddr2);
return 0;
}
log:
malloc poolAddr1 0x80349514 successed
malloc poolAddr2 0x80349624 successed
LOS_MemCheckLevelSet: LOS_MEM_CHECK_LEVEL_LOW
memset poolAddr1 overflow
[ERR] ---------------------------------------------
memset: dst inode availSize is not enough availSize = 0x100, memcpy length = 0x200
runTask->taskName = osMain
runTask->taskId = 64
*******backtrace begin*******
traceback 0 -- lr = 0x80209798 fp = 0x802c6930
traceback 1 -- lr = 0x80210fc4 fp = 0x802c6954
traceback 2 -- lr = 0x8020194c fp = 0x802c6994
traceback 3 -- lr = 0x80201448 fp = 0x802c699c
traceback 4 -- lr = 0x802012fc fp = 0x0
[ERR] ---------------------------------------------
memset poolAddr1
memcpy poolAddr2 overflow
[ERR] ---------------------------------------------
memcpy: dst inode availSize is not enough availSize = 0x100, memcpy length = 0x200
runTask->taskName = osMain
runTask->taskId = 64
*******backtrace begin*******
traceback 0 -- lr = 0x80209798 fp = 0x802c6930
traceback 1 -- lr = 0x8020dbc4 fp = 0x802c6954
traceback 2 -- lr = 0x8020194c fp = 0x802c6994
traceback 3 -- lr = 0x80201448 fp = 0x802c699c
traceback 4 -- lr = 0x802012fc fp = 0x0
[ERR] ---------------------------------------------
memcpy poolAddr2
由于开启size检测,非法的memset和memcpy操作被取消,输出error信息。“runTask->taskName = osMain”显示了该非法操作发生在osMain函数中,并打印寄存器lr和fp的值。此时可以打开编译后生成的 .asm反汇编文件,默认生成在“RTOS_Lite/out/<platform>”目录下,其中的platform为具体的平台名,通过对比“寄存器lr”的值,查看函数的嵌套调用。
内存泄露检测¶
使用场景
业务运行中发生内存泄露,业务逻辑复杂或者长时间运行才出现。
功能说明
申请内存和释放内存时,在内存节点控制头中记录函数调用栈,发生内存泄露时,通过分析used节点信息,可定位疑似内存泄露的位置。
使用方法
多层栈回溯:
打开菜单开启内存泄漏检测。
目前只有bestfit内存管理算法支持该功能,需要使能LOSCFG_KERNEL_MEM_BESTFIT。
Kernel ---> Memory Management ---> Dynamic Memory Management Algorithm ---> bestfit同时该功能依赖于LOSCFG_MEM_LEAKCHECK,可以在菜单项中选择“Enable Function call stack of Mem operation recorded”开启该宏开关:
Debug ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable Function call stack of Mem operation recorded
配置调用栈回溯信息菜单配置路径:
Debug ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable Function call stack of Mem operation recordedLOSCFG_MEM_OMIT_LR_CNT:调用栈回溯忽略层级,默认配置为2。
LOSCFG_MEM_RECORD_LR_CNT:调用栈回溯记录个数,默认配置为3。
默认配置下,获取0~2层LR信息,忽略第0和第1两层(调用封装接口的节点0层和1层LR信息相同),记录第2层。
使用Shell命令“memused”获取used节点数据。
系统稳定运行后,若used节点个数随时间一直增加,极大可能存在内存泄露,对数据进行对比分析,重点关注LR重复的节点是否存在内存泄露,泄漏点可通过LR信息进行回溯查找。
打印log信息如下,memused命令说明详见“memused”:
LiteOS # memused node LR[0] LR[1] LR[2] 0x802d7b34: 0x8006d86c 0x8011c604 0x8011c758 0x802dab6c: 0x8006d16c 0x8006d8a0 0x8011c604
基于任务的内存节点统计:
打开菜单开启LOSCFG_MEM_DFX_SHOW_CALLER_RA宏。
Kernel ---> Memory Management ---> Enable Memory Task Usage Statistics ---> Enable Show where MemAlloc function is called目前bestfit和bestfit_little内存管理算法都支持该功能。
使用Shell命令“task_mem”获取used节点数据。
打印log信息如下:
HuaWei LiteOS # task_mem ========== pool info ========== pool addr pool size used size free size max free node size used node num free node num UsageWaterLine --------------- -------- ------- -------- -------------- ------------- ------------ ------------ 0x114770 0x7eb890 0x5c68 0x7e5ae8 0x7e5ae8 0x9 0x1 0x5fd0 ========== [all task heap] pool start: 0x114770 ========== Task:Swt_Task(taskId=00) malloc details: Current total pool alloc size=0 Bytes Current total slab alloc size=400 Bytes Current total node cost size=0 Bytes Task:IdleCore000(taskId=01) malloc details: callerRA=0x00101d50 : totalSize=00018528 Bytes, blockCount= 3 callerRA=0x00106e36 : totalSize=00000604 Bytes, blockCount= 1 callerRA=0x00108172 : totalSize=00002068 Bytes, blockCount= 1 callerRA=0x001031a2 : totalSize=00002456 Bytes, blockCount= 4 Current total pool alloc size=23656 Bytes Current total slab alloc size=0 Bytes Current total node cost size=180 Bytes
分析定位
callerRA: 内存申请调用者。
totalSize: 调用者申请的总size大小。
blockCount:调用者申请的内存块个数。
定位思路:多次执行taskmem命令,重点排查size和count一直在递增的callerRA;
如果callerRA指向用户自己封装的user_defined_malloc接口,则在user_defined_malloc前后加上callerRA统计打点,示例如下:
#include "los_memory.h" void *user_defined_malloc(size) { LOS_SaveCallerRa((uintptr_t)__builtin_return_address(0)); /* 函数主体 ...... */ LOS_ResetCallerRa(); }
再次编译运行后,可以通过命令查看调用user_defined_malloc函数的上层函数callerRA,由此找到内存泄漏点。
注意事项
上述功能开启时会增加系统内存占用,影响内存操作性能,建议仅在定位问题时开启,默认关闭。
多层栈回溯内存占用size = 内存节点个数 * (LOSCFG_MEM_RECORD_LR_CNT - LOSCFG_MEM_OMIT_LR_CNT)* sizeof(指针)。
基于任务的内存节点统计内存占用size = 内存节点个数 * sizeof(unsigned int)。
内存重复释放检测¶
使用场景
业务运行中出现内存重复释放,业务逻辑复杂或者长时间运行才出现。
功能说明
释放内存时,在内存节点控制头中记录函数调用栈,发生内存泄露时,通过分析节点信息调用栈信息,可定位内存重复释放的位置。
使用方法
打开LOSCFG_MEM_DFX_DOUBLE_FREE_CHECK宏
Kernel ---> Memory Management ---> Enable check mem double free check by record function called address
目前bestfit和bestfit_little内存管理算法都支持该功能。
当发生内存重复释放时,会打印log信息:(当开启LOSCFG_MEM_DEBUG时,系统会因为重复释放挂死)
Memory double free detected!
Current running taskId = 3, taskName = app_Task.
Memory first free function addr = 0x0x100860.
Memory double free function addr = 0x0x101710.
任务间通信调测方法¶
队列调测方法¶
功能说明
队列是一种生产者消费者模型,生产者生产消息放入队列,等待被消费者使用。如果队列已满,生产者被挂起,如果队列已空,消费者被挂起。LiteOS中使用队列传递消息时,可以设置超时时间,队列的主要作用是实现任务间的异步通信。通过Shell命令“queue”或者使用OsShellCmdQueueInfoGe函数可以查看队列的使用情况。
使用方法
“queue”命令和OsShellCmdQueueInfoGe函数依赖LOSCFG_DEBUG_QUEUE,使用时需要在menuconfig中开启“Enable Queue Debugging”。
Debug ---> Enable a Debug Version ---> Enable MMC Debug ---> Enable mmc dma descriptors dump
使用实例
在Shell窗口中执行命令“queue”或者直接在任务中使用OsShellCmdQueueInfoGet函数,打印系统中的队列信息如下:

其输出项的含义见“队列”,调试过程中主要使用上图中的标识项“TaskEntry of creator”,即创建队列的接口函数地址(0x100830)。在.asm反汇编文件(make编译默认在“LiteOS/Huawei_LiteOS/out/<platform>”目录下,CMake编译默认在产品对应的目录下, 其中的platform为具体的平台名)中找到该地址,可以看到创建队列的函数名,比如这里的app_init(0x100830),见下图。

死锁检测机制¶
功能说明
在许多应用中进程需要以独占的方式访问资源,当操作系统允许多个进程并发执行时可能会出现进程永远被阻塞现象,如两个进程分别等待对方所占的资源,于是两者都不能执行而处于永远等待状态,此现象称为死锁。死锁检测模块支持自旋锁、互斥锁、 二值信号量和线程锁(pthread mutex)四种类型锁的死锁,以及重复上锁、错误释放和锁的深度溢出的检测。
使用方法
打开菜单,进入Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Lockdep Debugging菜单, 完成死锁检测模块的配置。
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_KERNEL_LOCKDEP |
使能死锁检测功能。 |
YES/NO |
NO |
LOSCFG_KERNEL_MEM_ALLOC |
LOSCFG_KERNEL_SPINDEP |
使能自旋锁死锁检测。 |
YES/NO |
NO |
LOSCFG_KERNEL_SMP |
LOSCFG_KERNEL_MUXDEP |
使能互斥锁死锁检测。 |
YES/NO |
NO |
LOSCFG_BASE_IPC_MUX |
LOSCFG_KERNEL_SEMDEP |
使能二值信号量死锁检测。 |
YES/NO |
NO |
LOSCFG_BASE_IPC_SEM |
LOSCFG_PTHREAD_MUXDEP |
使能线程锁死锁检测。 |
YES/NO |
NO |
LOSCFG_COMPAT_POSIX |
自旋锁定位实例
打开自旋锁检测后,检测到死锁时会打印死锁信息,死锁检测的打印信息示例如图1所示。
图 1 死锁模块打印信息

复制request addr的值(本例中为0x471d2),在系统镜像的.asm反汇编文件(make编译默认在“LiteOS/Huawei_LiteOS/out/<platform>”目录下,CMake编译默认在产品对应的目录下, 其中的platform为具体的平台名)中找到相应的地址,如图2所示,即可定位到调用的位置及调用函数(本例中为Task_B)。
图 2 反汇编文件中找到对应地址

根据图1中第二个蓝框的自旋锁,通过代码逻辑找到另一个任务持有该锁的情况,再结合代码,调整spinlock调用的时序,从而解决死锁问题。
须知: 自旋锁死锁一般发生在某个CPU卡住、任务不再发生调度时。若不开启自旋锁死锁检查,可以使用JLink等调试工具halt住CPU或者触发看门狗异常,查看PC值是否为自旋锁代码,以此确定是否发生了自旋锁死锁。
互斥锁死锁定位实例
构造ABBA互斥锁死锁场景,具体如下:
有两个任务,分别为Task_A和Task_B,同时系统中还存在其他系统默认初始任务。在任务Task_A中依次申请Mutex1和Mutex0,Task_B任务依次申请Mutex1和Mutex0, 构成两个任务ABBA死锁场景。
代码如下:
#include "los_mux.h"
static UINT32 mutexTest[2];
UINT32 g_TestTaskID01, g_TestTaskID02;
static VOID Task_A(VOID)
{
UINT32 ret;
ret = LOS_MuxPend(mutexTest[0], LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
dprintf("%s pend muxid %d\n", __func__, mutexTest[0]);
}
LOS_TaskDelay(0x2);
ret = LOS_MuxPend(mutexTest[1], 6000); // 定时阻塞模式 6000tick
if (ret == LOS_ERRNO_MUX_TIMEOUT) {
dprintf("%s pend muxid %d\n", __func__, mutexTest[1]);
}
return;
}
static VOID Task_B(VOID)
{
UINT32 ret;
ret = LOS_MuxPend(mutexTest[1], LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
dprintf("%s pend muxid %d\n", __func__, mutexTest[1]);
}
LOS_TaskDelay(0x2);
ret = LOS_MuxPend(mutexTest[0], 6000); // 定时阻塞模式 6000tick
if (ret == LOS_ERRNO_MUX_TIMEOUT) {
dprintf("%s pend muxid %d\n", __func__, mutexTest[0]);
}
return;
}
UINT32 mutexDeadlock(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S stTask1 = {0};
stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Task_A;
stTask1.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
stTask1.pcName = "Task_A";
stTask1.usTaskPrio = 9;
TSK_INIT_PARAM_S stTask2 = {0};
stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Task_B;
stTask2.uwStackSize = LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE;
stTask2.pcName = "Task_B";
stTask2.usTaskPrio = 9;
ret = LOS_MuxCreate(&mutexTest[0]);
ret |= LOS_MuxCreate(&mutexTest[1]);
if (ret != LOS_OK) {
PRINT_ERR("create mutex error %u\n", ret);
}
ret = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
ret |= LOS_TaskCreate(&g_TestTaskID02, &stTask2);
if (ret != LOS_OK) {
PRINT_ERR("create task error %u\n", ret);
}
LOS_TaskDelay(3000); // 任务休眠3000Ticks
ret = LOS_MuxDelete(mutexTest[0]);
ret = LOS_MuxDelete(mutexTest[1]);
LOS_TaskDelete(g_TestTaskID01);
LOS_TaskDelete(g_TestTaskID02);
return LOS_OK;
}
针对以上场景出现的问题,根据日志分析即可,具体操作步骤如下:
注:详细流程可参考自旋锁定位实例。
根据dump信息,可以看出任务Task_A与Task_B发生死锁,。
在.asm反汇编文件中,找到相应的任务函数,定位到互斥锁的pend位置及调用的接口。
根据上下文确认死锁原因,并调整申请锁的时序。
异常交互¶
使用场景
操作系统对运行期间发生异常时,用户可以使用Shell命令进一步定位异常原因,查看系统状态。
功能说明
系统发生异常时,保留Shell基础交互功能。
配置项
配置项 |
含义 |
取值范围 |
默认值 |
依赖 |
|---|---|---|---|---|
LOSCFG_EXC_INTERACTION |
异常交互功能的裁剪开关 |
YES/NO |
NO |
LOSCFG_SHELL && LOSCFG_KERNEL_CONSOLE |
使用方法
打开菜单开启异常交互功能。
该功能需要配置LOSCFG_EXC_INTERACTION=y,默认处于关闭状态。若关闭该选项,则异常交互失效。
开启该宏开关的菜单项为:
Debug ---> Enable a Debug Version ---> Enable Shell ---> Enable exc interaction输入“help”,查看当前支持的Shell命令。
注意事项
系统异常时,异常交互功能只会使能串口中断、其他中断都会被关闭;用户创建的任务也会被删除,只保留异常交互所使用的基本任务。
异常交互属于调测功能,默认配置为关闭,正式商用产品中禁止使用该功能。
免责声明:华为不承担在正式商用产品中使用该功能的任何风险。
低功耗调测方法¶
低功耗调测使能¶
LiteOS提供低功耗维测功能,在菜单项中使能Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource--->Enable Lowpower Sleep Debugging,可以开启低功耗维测。使能低功耗维测先使能低功耗框架需要先打开menuconfig菜单使能Shell,详见“配置项”;同时启用低功耗框架,详见“低功耗框架”。该功能仅供用户开发阶段使用,正式版本商用前请关闭此功能。
系统状态信息查询¶
功能说明
系统状态信息查询命令会打印出中断、定时器和任务的详细信息。阻止任务深睡事件查询、系统休眠唤醒打点命令配合该命令使用,方便用户定位。系统状态信息查询命令效果可参考系统命令hwi、swtmr、task,详见“系统命令参考”。
使用方法
输入命令“pm sysinfo”。

注意事项
打印中断与定时器信息,应先支持系统命令hwi、swtmr。详见“系统命令参考”。
低功耗动态开关¶
功能说明
低功耗动态开关支持手动打开、关闭、状态查询功能,默认处于打开状态。
使用方法
打开低功耗动态开关:lpm enable。
关闭低功耗动态开关:lpm disable。
查询低功耗动态开关的状态:lpm status。
注意事项
无。
阻止系统深睡事件查询¶
功能说明
阻止系统深睡事件是指用户钩子getSleepMode返回深睡模式,但是系统经判断无法进入深睡的事件。阻止系统深睡的原因可能是距离定时器或任务到期剩余的时间达不到深睡要求的最低时间阈值、任务投票阻止等。
使用方法
开机以来阻止系统深睡事件查询命令:lpm blockinfo cpuid。cpuid指的是具体的CPU号。
阻止系统深睡事件统计信息清空命令:lpm resetblockinfo cpuid。
示例:
从下图中可以得到以下信息:
0核task id为3的任务阻止系统深睡2次,任务地址为0x8008ced。
0核swtmr id为2的定时任务阻止系统深睡1次,定时器任务地址为0x800fc91。
Normal,3)、(Light,3)、(Deep,3)三个投票各阻碍一次深睡。

注意事项
当前只支持单核,因此cpuid当前只能为0,即“lpm blockinfo 0”或者“lpm resetblockinfo 0”。
统计信息根据定时器id或者任务id记录,任务或定时器销毁与创建可能存在id相同的情况,会被统计到一起,用户应避免此场景。如:某a时刻任务A id为8,执行完后销毁,阻止深睡3次;经过一段时间,任务B被创建,id同样为8,阻止深睡2次;那么统计信息中type为task,id为8的阻碍次数为3次,handler为任务B的handler。
被阻碍一次深睡会统计此刻所有的不满足深睡条件的因素。
系统休眠唤醒打点¶
功能说明
系统休眠唤醒打点功能会对Normal、Light、Deep模式下的睡眠时戳信息进行统计。单项信息依次为cpu号、睡眠模式、睡眠开始时刻、睡眠结束时刻,实际睡眠时间,被唤醒的中断。默认记录条数为10条,深睡时唤醒中断为固定值4294967295。
使用方法
系统休眠唤醒打点查询命令:lpm timestamp cpuid。cpuid指的是具体的CPU号。
修改最大记录条数可通过配置LOSCFG_LOWPOWER_RECORD_COUNT实现,最大值为1000。
示例:

注意事项
当前只支持单核,因此cpuid当前只能为0,即“lpm timestamp 0”。
系统休眠唤醒打点统计的是实际睡眠时戳信息。如上图中用户配置浅睡时间为100,实际浅睡时间为99。
魔法键使用方法¶
使用场景
系统未输出挂死相关信息,但是无响应时,可以通过魔法键查看中断是否有响应。在中断有响应的情况下,可以通过魔法键查看task信息中 的CPUP(CPU占用率),找到是哪个任务长时间占用CPU导致系统其他任务无响应(一般为比较高优先级任务一直抢占CPU,导致低优先级任务无响应)。
功能说明
在UART中断和USB转虚拟串口中断中,嵌入魔法键检查功能,对特殊按键进行识别,输出相关信息。
使用方法
打开菜单开启魔法键功能。
该功能需要配置LOSCFG_ENABLE_MAGICKEY=y,默认处于关闭状态,若关闭该选项,则魔法键失效,开启该宏开关的菜单项为:
Debug ---> Enable MAGIC KEY
说明:
可以在menuconfig中光标移动到该选项上,输入“?”,查看帮助信息:Answer Y to enable LiteOS Magic key. ctrl + r : Magic key check switch; ctrl + z : Show all magic op key; ctrl + t : Show task information; ctrl + p : System panic; ctrl + e : Check system memory pool.
输入“Ctrl + r ” 键,打开或者关闭魔法键检测功能。
在连接UART或者USB转虚拟串口的情况下,输入“Ctrl + r ” 键,打开魔法键检测功能,输出“Magic key on”;再输入一次后,则关闭魔法键检测功能,输出“Magic key off”。魔法键功能如下:
Ctrl + z:帮助键,输出相关魔法键简单介绍。
Ctrl + t:输出任务相关信息。
Ctrl + p:系统主动进入panic,输出panic相关信息后,系统会挂住。
Ctrl + e:系统进行简单的内存池完整性检查,检查发现错误则输出相关错误信息,检查正常则输出“system memcheck over, all passed!”。
注意事项
魔法键检测功能打开情况下,如果需要通过UART或者USB转虚拟串口输入特殊字符需避免与魔法键值重复,否则魔法键会被误触发,而原有设计功能可能出现错误。
栈估算工具使用方法¶
使用场景
对于内存资源受限的嵌入式设备,可以使用栈估算工具分析任务栈,达到精确使用栈空间的目的。
创建任务为任务设置任务栈大小时,可以使用栈估算工具分析任务栈,达到精确设置任务栈大小的目的。
系统划分内存设置中断栈大小时,可以使用栈估算工具分析中断栈,达到精确设置中断栈大小的目的。
可以使用栈估算工具分析任务栈溢出问题。
功能说明
栈估算工具是基于python语言开发的函数栈静态分析工具,是一款离线工具。通过解析反汇编文件,计算得到任务运行中栈空间使用的最大值,为设置任务栈大小、栈溢出分析、栈内存优化等场景,提供静态分析的数据参考。同时栈估算工具还提供递归函数告警和函数调用关系树展示功能。
工具运作机制
栈估算工具逐行解析反汇编文件,将跳转指令转化为函数调用关系,开栈指令解析为函数栈局部空间开销,最终使用深度优先搜索,遍历任务入口函数及其调用的所有子函数,得到任务栈内存使用的峰值。
使用方法
栈估算工具名为“stackusage.py”,使用工具前,可以通过“python3 stackusage.py –h”命令查看帮助信息。栈估算工具支持python3.6及以上版本。如果没有安装python3.6及以上版本,可以参见官网python安装文档完成安装。

生成反汇编文件(可选)。
如果没有反汇编文件,可以通过elf文件生成,例如有“Huawei_LiteOS.elf”文件,可以参考如下命令生成名为“Huawei_LiteOS.asm”的反汇编文件:
arm-none-eabi-objdump -d Huawei_LiteOS.elf > Huawei_LiteOS.asm修改配置文件(可选)。
统计错误分支中的异常处理方法的栈开销,有时是无意义的。因此栈估算工具支持可选地忽略部分断言函数、异常分支处理函数、打印函数等,在统计过程中会将他们的栈开销设置为0。
在修改“cfg/config.ini”文件时,将统计时需要忽略的函数,添加至“Ignore_function”行末尾,如有多个函数可以以空格隔开。工具执行时将自动解析配置文件。

运行栈估算工具。
例如反汇编文件为“Huawei_LiteOS.asm”,想要使用工具估算OsIdleTask任务的任务栈大小,可以参考如下命令运行工具:
python3 stackusage.py -sp -t OsIdleTask -out Huawei_LiteOS.asm执行上面的命令后,其输出如下图所示:

工具输出结果分为四部分:函数调用关系配置文件提示、递归函数告警、计算结果输出、调用关系树状图。调用关系树状图中,显示了任务的调用关系,以及每一层调用函数栈的最大开销和局部栈开销,并以“*Rec”标记对递归调用的函数进行提示,以“*Ignore”标记对忽略统计的函数进行提示。
标注函数指针(可选)。
工具将无法自动解析的函数调用关系,保存至“cfg/config.ini”配置文件中。以下图为例,表示在LOS_LkPrint方法和OsIdleTask任务入口函数中,存在无法自动解析的函数调用关系,需要用户手动将方法中调用的函数名添加到“Remarks_here”行末尾,如果有多个函数则以空格隔开,如果没有需要添加的函数则标注为NULL。

完成函数调用关系配置文件的编辑后,需要按步骤2重新运行工具,以获取更新后的计算结果,此时配置文件中添加的函数调用关系已添加进调用关系树中。
计算任务栈大小。
从工具输出结果中可以看到估算的任务栈大小,比如3显示的OsIdleTask任务栈为476Byte,由于设定任务栈时需要增加任务栈初始化开销和任务切换寄存器压栈开销(建议增加300Byte的开销),所以OsIdleTask的最终任务栈大小计算为776Byte。
估算中断栈(可选)。
栈估算工具无法自动识别或解析中断入口函数和中断回调函数,用户需要在“cfg/config.ini”配置文件中手动配置,配置格式如下,Interrupt_entry表示中断入口,Interrupt_isr表示中断回调函数,冒号后面需要增加中断号、中断回调函数入口。

执行下面的命令,可以进行中断栈估算:
python3 stackusage.py -irq -out Huawei_LiteOS.asm估算结果如下:

中断入口栈开销72Byte,中断号58的回调函数在所有配置的中断号里栈开销最大,为372Byte,所以总共的中断栈开销是444Byte。
工具命令
命令 |
功能 |
|---|---|
-h, --help |
查看帮助信息。 |
-m N |
统计模式,可配置的模式N有以下三种: 0:仅分析汇编代码; 1:分析源代码行号; 2:分析反汇编文件中的源代码。 |
-sp |
分析指定的任务条目,如果不标明-sp,将会统计所有函数。 |
-t taskEntry |
指定任务入口taskEntry,默认值为OsIdleTASK,如果没有指定-sp, 本命令将会被忽略。 |
-out |
将估算结果输出到界面。 |
-j |
将估算结果输出到JSON文件中。 |
-r N |
指定递归函数的递归层数N,默认为1。 |
-irq |
统计中断栈。如果设置了本命令,将只统计中断栈。 |
注意事项
由于基于静态的反汇编文件解析的函数调用关系,无法准确获取到动态赋值的函数指针取值,因此缺失函数调用关系,可能导致计算偏差。使用时请补全函数指针配置文件中的指针取值,以得到更准确的计算结果。
由于静态解析时无法确定递归函数的递归深度,工具默认以调用深度为1进行统计。如果需要设置递归深度值,可在使用工具时增加“-r”入参设置。
解析结果需要增加任务栈初始化开销和任务切换时寄存器压栈开销。这些开销一般与系统可使用的寄存器数量相关,建议增加300Byte的开销。
栈估算工具支持解析ARM指令集和RISC-V指令集的反汇编文件,不支持其他指令集。
由于中断栈随着中断嵌套的深度增加而增加,而中断嵌套又与设置的中断优先级、系统运行的实际情况有关,因此栈估算工具无法静态地估算中断嵌套情景的栈开销。栈估算工具支持用户配置多个中断回调函数入口,统计结束后会给出一个最大的中断栈开销,用户可根据实际业务可能的嵌套情况,参考最大中断栈开销去设置中断栈大小。
调试案例¶
踩内存定位方法¶
异常信息定位踩内存方法¶
通过异常信息定位问题,参见异常接管的“编程实例”。
内存合法性检查定位踩内存方法¶
通过内存合法性检查定位问题,参见“内存合法性检查”。
内存size检查定位踩内存方法¶
通过内存size检查定位问题,参见“内存size检查”。
全局变量踩内存定位方法¶
调试过程中,发现一个全局变量只在一处赋值为0,但使用时打印发现变成一个非零异常值,大概率是该全局变量被踩。
如果已知一个全局变量被踩内存,可在“LiteOS/Huawei_LiteOS/out/<platform>/.map”文件中找到该全局变量所在的地址。注意该地址前面最近被使用的变量,排查是否前面变量操作不当引发踩内存,比如对该前面变量进行memcpy,memset操作时越界,溢出覆盖了当前全局变量。
这里列举一个测试的例子,在文件中定义了两全局变量,并且初始化。
UINT32 g_uwEraseMap[16] = {0};
UINT32 g_uwEraseCount = 0;
在.map文件中可以找到这些全局变量在bss段对应的位置。

若g_uwEraseMap被踩,在“.map”文件中找到其地址,再查找该地址前面的变量,即g_uwEraseCount。特别注意分析g_uwEraseCount变量的使用情况,观察是否存在某处,对变量g_uwEraseCount进行了越界操作。
task状态判断是否踩内存¶
Shell命令“task”,可以查看当前系统所有任务的状态。命令输出的stackSize、WaterLine、StackPoint、Top0fStack信息,可以作为判断任务栈是否踩内存的指标。
这里举例说明如何通过task命令判断是否踩内存,如下图所示,有一任务名为shellTask。

StackSize = 0x3000(创建该任务时分配的栈大小)
WaterLine = 0x2810(水线,目前为止该任务栈已经被使用的内存大小)
StackPoint = 0x80d10084 (任务栈指针, 指向该任务当前的地址)
Top0fStack = 0x80d0d768(栈顶)
MaxStackPoint = Top0fStack + StackSize = 0x80d10768(得到该任务栈最大的可访问地址)
若WaterLine > StackSize,则说明该任务踩内存。
若StackPoint > MaxStackPoint 或 StackPoint < Top0fStack,则说明该任务踩内存。
Trigger定位踩内存方法¶
Trigger方法主要用于定位固定地址踩踏的问题,如全局变量,已知被踩地址,可以快速找出被谁踩。
功能分类 |
接口名 |
描述 |
|---|---|---|
根据提供的地址及范围配置保护 |
ArchProtectionSetAddrRo |
参数说明: addr:被保护范围的起始地址,当size为4时,addr需要4字节对齐; size:被保护范围的大小。 当调用ArchProtectionSetAddrRo(0x100000, 4)时,被保护的地址范围为 [0x100000, 0x100003] 共4Byte。 |
取消指定索引的trigger的保护 |
ArchProtectionUnSetAddrRo |
参数说明: index:trigger索引,取值范围为[0, 3],当index == 0xffffffff时,标识取消所有trigger保护。 |
使用方法
打开菜单LOSCFG_TRIGGER_ENABLE宏。
Targets ---> Enable Access Memory trigger.使用API来保护被踩地址。
UINT32 ret = ArchProtectionSetAddrRo(0x1405ca4, 4);对地址写后会触发如下打印:

其中:
task_name:写被保护地址的任务;
mepc:写被保护地址的指令地址,可从反汇编文件中找到对应代码所在行和函数;
caller:最近一层函数调用栈;
diff_addr:被踩的地址;
new_val:被踩地址被写入的新值;
old_val:被踩地址的旧值。
注意事项
当前方案基于LiteOS内核设计,仅针对RISCV核且LiteOS内核没有开启LOSCFG_ARCH_RISCV_INT_AUTO_STACK特性。方案支持的平台为LINX131、LINX132、LINX170以及LINX-1XX_TEE。
LiteOS Kernel启动流程¶
图 1 LiteOS Kernel启动流程

图1为系统启动流程,部分模块的初始化会在模块宏开启时才生效,用户可以选择是否创建app任务(app_init),菜单配置路径为:
Debug ---> Enable TestSuite or AppInit ---> Choose TestSuite or AppInit 菜单。
LOSCFG_APPINIT_TESTSUIT开启后可以选择创建app或testsuite任务,如果不开启,则认为app和testsuite任务都不创建。
平台兼容性适配修改要点¶
差异对比
在32位与64位模式下,涉及的主要差异点在于各个不同类型的数据位宽,开发过程中需要关注,详细如下表所示:
Programming type |
Size in A32 |
Size in A64-LP64 |
|---|---|---|
char |
8bit |
8bit |
short |
16bit |
16bit |
int |
32bit |
32bit |
int *(pointer) |
32bit |
64bit |
long |
32bit |
64bit |
long long |
64bit |
64bit |
float |
32bit |
32bit |
double |
64bit |
64bit |
size_t |
32bit |
64bit |
LiteOS的64位编译器使用的是LP64指令模式,从上表可以看出,指针、long和size_t类型都升级为64bit。
在64位模式下,相比于32位模式,由于位宽的差异会导致一些使用上的差异,请参考如下7个修改要点,对应用代码做相关差异性的检查。
修改要点
指针的处理。
指针不建议强转成非指针类型,如long、int等非指针数据类型。指针可以强转成其他类型的指针和void*,但64位的编译器会报出告警,需要消除掉。
须知:
也不建议指针用于数学计算时临时强转成unsigned long或int,可以转换成char* 然后再加或减常数。long都要修改为int。
除非涉及到内核公共模块,long都要修改为int,不要使用long以及相关强转。
须知:
在某些64bit编程规范中,严格禁止使用long类型。否则很容易出错。仔细检查一切有关int、long、指针的赋值和比较语句。
虽然原则上不允许使用long,但存在引用的数据类型是Kernel内部原生定义为long的情况。这样在不能修改内部原生数据定义的情况下,需要注意检查引用处。
在新的编译器中,数据类型会按照如下规则自动转换:
int + long = long
unsigned + signed = unsigned
在这种原则下,下列代码:
long long a; int b; unsigned int c; b = -2; c = 1; a = b + c;
a的值是0x00000000FFFFFFFF, 而不是-1。原因是b+c是unsigned+signed,结果是个无符号数,再转成long long,只能在前面填充0。而如下代码:
long long a; int b; unsigned int c; b = -2; c = 1; a = (long long) b + c;
a的结果就是0xFFFFFFFFFFFFFFFF,因为b转为long后就是0xFFFFFFFFFFFFFFFE,与unsigned int c相加,就是上述结果。
要在常量表达式中指定数据类型。
如果想将一个常量作为64位来使用,必须清楚地在数值后跟上ll或者LL,否则,编译器有可能把它当作int来对待。例如下面这个函数:
long long SetBitN(long long value, unsigned bitNum) { long long mask; mask = 1 << bitNum; return value | mask; }
在64位下乍一看以为没问题,但其实1被当成int型处理了, 如果bitNum为超过32的数,实际无效,超过的部分会被截断,结果就是0。必须这样写:
long long SetBitN(long long value, unsigned bitNum) { long long mask; mask = 1LL << bitNum; return value | mask; }
另外再举例,以下两种操作会导致指针的高32比特被清0。
(void*)((long long)p & ~(16 - 1)) (void*)((long long)p & ~(alain - 1)) /* alain为u32 */
可以修改成:
(void*)((long long)p & ~((long long)16 - 1)) (void*)((long long)p & ~((long long)alain - 1)) /* alain为u32 */
也可以采用如下定义规避:
1LL // (long long) 1ULL // (unsigned long long)
格式化输入输出问题及解决办法。
与32位系统相比,64位系统会有如下问题:
32位系统可以用%x打印指针,但在64位系统会有截断问题。
解决办法:指针的输入输出需要用%p。
32位系统上,使用%d来打印int或long类型的值,但是在64位平台上会截断。
解决办法:long的输入输出需要用%ld。
关于size_t类型:在32bit平台上,它的原形是unsigned int,而在64bit平台上,它的原形是unsigned long。这导致使用printf等函数时,无论使用%u或者%llu打印都会报warning。目前可采用%lu打印方式,规避该问题。
输入的时候,注意buffer的长度,在64位下发生变化了,确保buffer的长度足够:
char * buf[9]; int a = 0; Sprint(buf, "%p", &a);
上述代码在32bit模式下是正确的,一个指针的值是0xC0001A38,输入后是8个字符。在64bit模式下,长度是16个字符。
最大数、无效数的宏定义。
特别注意代码中有无引用相关最大数,无效数的宏定义,在64bit下可能已经发生变化,如:
#define BYTES_IN_WORD 4 #define INVALID_DMA_ADDRESS 0xffffffff
这些是否在64bit下还是无效或最大,需要注意,如:
size_t count = BIG_NUMBER; for (unsigned int index = 0; index != count; index++) ………
size_t是64位的,而index是32位的,所以上边的循环会一直循环下去。
检查union中成员变量大小。
union中成员变量大小要一致,32、64位差异可能会导致原union中的某些成员变量变大,各成员变量平衡被打破。检查各联合体,确保联合体内各成员变量大小一致。
如下示例是一个常见的开源代码包,可在ILP32却不可在LP64环境下运行。代码假定长度为2的unsigned short数组,占用了与long同样的空间,可这在LP64平台上却不正确。
union{ unsigned long bytes; unsigned short len[2]; } size;
正确的方法是检查是否对结构体有特殊的应用,如果有,那么需要在所有代码中仔细检查联合体,以确认所有的数据成员在LP64中都为同等长度。
优化建议
数据类型方面。
如果确定涉及到64bit计算和存储则定义为long long类型。如果32bit够用的话,数据类型建议使用int或unsigned int,以减少内存。不允许定义long类型。
类型定义使用体系结构无关的类型定义:u32,u16,u64,s16,s32,s64。
结构体定义:将最常用的数据字段都定义在一起相邻位置,这样便于一次取到Cache中,提升运行性能和效率。
由于对齐方式变为8字节了,结构体的排放方式需要注意新的对齐方式。
struct MyStruct { bool m_bool; char *m_pointer; int m_int; };
如上所示,结构体bool和char*之间会填充4个字节,故需要修改结构体排布方式,以节约内存使用,如下:
struct MyStructOpt { char *m_pointer; int m_int; bool m_bool; };
其他建议
关于非对齐操作
不同芯片平台对不同介质的非对齐操作支持程度不一致。在使用非对齐操作前,请确认目标平台是否支持以非对齐方式访问。
关于联合和位域的使用
C语言标准中未定义联合和位域的比特序。在编译时,联合和位域的比特序由编译器实现决定。建议在程序开发中减少联合和位域的使用。





