其他分享
首页 > 其他分享> > kernel_thread()和kthread_run()/kthread_create()的根本区别

kernel_thread()和kthread_run()/kthread_create()的根本区别

作者:互联网

0 本质区别

kthread_run()调用kthread_create(), kthread_create()加入链表后,有kthreadd()线程读取链表然后再调用kernel_thread()创建线程。
kernel_thread():实在真正的创建线程
kthread_run()/kthread_create() : 做特殊的准备工作,之后再调用kernel_thread()创建线程。

1. 函数的作用

首先要说明的是:这几个函数都是用来创建内核线程的。先看一下几个函数关系:
在这里插入图片描述
这里有两个长得很像的函数:create_kthread() 和 kthread_create()。(这不是长得像,根本就是一样的好吧,有点难记),这里只是函数封装的很像,但本质上还是kernel_thread() 和 **kthread_create()**这两个函数的区别。
从上面的函数调用便可以看出:
**kernel_thread()函数是通过调用do_fork()**函数创建的线程,而do_fork()则是在应用层fork(), vfork()和clone()函数的系统调用;此外还需要在其执行函数里调用daemonize()进行资源的释放;该线程挂接在init进程下。
kthread_create()函数是通过工作队列workqueue创建的线程,此线程挂在kthreadd线程下。
kthread_run()函数本质上是调用了kthread_create()和wake_up_process(), 就是除了挂在工作队列上后,便唤醒进行工作。
**kthread_create()**是比较推崇的创建内核线程的方式。
这几个函数在不同内核版本上有较大差别,请注意。

2. kernel_thread()

/* Create a kernel thread. *//*linux 2.6*/intkernel_thread(int (*fn)(void *), void * arg, unsigned long flags){struct pt_regs regs;memset(&regs, 0, sizeof(regs));/* Don't use r10 since that is set to 0 in copy_thread. */
	regs.r11 = (unsigned long) fn;
	regs.r12 = (unsigned long) arg;
	regs.erp = (unsigned long) kernel_thread_helper;
	regs.ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);/* Create the new process. */return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);}

在Linux2.6版本时该函数可以被驱动模块调用,因为被EXPORT_SYMBOL(kernel_thread);,但是在4.1版本没有没export,因此最好只用kthread_create()/kthread_run()来创建内核线程。
在Linux2.6版本时,非内核线程使用kernel_thread()必须在其执行函数里调用daemonize()释放资源:

#include <linux/kernel.h>#include <linux/module.h>static int Loop(void *dummy){   int i = 0;   daemonize("mythread");/*内核线程取的名字*/   while(i++ < 5) {   printk("current->mm = %p\n", current->mm);   printk("current->active_mm = %p\n", current->active_mm);   set_current_state(TASK_INTERRUPTIBLE);   schedule_timeout(10 * HZ);   }   return 0;}static __init int test_init(void){   kernel_thread(Loop, NULL, CLONE_KERNEL | SIGCHLD);   return 0;}static __exit int test_exit(void){   kernel_thread(Loop, NULL, CLONE_KERNEL | SIGCHLD);   return 0;}static void test_exit(void) {}module_init(test_init);module_exit(test_exit);

3. kthread_create()

kthread_create()函数创建的内核线程创建成功后是未被激活的,不能工作,如果需要工作,则需要使用wake_up_process()函数来唤醒。线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,本线程可以使用kthread_should_stop()来获取它其他线程kthread_stop()信号,从而实现温和的关闭方式。

/**
 * 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 val**ue 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;

	create.threadfn = threadfn;
	create.data = data;init_completion(&create.started);init_completion(&create.done);spin_lock(&kthread_create_lock);list_add_tail(&create.list, &kthread_create_list);wake_up_process(kthreadd_task);/*放到了工作队列中*/spin_unlock(&kthread_create_lock);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;}

4. kthread_run()

创建并唤醒该线程。 该函数基于kthread_create(),并且直接调用了wake_up_process()唤醒了该线程。因此使用kthread_run()函数创建的线程会直接开始工作。

/**
 * 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_run()创建线程:

#include <linux/module.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/delay.h>
 static int   thread_work(void *data){allow_signal(SIGTERM);current->state = TASK_INTERRUPTIBLE;
 
	printk("New kernel thread run\n");return 0;}
 static int __init test_init(void){/* Schedule the test thread */kthread_run (thread_work, NULL, "thread_1");
 return 0;}
 static void __exit test_exit(void){return;}
 MODULE_LICENSE("Dual BSD/GPL");module_init(test_init);module_exit(test_exit);

5. kthread_stop() / kthread_should_stop()

用来结束由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.  Your threadfn() must not call do_exit()
 * itself if you use this function!  This can also be called after
 * kthread_create() instead of calling wake_up_process(): the thread
 * will exit without calling threadfn().
 *
 * Returns the result of threadfn(), or %-EINTR if wake_up_process()
 * was never called.
 */int kthread_stop(struct task_struct *k){int ret;mutex_lock(&kthread_stop_lock);/* It could exit after stop_info.k set, but before wake_up_process. */get_task_struct(k);/* Must init completion *before* thread sees kthread_stop_info.k */init_completion(&kthread_stop_info.done);smp_wmb();/* Now set kthread_should_stop() to true, and wake it up. */
	kthread_stop_info.k = k;wake_up_process(k);put_task_struct(k);/* Once it dies, reset stop ptr, gather result and we're done. */wait_for_completion(&kthread_stop_info.done);
	kthread_stop_info.k = NULL;
	ret = kthread_stop_info.err;mutex_unlock(&kthread_stop_lock);return ret;}

**kthread_should_stop()**用来获取线程是否处于忙状态,如果是则返回true。之后再调用kthread_stop()完成线程的温和退出。
当然也可以直接调用kthread_stop()使线程退出。使用该函数时,线程的执行函数不得调用do_exit();

6. kthreadd() —(后续补充)

看完kthreadd()函数实现后感觉上面可能不是特别准确。。
首先这个函数kthreadd() 是上述工作队列的处理函数,从上述代码里可以看出**kthread_create()**创建线程的方法只是将工作放到工作队列中,之后实在这里做的后续处理:kthreadd是一个内核独立线程,是由do_fork()函数创建的。
kthreadd的创建:

static void noinline __init_refok rest_init(void)__releases(kernel_lock){int pid;kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);/*init线程*/numa_default_policy();
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);/*kthreadd线程*/
	kthreadd_task = find_task_by_pid(pid);unlock_kernel();/*
	 * The boot idle thread must execute schedule()
	 * at least one to get things moving:
	 */preempt_enable_no_resched();schedule();preempt_disable();/* Call into cpu_idle with preempt disabled */cpu_idle();}

kthreadd关于工作队列的处理流程:

int kthreadd(void *unused){/* Setup a clean context for our children to inherit. */kthreadd_setup();
	current->flags |= PF_NOFREEZE;for (;;) {set_current_state(TASK_INTERRUPTIBLE);if (list_empty(&kthread_create_list))schedule();__set_current_state(TASK_RUNNING);spin_lock(&kthread_create_lock);while (!list_empty(&kthread_create_list)) {/*如果链表内有内容*/struct kthread_create_info *create;/*取出链表节点信息*/
			create = list_entry(kthread_create_list.next, /*实现方式和container_of一样*/struct kthread_create_info, list);list_del_init(&create->list);spin_unlock(&kthread_create_lock);/*使用do_fork的方式创建线程*/create_kthread(create);/*do_fork方式*/spin_lock(&kthread_create_lock);}spin_unlock(&kthread_create_lock);}return 0;}

从这段代码可以看出,工作队列的处理方式是:如果工作队列有内容,取出任务,然后创建线程(最后调用do_fork()实现的)。因此可以得出结论:内核创建线程的方式和应用层最终调用的函数是相同的,都是do_fork(); 至于为什么不直接创建而使用工作队列进程创建还不清楚原因。

标签:kthread,kernel,run,thread,create,stop,线程
来源: https://blog.51cto.com/u_13291771/2707369