任务创建与删除API实战:从入门到避坑
各位同学,今天我们来聊聊VxWorks里最基础也最要命的话题——任务的创建与删除。说实话,我见过太多工程师在taskSpawn()和taskDelete()上栽跟头了。我自己刚入行那会儿,也因为这个踩过不少坑。今天我就把压箱底的经验都掏出来,咱们一个一个说清楚。
taskSpawn()参数详解:你真的会用吗?
taskSpawn()是VxWorks里创建任务最常用的API。它的原型长这样:
int taskSpawn(
char *name, // 任务名
int priority, // 优先级
int options, // 选项
int stackSize, // 栈大小
FUNCPTR entryPt, // 入口函数
int arg1, // 参数1
int arg2, // 参数2
...
);
每个参数都有讲究,我一个个说。
name(任务名):说白了就是个标签,方便你调试时识别。我个人习惯用"t"开头,比如"tRecvTask"、"tSendTask"。注意名字最长不能超过31个字符,否则会被截断。
priority(优先级):0到255,数值越小优先级越高。0是最高,留给系统任务用的。我建议用户任务从50开始往上排。为什么?因为系统任务通常占0-49,你设太低容易跟系统抢资源,搞不好系统就崩了。
options(选项):这里有个坑。常用的选项有:
- VX_FP_TASK:任务使用浮点运算
- VX_NO_STACK_FILL:不填充栈(调试时建议别用)
- VX_PRIVATE_ENV:私有环境
我遇到过最典型的问题:任务里用了浮点运算,但没加VX_FP_TASK选项。结果呢?浮点寄存器被别的任务污染了,算出来的数据全是错的。排查了整整两天才找到原因。
stackSize(栈大小):这个参数我建议宁大勿小。默认是20000字节,但实际项目中我一般给40000起步。为什么?你想想看,任务里调个函数、嵌套几个调用,栈空间就蹭蹭往上涨。栈溢出不会报错,只会悄无声息地覆盖别的内存,然后系统莫名其妙地崩溃。
entryPt(入口函数):就是任务启动后执行的第一个函数。注意这个函数不能返回,否则任务会异常终止。我习惯在函数末尾加个while(1)循环,或者用taskDelay()挂起。
参数1到参数10:最多传10个整型参数。如果需要传更多数据,我建议用指针传结构体地址。
实战经验:我写taskSpawn()时,一定会检查返回值。返回ERROR就说明创建失败,常见原因有内存不足、任务数超限。别偷懒,一定要加错误处理。
taskInit()与taskActivate()组合使用:更灵活的控制
taskSpawn()是一步到位,但有时候我们需要更精细的控制。这时候就要用taskInit() + taskActivate()组合了。
先看taskInit():
STATUS taskInit(
WIND_TCB *pTcb, // TCB指针
char *name, // 任务名
int priority, // 优先级
int options, // 选项
int stackSize, // 栈大小
FUNCPTR entryPt, // 入口函数
int arg1, // 参数1
...
);
taskInit()只是初始化任务控制块(TCB),但任务还没开始运行。这时候你可以做点手脚,比如修改任务的某些属性。等准备好了,再调用taskActivate()让它跑起来。
taskActivate(tid); // tid是taskInit返回的任务ID
为什么要这么麻烦?我举个例子。有一次我需要创建一个任务,但它的优先级要在运行时根据系统负载动态调整。用taskSpawn()的话,优先级一创建就固定了。但用taskInit(),我可以在初始化后、激活前修改优先级,然后再启动。
小技巧:taskInit()分配的内存需要你自己管理。记得在删除任务后释放TCB和栈空间,否则会内存泄漏。
taskDelete()的陷阱:删任务不是闹着玩的
taskDelete()看起来简单:
STATUS taskDelete(int tid);
但这里面的坑,我敢说十个工程师有八个踩过。
陷阱一:任务正在使用共享资源
假设任务A正在操作一个互斥信号量,你突然把它删了。信号量没释放,别的任务就永远等在那里了。这就是死锁。
陷阱二:任务持有内存锁
任务正在malloc分配内存,你把它删了。内存分配器的内部锁没释放,整个系统的内存分配就瘫痪了。
陷阱三:任务正在执行关键操作
比如正在写文件、正在操作硬件寄存器。突然删除,文件可能损坏,硬件可能处于未知状态。
我曾经踩过的坑:有一次在通信模块里,我直接删了一个正在发送数据的任务。结果呢?网卡DMA缓冲区没清干净,后续所有数据包都乱了。排查了整整一个下午,最后发现是taskDelete()惹的祸。
那正确的做法是什么?我建议用事件标志或消息队列来通知任务自己退出。任务收到退出信号后,先释放资源,再调用taskDelete()删除自己。
// 安全删除任务的示例
void myTask(void)
{
while (1)
{
// 检查退出标志
if (exitFlag == TRUE)
{
// 释放资源
semGive(mySem);
// 删除自己
taskDelete(taskIdSelf());
}
// 正常任务逻辑
...
}
}
另外,千万别在中断服务程序里调用taskDelete()。中断上下文不能执行任务删除操作,这是VxWorks的硬性规定。我见过有人这么干,结果系统直接挂掉。
总结一下
任务创建和删除看似简单,但每个细节都可能成为系统的隐患。我的建议是:
- 创建任务时,栈大小给足,选项别漏
- 需要灵活控制时,用taskInit()+taskActivate()
- 删除任务前,确保资源已释放
- 永远不要在中断里删任务
嗯,今天就聊到这儿。下节课我们讲任务优先级反转,那才是真正让人头疼的问题。到时候我给你们讲讲我当年是怎么被优先级反转折磨的。