1. 函数调用
缺点:导致层与层之间紧耦合;上下层必须在统一上下文环境(任务环境);有可能产生循环依赖
上层很可能并非简单的函数模块,而是具有活动能力的任务模块,但是下层通过直接函数调用方式调用上层接口只能保持在同一个任务运行环境中。并且,上下模块间由此存在循环的依赖关系,即使是在同一个任务环境中实现的上下层模块,这种循环依赖关系也是不利于体系结构设计的。
2. 回调函数方式
回调函数方式,解决第一种自己函数接口方式的循环依赖问题,也就是说下层访问上层的接口在下层定义并管理注册,但是在上层实现,并注册到下层管理接口,以便下层访问。回调函数在很多编程环境中都有,大多数都具有改变上下层依赖关系、提供层间抽象的目的。例如 Linux中驱动程序需要编写的驱动函数,就是典型的回调函数方式。回调函数方式改变了依赖关系方向,但是没有改变上层模块是任务环境的问题,上下层模块仍然必须是同一个执行环境。可以看到,通过这些方式的转换,层间的依赖关系得到改善,各层中都转变成为编写基于下方提供的函数接口的方式,这样编写代码逻辑更为自然、灵活,易于表达。例如网络应用开发接口中常见的 Socket套接字,就是可以通过上述方式进行设计后,上层接收应用模块的编写变成对下方提供的功能函数的调用。
3. FIFO缓冲队列缓冲的衔接方式
FIFO 缓冲和队列缓冲方式都有改变依赖关系方向的能力,同时具有不同执行环境之间衔接的能力。 但是并不完全是不同任务执行环境衔接, 因为上层对下层调用还是函数接口。不过这类方案已经可以用于大多数开发环境。例如网络应用,上层是一个应用任务,下层实际上最终来自于 ISR、SISR 或其他任务。下层访问上层是,需要传递到上层的任务环境,但是上层访问下层时,却不需要跨任务环境,只需要调用下层的功能函数可以将数据包发送出去。下图结合内核体系结构描述说明这种上下两个方向不对称的情况。
回调函数方式的应用
比较小比较专用的嵌入式系统如vxworks是通过内核提供回调函数注册接口,应用自身没有main,而是把应用入口初始化函数注册到内核中,待内核初始化完毕后调用应用的初始化函数,进而启动应用任务
而ucosII则让应用的开发者来写main(),系统完成引导后就转入了开发者的main(),而在main()中调用了一个内核初始化函数osinit(),在完成应用和设备的初始化后还要调用一个osstart启动内核进程调度
自陷是为了用户态程序调用内核态api,好处是不用使用内核函数名而是用的系统调用号,这样内核和应用不用静态的连接在一起,系统调用号就象个接口,可以动态注册
一个误区的解释:ucos系统运行时会不会有一个任务都没有的情况?
没有系统会在启动时启动一个idle任务,是优先级最低的任务,这样系统在调度时就不会找不到可以运行的任务了。