关于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_COUNTprintk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#elseprintk(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_COUNTprintk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#elseprintk(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_COUNTprintk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#elseprintk(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_COUNTprintk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#elseprintk(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,用于检查是否应该主动退出,否则线程是不会主动退出的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/234402.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中的IO流

在Java中,I/O(输入/输出)流用于处理与输入和输出相关的操作。Java的I/O流按照数据处理的不同方式分为两大类:字节流和字符流。每个类别又分为输入流和输出流。以下是Java中常用的I/O流及其继承关系: 字节流&#xff0…

批量剪辑方法:掌握视频剪辑技巧,按指定时长轻松分割视频

在视频制作和编辑过程中,经常要批量处理和剪辑大量的视频片段。学会批量剪辑方法可以提高工作效率,还可以使视频编辑更加准确和高效。下面来看下云炫AI智剪如何按指定时长轻松分割视频的批量剪辑方法。 分割后的视频文件效果,已分割分段的视…

业界首款PCIe 4.0/5.0多通道融合接口SSD技术解读

之前小编写过一篇文章劝大家不要碰PCIe 5.0 SSD,详细内容,可以再回顾下: 扩展阅读:当下最好不要入坑PCIe 5.0 SSD 如果想要进一步了解PCIe 6.0,欢迎点击阅读: 浅析PCIe 6.0功能更新与实现的挑战 PCIe 6.…

云卷云舒:【实战篇】云主机/虚拟机迁移

1. 简介 用户原有业务通过不同版本型号、不同操作系统的主机承载,形式上包括物理服务器、虚拟机、公有云主机等。随着业务不断扩张,需要将其业务云化转型,必须保证上云过程数据完整,业务平滑过度。 如果将所有业务系统都重新部署…

教你如何将本地虚拟机变成服务器,供其它电脑访问

场景:最近在做数据仓库的作业,需要团队协作,买不起阿里云服务器,所以想到能不能将我本地机上的虚拟机变成服务器,供其它同学的电脑访问。在虚拟机上安装hadoop和hive,然后同学机子上安装kettle进行连接。最…

Java 反射(一)

反射 1.反射的介绍 1.反射机制允话程序在执行期间借助于Refelction API取得任何类的信息(比如成员变量,构造器,成员方法等)并能操作对象的属性及方法,反射在设计模式和框架底层都会用到 2.加载完类之后,在…

可狱可囚的爬虫系列课程 08:新闻数据爬取实战

前言 本篇文章中我带大家针对前面所学 Requests 和 BeautifulSoup4 进行一个实操检验。 相信大家平时或多或少都有看新闻的习惯,那么我们今天所要爬取的网站便是新闻类型的:中国新闻网,我们先来使用爬虫爬取一些具有明显规则或规律的信息&am…

【Redis-04】Redis命令在客户端与服务器之间的执行流程

Redis本质上是一个数据结构服务器,支持键值对类型存储的内存管理系统,可以用作数据库、缓存和消息中间件,在我日常的开发中,基本上使用redis作为缓存中间件。 在Redis中有两个重要的角色,一个是服务器server&#xff0…

Adding Conditional Control to Text-to-Image Diffusion Models——【论文笔记】

本文发表于ICCV2023 论文地址:ICCV 2023 Open Access Repository (thecvf.com) 官方实现代码:lllyasviel/ControlNet: Let us control diffusion models! (github.com) Abstract 论文提出了一种神经网络架构ControlNet,可以将空间条件控制添加到大型…

性能分析与调优: Linux 监测工具的数据来源

目录 一、实验 1.环境 2. proc目录 3. sys目录 4.netlink 5.tracepoint 6.kprobes 7. uprobes 二、问题 1.systemd如何查看启动时间 2.CentOS与Ubuntu如何安装bpftrace 3.snap有哪些常用的命令 4.snap如何安装store 5.如何列出使用bpftrace的OpenJDK USDT探针 一…

显示管理磁盘分区 fdisk

显示管理磁盘分区 fdisk fdisk是用于检查一个磁盘上分区信息最通用的命令。 fdisk可以显示分区信息及一些细节信息,比如文件系统类型等。 设备的名称通常是/dev/sda、/dev/sdb 等。 对于以前的设备有可能还存在设备名为 /dev/hd* (IDE)的设备,这个设…

回顾2023编程之旅

一、前言 看在给了我一个博客专家的份上就继续写写博客,实事求是的讲如果是工作之余去总结csdn写写技术博客,还想混个专家什么的,真的是精力不够。因为里面的灌水的实在太多,比不过的,写这个玩意必须得淡泊名利才能悠然…

【PostgreSQL在线创建索引(CIC)功能的锁分析以及使用注意】

前一篇文章提到了普通创建索引会阻塞DML操作 PostgreSQL创建索引的锁分析和使用注意 而PostgreSQL里可以使用create index concurrently 在线创建索引(CIC)功能,降低创建索引在表上申请的锁的级别,ShareUpdateExclusiveLock级别的锁和RowExclusiveLock…

烟花燃放如何管控?智能分析网关V4烟火检测保障烟火安全

一、方案背景 随着元旦佳节的热潮退去,春节也即将来临,在众多传统的中国节日里,烟花与烧纸祭祀都是必不可少的,一方面表达了人们对节日的庆祝的期许,另一方面也是一种对故者思念的寄托。烟花爆竹的燃放不仅存在着巨大的…

Git将本地项目上传到Gitee仓库

1.右键点击文件,点击Git Bash Here,进入git窗口 2.初始化本地仓库 git init3.将本地仓库与远程仓库建立连接 git remote add origin 远程仓库地址远程仓库地址在gitee仓库复制即可 4.将远程仓库的文件拉到本地仓库中 git pull origin master5.将本地文件全部上传…

多模态推荐系统综述:二、特征交互 Fusion

二、Fusion 融合不同的多模态信息,与bridge相比,融合更关注项目之间的多模态内部关系。 它可以灵活地融合不同权重和焦点的多模态信息。 注意机制是应用最为广泛的特征融合。 2.1 粗粒度注意力。 一些模型应用注意力机制在粗粒度级别融合来自多种模式…

使用openssl 生成pfx格式证书时报错:unable to load certificates

问题现象包如下: 之前在centos上使用openssl部署证书服务器以及颁发证书的时候遇到的问题,在进行个人证书生成之后需要形成pfx格式证书,结果过程中报错了。网上类似资料比较少,做个记录。 生成pfx格式证书的命令: o…

c++实现支持动态扩容的栈(stack)

1.在栈容量满时自动扩容: 支持自动扩容栈实现: // // myStack.hpp // algo_demo // // Created by Hacker X on 2024/1/9. //#ifndef myStack_hpp #define myStack_hpp #include <stdio.h> #include <string.h> //栈实现 //1.入栈 //2.出栈 //3.空栈 //4.满栈 …

git安装

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;…

Java后端开发——Mybatis实验

文章目录 Java后端开发——Mybatis实验一、MyBatis入门程序1.创建工程2.引入相关依赖3.数据库准备4.编写数据库连接信息配置文件5.创建POJO实体6.编写核心配置文件和映射文件 二、MyBatis案例&#xff1a;员工管理系统1.在mybatis数据库中创建employee表2.创建持久化类Employee…