タスクハンドルとは何か?
FreeRTOSを扱う際、タスクの作成には「タスクハンドル」が必要です。このハンドルは、タスクの制御情報を保持する「タスク制御ブロック(TCB)」へのポインタです。タスク作成APIの引数として、このハンドルを渡すことで、作成されたタスクの参照を取得できます。
タスクの動的生成例
以下は、FreeRTOSでタスクを動的に生成するコード例です。
#define TASK_PRIORITY 2
#define STACK_SIZE 128
typedef void* TaskHandle;
TaskHandle taskHandleInstance;
xTaskCreate(
taskFunction, // タスク関数
"worker_task", // タスク名
STACK_SIZE, // スタックサイズ
NULL, // 引数
TASK_PRIORITY, // 優先度
&taskHandleInstance // タスクハンドルのポインタ
);
// タスク関数
void taskFunction(void *arg) {
while (1) {
vTaskDelay(1000);
}
}
なぜ二重ポインタが必要なのか
タスク作成関数の最後の引数はTaskHandle*型のポインタです。つまり、この関数に渡すのは「ポインタのポインタ」、すなわち「二重ポインタ」です。
これは、関数内でハンドル変数の値を変更できるようにするためです。C言語では関数の引数は値渡しのため、関数内でポインタ変数自体の値(アドレス)を変更するには、その変数のアドレスを渡す必要があります。
ポインタと二重ポインタの違いを確認する例
以下のコードは、関数内でメモリを割り当て、そのアドレスを呼び出し元に返す処理の誤りと修正を示しています。
誤りの例
void allocateMemory(char *ptr) {
ptr = malloc(100);
strcpy(ptr, "example");
}
int main() {
char *str = NULL;
allocateMemory(str);
// strは依然としてNULL
}
正しい例(二重ポインタ使用)
void allocateMemory(char **ptr) {
*ptr = malloc(100);
strcpy(*ptr, "example");
}
int main() {
char *str = NULL;
allocateMemory(&str);
// strはmallocされたメモリ領域を指す
}
タスクハンドルに応用
上記の例と同じように、FreeRTOSのxTaskCreate()関数では、タスクのTCB構造体の先頭アドレスを関数内で設定するために、ハンドル変数のアドレス(&taskHandleInstance)を渡しています。
これにより、タスク作成後にそのハンドルを使ってタスクを操作(一時停止・再開・削除など)することが可能になります。