关于kthread_stop的疑问(linux3.16)

线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。

之前找销毁内核线程的接口时,发现了kthread_stop这个接口。网上说这个函数能够销毁一个内核线程。

最开始以为对于一个已经唤醒的内核线程,我们直接调用这个接口就能够让这个线程自动退出。后面经过试验发现并不是这样的======》kthread_stop只是通知线程退出,至于线程是否会退出,取决于线程的行为,即是否有kthread_should_stop动作,去检查,去主动退出

测试代码1

static struct task_struct *test_task;
struct task_struct *task;
struct timer_list timer;
int test_thread(void* a)
{
	printk(KERN_EMERG "\r\n thread start\n");
#ifdef CONFIG_PREEMPT_COUNT
	printk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#else
	printk(KERN_EMERG "\r\n not define CONFIG_PREEMPT_COUNT\n");
#endif	
	while (1)
    {
		if( kthread_should_stop())
		{
			printk(KERN_EMERG "\r\n exit while\n");
			break;
		}
		msleep(10000);	
	}
	printk(KERN_EMERG "\r\n thread end\n");
	return 0;
}
int kill_thread(void* a)
{
	if (NULL != test_task)
	{
		printk(KERN_EMERG "\r\n attempt kill test_thread\n");
		kthread_stop(test_task);
		printk(KERN_EMERG "\r\n kill test_thread complete\n");
	}
	return;
}
void timer_work(unsigned long data)
{
	wake_up_process(task);
	return;
}

static int smsc911x_init(struct net_device *dev)
{
................	
	timer.expires=jiffies+msecs_to_jiffies(20000);
	timer.function=timer_work;
	init_timer(&timer);
	add_timer(&timer);
	printk(KERN_EMERG "\r\n create thread\n");
	//spin_lock(&lockdep_test);
	test_task = kthread_create(test_thread, NULL, "test_task");
	wake_up_process(test_task); 
	task = kthread_create(kill_thread, NULL, "kill_task");
  	//kthread_stop(test_task);
	printk(KERN_EMERG "\r\n create thread end\n");
.....................
}

log如下:可以看到在上面的代码样例中(kthread_should_stop必须要有这个),确实能够让内核线程退出

测试代码2 

将内核线程的处理函数里面的kthread_should_stop去掉,其他保持不变。看看会发生什么

int test_thread(void* a)
{
	printk(KERN_EMERG "\r\n thread start\n");
#ifdef CONFIG_PREEMPT_COUNT
	printk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#else
	printk(KERN_EMERG "\r\n not define CONFIG_PREEMPT_COUNT\n");
#endif	
	while (1)
        {
		/*if( kthread_should_stop())
		{
			printk(KERN_EMERG "\r\n exit while\n");
			break;
		}*/
		msleep(10000);	
	}
	printk(KERN_EMERG "\r\n thread end\n");
	return 0;
}

log如下:可以看到。如果处理函数里面没有kthread_should_stop,即使通过kthread_stop通知内核线程应该退出了,但是内核线程不去检查是否应该退出,那么内核线程也是无法退出的。即kthread_stop是无法强制杀死内核线程的

测试 代码3

内核线程处理函数不要kthread_should_stop,在内核线程刚被创建并唤醒的时候,调用kthread_stop通知其退出。按照测试代码2,它应该不会主动退出的。但是实测这样是可以让内核线程退出的(甚至内核线程都没有被调度),这里没有想明白

static struct task_struct *test_task;
int test_thread(void* a)
{
	printk(KERN_EMERG "\r\n thread start\n");
#ifdef CONFIG_PREEMPT_COUNT
	printk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#else
	printk(KERN_EMERG "\r\n not define CONFIG_PREEMPT_COUNT\n");
#endif	
	while (1)
        {
		/*if( kthread_should_stop())
		{
			printk(KERN_EMERG "\r\n exit while\n");
			break;
		}*/
		msleep(10000);	
	}
	printk(KERN_EMERG "\r\n thread end\n");
	return 0;
}

static int smsc911x_init(struct net_device *dev)
{
........................	
	/*timer.expires=jiffies+msecs_to_jiffies(20000);
	timer.function=timer_work;
	init_timer(&timer);
	add_timer(&timer);*/
	printk(KERN_EMERG "\r\n create thread\n");
	//spin_lock(&lockdep_test);
	test_task = kthread_create(test_thread, NULL, "test_task");
	wake_up_process(test_task); 
	//task = kthread_create(kill_thread, NULL, "kill_task");
  	kthread_stop(test_task);
	printk(KERN_EMERG "\r\n create thread end\n");
......................................
}

log如下:可以看到内核线程打印的thread start,并且也搜到对应的内核线程

后面有时间研究一下这个是为什么

现在来解答为什么测试代码3能够让内核线程退出

static int kthread(void *_create)
{
	/* Copy data: it's on kthread's stack */
	struct kthread_create_info *create = _create;
	int (*threadfn)(void *data) = create->threadfn;
	void *data = create->data;
	struct completion *done;
	struct kthread *self;
	int ret;

	self = kzalloc(sizeof(*self), GFP_KERNEL);
	set_kthread_struct(self);

	/* If user was SIGKILLed, I release the structure. */
	/* 将create->done赋值为NULL,并返回create->done原来的值 */
	done = xchg(&create->done, NULL);
	if (!done) {
		kfree(create);
		do_exit(-EINTR);
	}

	if (!self) {
		create->result = ERR_PTR(-ENOMEM);
		complete(done);
		do_exit(-ENOMEM);
	}

	self->data = data;
	init_completion(&self->exited);
	init_completion(&self->parked);
	/* 此时的current就已经是我们创建好的内核线程了 */
	current->vfork_done = &self->exited;

	/* OK, tell user we're spawned, wait for stop or wakeup */
	__set_current_state(TASK_UNINTERRUPTIBLE);
	//__kthread_create_on_node里面将result当做返回值的原因在这里体现
	create->result = current;
	/* 在这里释放的completion,__kthread_create_on_node才会继续往下走 */
	complete(done);
	/*
	可以看到内核线程创建完了会先让出cpu,并不会立即执行我们的线程处理函数
	这就是我们为什么需要wake_up_process的原因,需要wake之后,才会继续从这里执行
	然后走到我们的threadfn
	*/
	schedule();

	ret = -EINTR;
	/*这个检查,我怀疑就是导致kthread_stop表现出不同行为的原因*/
	if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
		cgroup_kthread_ready();
		__kthread_parkme(self);
	    /* 执行内核线程设置的处理函数 */
		ret = threadfn(data);
	}
	/* 可以看到如果threadfn执行完了,内核线程退出是do_exit */
	do_exit(ret);
}

 个人认为这里就是wake_up_process后立即调用kthread_stop能让内核线程退出的原因(在函数kthread中)。

	/*这个检查,我怀疑就是导致kthread_stop表现出不同行为的原因*/
	if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
		cgroup_kthread_ready();
		__kthread_parkme(self);
	    /* 执行内核线程设置的处理函数 */
		ret = threadfn(data);
	}

1、在调用kthread_stop之后会设置 KTHREAD_SHOULD_STOP。如果设置了这个标记后,在走到这个地方,一定不会执行内核线程处理函数threadfn,所以也不会有我们的打印

那如何能保证一定是先设置标记,在执行内核线程呢?

1、创建好内核线程后会先让出cpu,所以内核线程想要运行必须要先被唤醒wake_up_process(其实这里没有搞明白,如果是就绪的内核线程,即使被换下cpu了,也可以被调度器再次调度。这里的内核线程是如何保证,在wake up前不会被调度呢?)

	/* 在这里释放的completion,__kthread_create_on_node才会继续往下走 */
	complete(done);
	/*
	可以看到内核线程创建完了会先让出cpu,并不会立即执行我们的线程处理函数
	这就是我们为什么需要wake_up_process的原因,需要wake之后,才会继续从这里执行
	然后走到我们的threadfn
	*/
	schedule();

2、我编译的内核是非抢占的,因此在wake_up_process和kthread_stop之间,如果没有主动让出cpu的行为,内核线程是没有机会得到执行的(那能不能拿到其他核上面执行呢??)。所以就能保证一定是先设置标记,在被调度。

基于第2点在进行实验:结论是有可能在其他核上执行的

测试代码如下

如果test_task不进行绑核,唤醒内核线程后空转5s中,内核线程是会被调度的。但是如果是下面经过绑核的代码,内核线程是无法被执行的。即使wake_up_process和kthread_stop之前空转了5s,

int test_thread(void* a)
{
	printk(KERN_EMERG "\r\n thread start\n");
#ifdef CONFIG_PREEMPT_COUNT
	printk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#else
	printk(KERN_EMERG "\r\n not define CONFIG_PREEMPT_COUNT\n");
#endif	
	while (1)
        {
		if( kthread_should_stop())
		{
			printk(KERN_EMERG "\r\n exit while\n");
			break;
		}
		msleep(10000);	
	}
	printk(KERN_EMERG "\r\n thread end\n");
	return 0;
}
static int smsc911x_init(struct net_device *dev)
{
............................
	test_task = kthread_create(test_thread, NULL, "test_task");
	kthread_bind(test_task, smp_processor_id());
	wake_up_process(test_task); 
	//task = kthread_create(kill_thread, NULL, "kill_task");
	mdelay(5000);
  	kthread_stop(test_task);
	printk(KERN_EMERG "\r\n create thread end\n");
.....................
}

 使用注意事项

1、在执行kthread_stop的时候,目标线程必须没有退出,否则会Oops。原因很容易理解,当目标线程退出的时候,其对应的task结构也变得无效,kthread_stop引用该无效task结构就会出错。

2、内核线程的处理函数里面需要有kthread_should_stop,用于检查是否应该主动退出,否则线程是不会主动退出的。

相关推荐

  1. 关于ARINC653疑问

    2024-01-09 19:30:02       7 阅读
  2. 关于DDD设计模式各种疑问:什么是DDD架构?

    2024-01-09 19:30:02       15 阅读
  3. kernelmodule目录名疑问

    2024-01-09 19:30:02       34 阅读
  4. 【React】常见疑问整理

    2024-01-09 19:30:02       32 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-09 19:30:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-09 19:30:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-09 19:30:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-09 19:30:02       18 阅读

热门阅读

  1. 【MySQL】ANY函数 的巧用(筛选字段 = ANY(语句))

    2024-01-09 19:30:02       39 阅读
  2. 逆流而上-摘抄句子

    2024-01-09 19:30:02       36 阅读
  3. Git命令 本地-远程 简洁步骤

    2024-01-09 19:30:02       31 阅读
  4. React-路由进阶

    2024-01-09 19:30:02       33 阅读