线程生命周期
线程是一个内核对象,应用程序通过线程可以处理一些中断服务函数不能处理的那些执行时间太长或者逻辑太复杂的任务相关概念
一个应用程序可以定义任意个线程,每个线程在派生的时候都会返回一个线程ID,应用程序可以通过线程ID来引用线程。一个线程有以下几个属性:①每个线程独自拥有的一块内存区域“栈”,这个栈的空间大小可以根据线程处理的任务需要指定
②内核用于记录线程私有元数据的线程控制块,它的结构体类型是 struct k_thread
③在线程启动时可注册一个入口点函数,这个函数有3个参数
④线程优先级,内核调度器会根据这个优先级为线程分配CPU时间
⑤一个线程配置选项集合,通过设置选项可以使线程在特定场景下执行特定的操作
⑥启动延时时间,这个时间指定的是线程在启动前内核需要等待的时间
创建线程
线程在使用前必须先创建。线程会初始化线程控制块以及栈的一部分,剩下的部分没有进行初始化如果线程的启动延时设置为K_NO_WAIT,那么内核会立即执行线程。另外,内核也可以指定一段时间后再执行线程。比如这样一种应用场景,如果一个外设硬件设备需要一段指定的时间后才可以稳定工作,那么就可以给线程指定一个延时时间,等待硬件稳定后再启动线程来操作硬件
如果一个线程设置了启动延时时间,那么在延时没被启动的这段时间里可以被取消执行。如果线程已经启动了,那么就无法取消了。如果一个线程被成功取消后需要被重新使用时,需要重新派生这个线程
同步终止线程
一个启动线程默认状态是永远执行。然而,也有可能在线程的入口点函数中通过返回方式同步终止线程。这就是线程的终止一个线程终止返回前需要释放所有已经占用的共享资源(比如说互斥锁和动态申请的内存),因为内核不会自动回收这些资源
note:目前内核关于应用程序是否可以派生一个已经被终止的线程没有做任何声明
异步中止线程
通过执行aborting可以异步中止一个线程,如果线程触发了一个致命错误内核会自动中止这个线程,比如定义一个空指针一个线程也会被另一个线程通过调用k_thread_abort()中止。然而,通过发送信号给线程让他自己同步终止自己是一个比较好的方式
如果执行一个线程中止操作,内核不会回收被中止线程占有的共享资源
note:目前内核关于应用程序是否可以派生一个已经被中止的线程没有做任何声明
线程挂起
一个运行的线程可通过进入挂起状态而停止运行(挂起时间长短不确定)。可以通过k_thread_suspend()接口挂起一个线程,包括调用线程自己。如果挂起一个已经挂起的线程不会产生任何影响一个线程一旦被挂起,如果其他线程不调用k_thread_resume()移除线程挂起状态,那么他就永远不会被重新调度
note:一个线程可以通过调用k_sleep()睡眠一段时间(时间长短可以指定)。然而,这个和线程挂起不同的是,当被挂起的时间到了指定的时间后就会自动变成可执行状态
线程可选配置项
内核支持一个可选设置集合项来设置一个线程在特定场景下执行特定的操作,这个可选的设置项是在线程派生时指定的如果线程的可选设置项设置为零表示没有任何设置请求。如果需要设置可选设置项可以通过相应的名字请求,而且多个请求可以通过 | 运算符组合
目前内核支持的可选配置项如下:
①K_ESSENTIAL
这个选项标签表示线程是一个很重要的线程,所以在出现一个致命系统错误时内核会终止(同步)或中止(异步)线程
②K_FP_REGS和K_SSE_REGS
在x86架构下这俩选项分别表示线程是否使用CPU的浮点运算寄存器和SSE寄存器。这个选项会告诉内核在调度线程的时候需要执行额外的操作来保存和恢复这些寄存器的内容
默认情况下,在调度的时候内核是不会尝试保存和恢复这些寄存器的内容的
线程的使用
派生一个线程
通过定义线程栈空间和线程控制块,然后调用k_thread_create()就可以派生出一个线程了。在定义栈空间是必须使用__stack属性确保栈空间正确对齐派生一个线程后会返回一个线程ID,然后就可以通过这个线程ID来应用这个线程了
下面是派生一个线程并立即启动线程的代码:
#define MY_STACK_SIZE 500
#define MY_PRIORITY 5
extern void my_entry_point(void *, void *, void *);
char __noinit __stack my_stack_area[MY_STACK_SIZE];
struct k_thread my_thread_data;
k_tid_t my_tid = k_thread_create(&my_thread_data, my_stack_area,
MY_STACK_SIZE, my_entry_point,
NULL, NULL, NULL,
MY_PRIORITY, 0, K_NO_WAIT);
另外,也可以通过调用K_THREAD_DEFINE在编译时静态分配一个线程,这个宏定义里定义了线程栈空间和线程控制块以及自动定义了一个线程ID
下面的代码和上面的一样,同样可以派生一个线程:
#define MY_STACK_SIZE 500
#define MY_PRIORITY 5
extern void my_entry_point(void *, void *, void *);
K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,
my_entry_point, NULL, NULL, NULL,
MY_PRIORITY, 0, K_NO_WAIT);
同步终止线程
一个线程通过在入口点函数中以返回的方式同步终止自己下面的代码展示了如何同步终止线程:
void my_entry_point(int unused1, int unused2, int unused3)
{
while (1) {
...
if (<some condition>) {
return; /* thread terminates from mid-entry point function */
}
...
}
/* thread terminates at end of entry point function */
}
推荐用法
①使用线程处理一些在中断服务函数中无法处理的任务(比如操作时间长或者逻辑复杂的任务)②使用分离线程来并行处理在逻辑上不同的处理操作
配置选项
没有相关的配置项APIs
下面的线程API在kernel.h头文件中定义K_THREAD_DEFINE
k_thread_create()
k_thread_cancel()
k_thread_abort()
k_thread_suspend()
k_thread_resume()