linux内核 kthread_run函数 理解学习

最近发现在内核创建线程的时候经常会用到kthread_run()这样的一个调用。于是准备拿出来学习一下。首先看看它的定义之处才发现它是一个宏函数,而不是一个真正意义上的函数。在include/linux/Kthread.h里有/*** kthread_run - create and wake a thread.* @threadfn: the function to run until signal_pending(current).* @data: data ptr for @threadfn.* @namefmt: printf-style name for the thread.** Description: Convenient wrapper for kthread_create() followed by* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).*/#define kthread_run(threadfn, data, namefmt, ...) /({ /struct task_struct *__k / = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); /if (!IS_ERR(__k)) / wake_up_process(__k); /__k; /})这个函数的英文注释里很明确的说明: 创建并启动一个内核线程。可见这里的函数kthread_create()只是创建了内核线程,而最后启动是怎么启动的呢,我们看到了后面的wake_up_process()函数,没错就是这个函数启动了这个线程,让它在一开始就一直运行下去。知道遇见kthread_should_stop函数或者kthread_stop()函数。那我们具体看看前一个函数到底做了什么吧。在这个宏里面主要是调用了函数:kthread_create()这个函数是干什么的呢?在Kernel/Kthread.c里面我们可以看到:/*** kthread_create - create a kthread.* @threadfn: the function to run until signal_pending(current).* @data: data ptr for @threadfn.* @namefmt: printf-style name for the thread.** Description: This helper function creates and names a kernel* thread. The thread will be stopped: use wake_up_process() to start* it. See also kthread_run(), kthread_create_on_cpu().** When woken, the thread will run @threadfn() with @data as its* argument. @threadfn can either call do_exit() directly if it is a* standalone thread for which noone will call kthread_stop(), or* return when 'kthread_should_stop()' is true (which means* kthread_stop() has been called). The return value should be zero* or a negative error number; it will be passed to kthread_stop().** Returns a task_struct or ERR_PTR(-ENOMEM).*/struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...){struct kthread_create_info create;DECLARE_WORK(work, keventd_create_kthread, &create);create.threadfn = threadfn;create.data = data;init_completion(&create.started);init_completion(&create.done);/** The workqueue needs to start up first:*/if (!helper_wq) work.func(work.data);else { queue_work(helper_wq, &work); wait_for_completion(&create.done);}if (!IS_ERR(create.result)) { va_list args; va_start(args, namefmt); vsnprintf(create.result->comm, sizeof(create.result->comm), namefmt, args); va_end(args);}return create.result;}EXPORT_SYMBOL(kthread_create);注意到上面的这段英文解释:说这个函数会创建一个名为namefmt的内核线程,这个线程刚创建时不会马上执行,要等到它将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。我们看到creat结构体,我们将传入的参数付给了它,而threadfn这个函数就是创建的运行函数。在使用中我们可以在此函数中调用kthread_should_stop()或者kthread_stop()函数来结束线程。这里我们看到创建线程函数中使用工作队列DECLARE_WORK,我们跟踪一下发现这只是将函数#define DECLARE_WORK(n, f, d) /struct work_struct n = __WORK_INITIALIZER(n, f, d)然后再跟进:#define __WORK_INITIALIZER(n, f, d) { /.entry = { &(n).entry, &(n).entry }, /.func = (f), /.data = (d), /.timer = TIMER_INITIALIZER(NULL, 0, 0), /}反正目的是创建一个工作组队列,而其中keventd_create_kthread()函数主要是起到创建线程的功能/* We are keventd: create a thread. */static void keventd_create_kthread(void *_create){struct kthread_create_info *create = _create;int pid;/* We want our own signal handler (we take no signals by default). */pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);if (pid < 0) { create->result = ERR_PTR(pid);} else { wait_for_completion(&create->started); read_lock(&tasklist_lock); create->result = find_task_by_pid(pid); read_unlock(&tasklist_lock);}complete(&create->done);}再看看kernel_thread()函数最后调用到了哪里:/** Create a kernel thread.*/pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags){struct pt_regs regs;long pid;memset(®s, 0, sizeof(regs));regs.ARM_r1 = (unsigned long)arg;regs.ARM_r2 = (unsigned long)fn;regs.ARM_r3 = (unsigned long)do_exit;regs.ARM_pc = (unsigned long)kernel_thread_helper;regs.ARM_cpsr = SVC_MODE;pid = do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);MARK(kernel_thread_create, "%ld %p", pid, fn);return pid;}EXPORT_SYMBOL(kernel_thread);好,最后我们看到了线程通过申请进程的pid号来被创建,关键是我们要知道如何使用这个宏函数,也就是如何应用它。要注意的是它调用了创建线程函数,同时也激活了线程。所以代码中调用了它的话就隐含着已经启动一个线程。
Linux 内核编程中,`kthread_run` 是一个常用的宏定义,用于创建并立即启动一个内核线程。它内部调用了 `kthread_create` 来创建线程,并通过 `wake_up_process` 启动该线程[^3]。然而,`kthread_run` 本身并不支持直接设置线程的优先级和调度策略。 如果需要创建内核线程并设置其优先级和调度策略,可以使用 `kthread_create` 并结合 `sched_setscheduler` 函数来实现。这种方式提供了更大的灵活性,允许开发者自定义线程的调度属性。 ### 使用 `kthread_create` 创建线程 ```c struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...); ``` 此函数仅创建内核线程但不会立即运行它。要启动线程,必须手动调用 `wake_up_process()` [^5]。 ### 设置线程优先级和调度策略 一旦线程被创建,可以通过 `sched_setscheduler` 函数来更改其调度策略和优先级: ```c int sched_setscheduler(struct task_struct *p, int policy, const struct sched_param *param); ``` 其中 `policy` 可以是以下几种调度策略之一: - `SCHED_NORMAL`: 普通进程调度策略(默认) - `SCHED_FIFO`: 实时 FIFO 调度策略 - `SCHED_RR`: 实时轮转调度策略 `struct sched_param` 结构体包含了一个字段 `sched_priority`,用来指定线程的静态优先级(数值范围通常为 1 到 99): ```c struct sched_param { int sched_priority; }; ``` ### 示例代码 下面是一个完整的示例,展示如何使用 `kthread_create` 创建内核线程,并通过 `sched_setscheduler` 设置其优先级和调度策略: ```c #include <linux/kthread.h> #include <linux/sched.h> static struct task_struct *my_thread; static int my_thread_fn(void *data) { while (!kthread_should_stop()) { // 线程逻辑处理 schedule_timeout_interruptible(msecs_to_jiffies(1000)); // 睡眠一秒 } return 0; } // 创建线程并设置优先级 my_thread = kthread_create(my_thread_fn, NULL, "my_kthread"); if (my_thread) { struct sched_param param = { .sched_priority = 50 }; // 设置优先级为50 sched_setscheduler(my_thread, SCHED_FIFO, &param); // 设置调度策略为SCHED_FIFO wake_up_process(my_thread); // 启动线程 } ``` 上述代码首先使用 `kthread_create` 创建了一个内核线程,然后定义了调度参数结构体 `sched_param` 并设置了优先级为 50,接着通过 `sched_setscheduler` 将调度策略设为 `SCHED_FIFO`(实时 FIFO),最后调用 `wake_up_process` 启动线程。 这种方法不仅替代了 `kthread_run` 的功能,还增加了对线程优先级和调度策略的支持,适用于需要更精细控制内核线程行为的场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值