Linux驱动之线程的使用——学习笔记(9)

211 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

还是由于写了个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 */