摘要:学过用过FreeRTOS的人都知道,在创建任务时候我们都要定义一个任务句柄,这个任务句柄有啥含义?书上的解释是任务创建成功以后会返回此任务的任务句柄,这个句柄就是任务的堆栈。此参数就用来保存这个任务句柄。其他API函数可能会使用到这个句柄。
那么任务句柄是到底是怎么一回事,它保存的是任务控制块的首地址。那么它又是如何来保存任务的首地址呢?这就是我们今天要讨论的话题。我尽量写得通俗易懂,让大家都能轻松理解。
1、创建一个任务
动态创建一个任务
#define TASK1_TASK_PRIO 1 //任务优先级
#define TASK1_STK_SIZE 128 //任务栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
//动态创建一个任务1
xTaskCreate((TaskFunction_t )task1_task, //任务函数
(const char* )"task1_task", //任务名称
(uint16_t )TASK1_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )TASK1_TASK_PRIO, //任务优先级
(TaskHandle_t* )&Task1Task_Handler); //任务句柄
//task1任务函数
void task1_task(void *pvParameters)
{
for(;;)
{
vTaskDelay( 2000 );
}
}

参数:
- pxTaskCode:任务函数。
- pcName:任务名字,一般用于追踪和调试,任务名字长度不能超过。
configMAX_TASK_NAME_LEN
,在FreeRTOSConfig.h
文件中宏定义为16。 - usStackDepth:任务堆栈大小,实际申请到的堆栈是
usStackDepth
的4倍。其中空闲任务的任务堆栈大小为configMINIMAL_STACK_SIZE
,在FreeRTOSConfig.h
文件中宏定义为130(字)。 - pvParameters:传递给任务函数的参数。
- uxPriority:任务优先级,范围
0—configMAX_PRIORITIES-1
,在FreeRTOSConfig.h
文件中configMAX_PRIORITIES
宏定义为32。 - pxCreatedTask:任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任务句柄。其他API函数可能会使用到这个句柄。
返回值:
- pdPASS:任务创建成功。pdPASS宏定义为1
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY
:任务创建失败,因为堆内存不足!
在创建一个任务时一般都会在程序开头都有这三个宏定义

要指定任务的优先级、任务的栈大小,以及任务的句柄。
优先级很好理解,它决定了多个任务之间执行任务的先后顺序,任务的栈大小也很理解,在创建任务时,任务的局部变量以及任务切换时的数据都保存在栈里面。那么任务句柄是怎么一回事,它保存的是任务控制块的首地址。那么它又是如何来保存任务的首地址呢?这就是我们今天要讨论的话题。

创建任务是时传入的是一个指针?
是一个指针吗?
不是,是一个指针的指针。
为什么要传入指针的指针?
什么是指针的指针?
这些问题都需要搞明白你才能解决这个问题?
二、二级指针
正好前两天在公众号看到了这样一篇文章,里面有一道C语言的题可以引用来解释我们今天的问题,我们一起来看一下

上面这个代码有好几处错误,它的目的很简单,就是想把字符串hello world
拷贝给str
,但是它能拷贝成功吗?

很显然是不可以的。
为了使大家看的更清楚,代码简单修改一下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemory(char *p)
{
p = (char *) malloc(100);
strcpy(p,"www.zhiguoxin.cn");
printf("*p:%s &(*p):0x%x\r\n",p,&p);
}
int main()
{
char *str="www.baidu.cn";
getmemory(str);
printf("str:%s &str:0x%x\r\n",str,&str);
free(str);
return 0;
}
按照我们一般人的的想法,结果应该是:
p :www.zhiguoxin.cn &p :xxxxxxx
str:www.zhiguoxin.cn &str:xxxxxxx
但是实际上结果是多少?

完全没有变化,为了彻底解决这个问题,画了一个图,希望大家能够看的更加清楚一点。

从这里可以看出来,在分配内存后,str
与p
就分道扬镳了,而str也还是指向www.baidu.cn
。
如何修改呢?正确的是啥样的?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemory(char **p)
{
*p = (char *) malloc(100);
strcpy(*p,"www.zhiguoxin.cn");
printf("*p:%s &(*p):0x%x\r\n",*p,&(*p));
}
int main()
{
char *str="www.baidu.cn";
getmemory(&str);
printf("str:%s &str:0x%x\r\n",str,&str);
free(str);
return 0;
}
编译运行,发现没问题。

达到了我们想要的目的,字符串也得到了正常的拷贝。
如何解释?
函数中参数都是传值,传指针本质上也是传值,只不过它的值是指针类型罢了。如果想要改变入参内容,则需要传该入参的地址,通过解引用修改其指向的内容。
这里的str
的值就是*p
的值,是多少?它们都是一个指针,就是保存的是一个地址,地址是多少?地址就是使用动态分配内存malloc函数分配的100字节的首地址。然后又使用strcpy()
函数将hello world
拷贝到*p
里面。
这里面就涉及到了二级指针,首先str
毫无疑问是一个指针变量对吧?那么&str
是啥?理所当然就是一个指针的指针吧,就是地址的地址。
所以,我如果在某个地方申请了一块内存,如果想得到这块内存的首地址,而此时我们又定义了一个指针变量,想让这个指针来保存我们申请内存你的首地址,就必须要传入这个指针的地址,即指针的指针(二级指针)而不是传入这个指针。
至于原因上面的例子已经非常清楚的讲解了原因。
下面接着回到我们最开始的创建函数的任务句柄。在开始之前我们再把上面的函数封装一下。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char* TaskHandle_t;
void getmemory(TaskHandle_t *p)
{
*p = (char *) malloc(100);
strcpy(*p,"www.zhiguoxin.cn");
printf("*p:%s &(*p):0x%x\r\n",*p,&(*p));
}
int main()
{
TaskHandle_t str;
getmemory(&str);
printf("str:%s &str:0x%x\r\n",str,&str);
free(str);
return 0;
}
没啥大不了的,就是就是给char*
起了一个别名而已,让下面的代码看起来更加顺畅一写。

这样对比一下是不是很清楚了呢?这样一来我们创建任务时候这个任务句柄就保存的是我们TCB控制块这个结构体的首地址了,知道了一个任务的TCB控制块首地址的话,那么这个任务的所有信息我是不是都知道了。是的,就是这么奇妙。通过指针的指针,二级指针来转换一下。
好了,今天的内容就分享到这里!
A person essentially lend a hand to make significantly articles I’d state.
That is the first time I frequented your website page and thus far?
I surprised with the analysis you made to create this actual publish amazing.
Wonderful process!
my site … coupon
I know this if off topic but I’m looking into starting my
own blog and was curious what all is needed to get set up?
I’m assuming having a blog like yours would cost a pretty penny?
I’m not very web smart so I’m not 100% sure. Any recommendations
or advice would be greatly appreciated. Many
thanks
Here is my web blog; tracfone special coupon 2022
Usually posts some quite exciting stuff like this. If youre new to this site. Von Worlds
Im not positive where you are getting your information, but great topic. I needs to spend some time learning much more or working out more. Barrett Zakar
I really liked your blog post. Much thanks again. Cool. Lynn Montanez
I really like and appreciate your blog. Really looking forward to read more. Fantastic. Darrell Labonville
I cannot thank you enough for the blog post. Really looking forward to read more. Want more. Rubin Madere
I really like looking through a post that will make people think. Also, many thanks for allowing me to comment. Melvin Knobler
Say, you got a nice blog. Really looking forward to read more. Awesome. Rueben Yeah
Someone necessarily assist to make severely articles I might state. Brendan Ianuzzi
Hey this is kind of of off topic but I was wanting to know if blogs use WYSIWYG editors or if you have to manually code with HTML. Man Dennard
I liked up to you will receive carried out right here. Shad Kuntzman
You completed some fine points there. I did a search on the matter and found the majority of persons will agree with your blog. Antoine Ewelike
Pretty! This has been an incredibly wonderful post. Thank you for supplying this information. Carson Yehle
Your style is so unique in comparison to other people I have read stuff from. Hung Lege
Way cool! Some very valid points! I appreciate you penning this post plus the rest of the site is really good. Mauro Timberman
With imaginative crowdfunding, it has much more to do than simply that. Crowdfunding occurs on a genuine hidden need. Reed Meneal
What a data of un-ambiguity and preserveness of precious knowledge about unexpected feelings. Addia Shep Hooper
Good article. I am experiencing many of these issues as well.. Randall Alvirez
Really informative blog. Really thank you! Really Cool. Earle Puyear