本文已参与「新人创作礼」活动,一起开启掘金创作之路。
还是由于写了个RTC驱动,想要在驱动中定时更新系统时钟的原因,研究了定时器,结果发现定时器是通过中断实现的,而读写寄存器的过程中存在休眠函数,所以会导致卡死。然后就来研究一下线程的使用了。
一、知识点引入
内核线程在源码中的位置
- kernel\include\linux\kthread.h
- kernel\kernel\kthread.c
- kernel\include\linux\sched.h
1、结构体 struct task_struct 说明
结构体定义的路径:kernel\include\linux\sched.h 这是个线程任务结构体,如果一般是用的话不太需要关心里面成员的信息,内容太多了就不贴出来了。
2、函数说明
(1)创建线程 kthread_create
这是个宏,定义在 kernel\include\linux\kthread.h
#define kthread_create(threadfn, data, namefmt, arg...) \
kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
可以看出其实调用的是 kthread_create_on_node 函数,定义在kernel\kernel\kthread.c 参数: 依次传入 任务函数指针、数据、在哪个CPU上运行(-1则不指定)、线程名、参数。 返回: struct task_struct 结构体指针。通常需要使用 IS_ERR 判断一下指针是否有错误。
(2)唤醒/启动线程 wake_up_process
这个函数实现在 kernel\kernel\sched\core.c 使用 kthread_create 创建一个线程后该线程并不会立马启动,而是需要在调用 wake_up_process 函数之后才会启动。 参数: 传入 struct task_struct 结构体指针。 返回: 返回 1 说明被唤醒了,返回 0 说明已经在运行了。
/**
* wake_up_process - Wake up a specific process
* @p: The process to be woken up.
*
* Attempt to wake up the nominated process and move it to the set of runnable
* processes.
*
* Return: 1 if the process was woken up, 0 if it was already running.
*
* It may be assumed that this function implies a write memory barrier before
* changing the task state if and only if any tasks are woken up.
*/
int wake_up_process(struct task_struct *p)
{
WARN_ON(task_is_stopped_or_traced(p));
return try_to_wake_up(p, TASK_NORMAL, 0);
}
EXPORT_SYMBOL(wake_up_process);
(3)终止线程 kthread_stop
这个函数实现在 kernel\kernel\kthread.c 终止通过 kthread_create 创建的线程。
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit. This can also be called after kthread_create()
* instead of calling wake_up_process(): the thread will exit without
* calling threadfn().
*
* If threadfn() may call do_exit() itself, the caller must ensure
* task_struct can't go away.
*
* Returns the result of threadfn(), or %-EINTR if wake_up_process()
* was never called.
*/
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread;
int ret;
trace_sched_kthread_stop(k);
get_task_struct(k);
kthread = to_live_kthread(k);
if (kthread) {
set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
__kthread_unpark(k, kthread);
wake_up_process(k);
wait_for_completion(&kthread->exited);
}
ret = k->exit_code;
put_task_struct(k);
trace_sched_kthread_stop_ret(ret);
return ret;
}
EXPORT_SYMBOL(kthread_stop);
(4)判断线程是否应该被终止 kthread_should_stop
这个函数实现在 kernel\kernel\kthread.c 然后通过调用 test_bit 函数去判断当前线程的 flag 标志。 返回: 返回 0 则不终止,否则终止。
/**
* kthread_should_stop - should this kthread return now?
*
* When someone calls kthread_stop() on your kthread, it will be woken
* and this will return true. You should then return, and your return
* value will be passed through to kthread_stop().
*/
bool kthread_should_stop(void)
{
return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
}
二、出现的问题
1、线程无法终止
如果在线程中通过while或者for进行死循环操作,那么仅仅只是调用 kthread_stop 函数线程是不会终止的,也就是如果驱动被卸载了,但是线程依然在跑无法退出。 那么就要在线程的循环中调用 kthread_should_stop 函数去判断这个线程是否应该要被终止了。
if (kthread_should_stop()) {
break;
}
三、测试代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
/*************************************************************************************************/
// 局部宏定义
/*************************************************************************************************/
#define EN_DEBUG 1 /* 调试信息开关 */
#if EN_DEBUG
#define PRINT(x...) printk(KERN_EMERG x) /* 提高打印等级 */
#else
#define PRINT(x...)
#endif
#define KTHREAD_PERIOD 1 /* 线程周期,秒 */
#define UPDATE_PERIOD (1 * 10) /* 定时更新周期,秒 */
/*************************************************************************************************/
// 局部变量
/*************************************************************************************************/
static struct task_struct *s_kthread; /* 线程结构体 */
/**************************************************************************************************
** 函数名称: kthread_task
** 功能描述: 线程任务函数
** 输入参数: 无
** 输出参数: 无
** 返回参数: 无
**************************************************************************************************/
static int kthread_task(void *data)
{
int count;
count = 0;
while (1) {
if (kthread_should_stop()) {
break;
}
ssleep(KTHREAD_PERIOD);
if (count < UPDATE_PERIOD) {
count++;
PRINT("[KERNEL]:%s, count = %d\n", __FUNCTION__, count);
continue;
}
PRINT("[KERNEL]:%s, count = %d\n", __FUNCTION__, count);
PRINT("[KERNEL]:%s, do something\n", __FUNCTION__);
count = 0;
}
return 0;
}
/**************************************************************************************************
** 函数名称: kthread_init
** 功能描述: 线程初始化
** 输入参数: 无
** 输出参数: 无
** 返回参数: 无
**************************************************************************************************/
static void kthread_init(void)
{
s_kthread = kthread_create(kthread_task, NULL, "s_kthread");
if (IS_ERR(s_kthread)) {
PRINT("[KERNEL]:%s, create kthread fail !\n", __FUNCTION__);
return;
}
wake_up_process(s_kthread);
}
/**************************************************************************************************
** 函数名称: drv_init
** 功能: 驱动初始化函数,在加载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
static int __init drv_init(void)
{
PRINT("[KERNEL]:%s ------ \n", __FUNCTION__);
kthread_init();
return 0;
}
/**************************************************************************************************
** 函数名称: drv_exit
** 功能描述: 驱动退出函数,在卸载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
static void __exit drv_exit(void)
{
PRINT("[KERNEL]:%s ------ \n", __FUNCTION__);
kthread_stop(s_kthread);
}
module_init(drv_init); /* 模块初始化 */
module_exit(drv_exit); /* 模块卸载 */
MODULE_AUTHOR("hrx"); /* 模块作者 */
MODULE_DESCRIPTION("Linux Driver"); /* 模块描述 */
MODULE_VERSION("1.0.0"); /* 模块版本 */
MODULE_LICENSE("GPL"); /* 模块遵守的License */