当前位置:首页 > 嵌入式 > 嵌入式硬件
[导读]摘要:讨论一个利用标准C语言setjmp库函烽实现查询式协作多任务系统,给出完整的内核和样例程序并对源代码进行说明。该系统具有简单易用的特点,只需要编写存取堆栈指针的宏

摘要:讨论一个利用标准C语言setjmp库函烽实现查询式协作多任务系统,给出完整的内核和样例程序并对源代码进行说明。该系统具有简单易用的特点,只需要编写存取堆栈指针的宏就可方便地移植到新的平台上。文章详述了系统的优缺点,讨论一些性能扩展的方法。该内核适用于中小规模的嵌入式软件。 关键词:协作式多任务 C语言 setjmp 引言 本文介绍的是利用标准C语言setjmp库函数实现的具备此特点的协作式多任务系统。从本质上讲,实时多任务操作系统应该具备按照优先级抢占调度的内核。然而,在实际应用中,抢中式的多任务某种程序上带来了用户程序设计时数据保护的困难,并且,具备抢占功能的多任务内核设计时困难也比较多,这会增加操作系统自身的代码,也使它在小资源单片机系统中应用较少;而协作多任务系统的调度只在用户指定的时机发生,这会大大简化内核和用户系统的设计,尤其本文实现的系统通过条件查询来放弃CPU,既符合传统单片机程序设计的思维,又带来了多任务、模块化、可重入的编程便利。 Setjmp是标准C语言库函数的组成部分,它可以实现程序执行中的远程转操作。具体来说,它可以在一个函数中使用setjmp来初始化一个全局标号,然后只要该函数未曾返回,那么在其它任何地方都可以通过longjmp调用来跳转到setjmp的下一条语句执行。实际上,setjmp函数将发生调用处的局部环境保存在一个jmp_buf的结构当中,只要主调函数中对应的内存未曾释放(函数返回时局部内存就失效了),那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。我们的系统中就是分析了setjmp标准库函数的特点,以简单的方式实现了协作式多任务。 1 演示程序 为了便于理解,首先给出多任务演示程序的源代码。这个程序演示了协作式多任务切换、任务的动态生成、多任务共用代码等功能,一共使用了init_coos 初始化根任务(也就是C语言main函数)、creat_task创建新任务和WAITFOR查询条件这3个基本的系统调用。由于面向嵌入式系统,因而程序不会中止并且运行中也没有进行任何输出,需要借助适合的调试工具来理解多任务系统的运行。 example.c文件清单: #include #include“co-os.h” void tskfunc1(int argc,void *argv); void tskfunc2(int argc,void *argv); void subfunc(void); volatile int cnt,test; int main(void){ int i; init_coos(400); creat_tsk(tskfunc1,12,NULL,400); creat_tsk(tskfunc2,0,NULL,400); i=0; while(1){ WAITFOR(cnt= =8); while(i++argc); test=0x55; /*使用函数调用在子程序中测试WAITFOR*/ subfunc(); while(i++15); cnt=0; } } void subfunc(void){ int i; WAITFOR(cnt<5); for(i=0;i<++)test=0x10*i; } 2 内核构成 内核包括一个供外部用户程序包含的头文件(co-os.h)和具体实现的源文件(co-os.c),它们提供了演示程序中用到的3个系统调用。 内核的实现代码假定了CPU堆栈是向下增长的,并且通过宏来直接操作堆栈指针。以下代码在Microsoft VC6 for x86、Borland C++ Builder 5.5、SDS CrossCode7.0 for 68K和GCC3.2 for AVR四种平台中测试过,只需在co-os.h头文件中定义相应的平台类型即可顺利编译。 (1)co-os.h文件清单 #include /*选择X86_VC6,X86_BC5,AVR_GCC或M68H_SDS.*/ #define X86_VC6 #define MAX_TSK 10 typedef struct { void (*entry)(int argc,void *argv); jmp_buf env; int argc; void *argv; }TVB; extern TCB tcb[MAX_TSK]; extern int task_num,tskid; void init_coos(int mainstk); int creat_tsk(void(*entry)(int argc,void *argv),int argc,void *argv,int stksize); #define WAITFOR(condition)do{ setjmp(tcb[tskid].env); if(!(condition)){ tskid++; if(tskid>=task_num)tskid=0; longijmp(tcb[tskid].env,1); } }while(0) (2)co-os.c文件清单 #include "co-os.h" #if defined(X86_VC6)||defined(X86_BC5) #define SAVE_SP(p) _asm mov p,esp #define RESTORE_SP(p) _asm mov esp,p #elif defined(AVR_GCC) #include #define SAVE_SP(p) p=(int*)SP #define RESTORE_SP(p) SP=(int)p #elif defined(M68K_SDS) #define SAVE_SP(p) asm("MOVE.L A7,{"#p"}") #define RESTORE_SP(p) asm("MOVE.L {"#p"},A7") #endif TCB tcb[MAX_TSK]; Int task_num=1; Int tskid; Static int stktop,oldsp; Void init_coos(int mainstk){ SAVE_SP(stktop); stktop=stktop+sizeof(void(*)(void))/sizeof(int) -(mainstk+sizeof(int)-1)/sizeof(int); } int creat_tsk(void(*entry)(int argc,void *argv), int argc,void *argv,int stksize){ if(task_num>=MAX_TSK)terurn-1; SAVE_SP(oldsp); RESTORE_SP(stktop); If(!setjmp(tcb[task_num].env)){ RESTORE_SP(oldsp); tcb[task_num].entry=entry; tcb[task_num].argc=argc; tcb[task_num].argv=argv; task_num++; stktop-=(stksize+sizeof(int)-1)/sizeof(int); } else tcb[tskid].entry(tcb[tskid].argc,tcb[tskid].argv); return 0; } 3 代码说明 任务代码通过执行setjmp设置本任务下次查询时的返回点,然后在等待条件放弃掉CPU跳转到下一任务的返回点处执行。如此周而复始,让各任务都获得轮转运行的机会,也要求各任务都需要主动通过等待条件的方式放弃掉CPU。系统中除了中断服务程序之外,所有任务都是平等的,都应该遵循同样的规则和其它任务一起协作运行。基本系统中没有设计杀死任务的调用,这要求各任务都应当设计成某种形式的无限循环。 任务中等待的条件可以是任务复杂的表达式或都函数调用,也可以是中断服务程序设置的全局变量(注意加volatile定义)。一般在任务执行时会让下次等待的条件不再满足,避免某个任务一直霸占CPU将系统饿死。在嵌入式软件中还经常会遇到任务定时启动和超时等待在I/O操作上,在我们的系统中可以维护一个时间计数器,只需在适当的地方记录时刻,然后在任务查询条件中判断当前计数器和记录时刻之间的差值就可以了。 内核实现的代码则相当简法。由于主要的保护和恢复任务现场的工作都由C语言标准库setjmp实现了,我们就只需要操纵一下堆栈指针防止不同的任务使用了重叠的局部环境,这个工作在初始化和创建任务的时候通过预定义的两个宏来实现。在init_coos函数中,记录了主任务(main函数)保留 mainstk字节堆栈后的新栈顶位置(stktop),然后在每次creat_task时都根据要求为每个任务保留stksize字节的堆栈并重新计算下一个stktop。在creat_task函数中利用了setjmp函数的返回值(直接返回时为0,longjmp跳转返回时非0),使得一方面 creat_task能正常回到调用者,又让下次轮转到新任务时能够找到创建时的入口。 co-os.h中定义的WAITFOR使用了一个do{…while(0)实现多语句宏的C语言小技巧,这样能保证在任何情况下WAITFOR都可以如单条语句一样在源程序中使用,需要担心多了或者少了大括弧破坏if/else匹配之类的问题,并且,所有的编译器都会优化掉这个假循环。 为了尽量使程序简单并说明问题,以上代码中没有考虑中断相关的数据保护问题。实际运行的系统中,如果首先写堆栈指针不能一步完成(如AVR这样的8位机),那么,在写操作正在进行的时候绝对不能允许中断;另外,在任务中查询的条件如果和中断有关,那么也必须考虑数据的完整性。 4 性能分析 所有的协作式多任务系统中任务切换时间都和用户代码(是否长期占用CPU)、就绪时刻有关,比标准系统略差的是,我们的简单系统中不支持任务优先级,并且完成一轮查询调度的时间还和任务数目有关。这也是为了达到简单和可移植性目标而不得已作出的牺牲。在各任务间切换查询条件通过C语言标准库函数 longjmp实现,一般来讲longjmp函数要从内存中恢复大部分CPU寄存器,执行它也需要若干条指令的时间。 为了面向嵌入式系统应用,任务控制块(TCB)采用静态数组来实现,这样要求预先确定系统的最大任务数(co-os.h中的MAX_TSK)。如果需要,也可以通过环波链表来动态管理任务控制块(TCB),这时可以简单实现任务的动态创建和删除,并且通过指针来访问TCB也要比通过下标(tskid)略快一点。 在某些情况下,如果在中断返回后需要执行某关键任务,可以考虑通过设置“高级中断”的方法来实现。具体地讲,就是在中断返回前改变返回地址到某函数入口(“高级中断服务程序”),同时保留原返回地址到堆栈中,这样在“高级中断服务程序”完成后执行return就又回到了正常的多任务查询流程。使用“高级中断”时要注意现场保护的衔接,并且这种技巧显然和CPU和体系结构有关。 5 结论 setjmp是标准C语言中用于远程跳转的库函数,利用它可方便实现一个简单易移植的协作式多任务系统。该系统功能完备、编程简单、易于学习,适合一些中小规模的嵌入式软件使用;并且,以此为基础,还可以用一些与平台相关的编程技巧提高其实时性和灵活性。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭