内核线程创建-kthread_create

  文章参考Linux内核线程kernel thread详解 - 知乎

大概意思就是早期创建内核线程,是交由内核处理,由内核自己完成(感觉好像也不太对呢),创建一个内核线程比较麻烦,会导致内核阻塞。因此就诞生了工作队列以及现在的kthreadd 2号进程。这样我们在创建内核线程时,只需要将消息告诉它们,实际进行内核线程创建的任务有kthreadd完成,感觉类似一个下半部。

我环境使用的是kthreadd进行内核线程的创建

内核线程创建kthread_create

kthread_create-->kthread_create_on_node-->__kthread_create_on_node

#define kthread_create(threadfn, data, namefmt, arg...) \kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)

 可以看到这里只是将创建内核线程的任务加入了链表里面,然后唤醒kthreadd进行内核线程的创建

struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),void *data, int node,const char namefmt[],va_list args)
{DECLARE_COMPLETION_ONSTACK(done);struct task_struct *task;struct kthread_create_info *create = kmalloc(sizeof(*create),GFP_KERNEL);if (!create)return ERR_PTR(-ENOMEM);/* 被创建的内核线程的信息被存放到了create_info里面 */create->threadfn = threadfn;create->data = data;create->node = node;create->done = &done;spin_lock(&kthread_create_lock);/* 将create_info加入到链表中,然后唤醒kthreadd_task(2号进程)进行后续的内核线程创建 */list_add_tail(&create->list, &kthread_create_list);spin_unlock(&kthread_create_lock);wake_up_process(kthreadd_task);/** Wait for completion in killable state, for I might be chosen by* the OOM killer while kthreadd is trying to allocate memory for* new kernel thread.*//* 这里是等待内核线程创建完成,内核线程创建完成后会释放这样完成量函数kthread里面会释放这个completion*/if (unlikely(wait_for_completion_killable(&done))) {/** If I was SIGKILLed before kthreadd (or new kernel thread)* calls complete(), leave the cleanup of this structure to* that thread.*/if (xchg(&create->done, NULL))return ERR_PTR(-EINTR);/** kthreadd (or new kernel thread) will call complete()* shortly.*/wait_for_completion(&done);}/* 函数kthread里面会将result赋值为创建好的内核线程的task_struct */task = create->result;if (!IS_ERR(task)) {static const struct sched_param param = { .sched_priority = 0 };char name[TASK_COMM_LEN];/** task is already visible to other tasks, so updating* COMM must be protected.*/vsnprintf(name, sizeof(name), namefmt, args);set_task_comm(task, name);//这里设置内核线程的名字/** root may have changed our (kthreadd's) priority or CPU mask.* The kernel thread should not inherit these properties.*/sched_setscheduler_nocheck(task, SCHED_NORMAL, &param);set_cpus_allowed_ptr(task, cpu_all_mask);}kfree(create);return task;
}

那2号进程kthreadd干了什么事情呢?

2号进程在rest_init里面创建,其处理函数为kthreadd

noinline void __ref rest_init(void)
{...............................pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);rcu_read_lock();kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);rcu_read_unlock();
............................
}

kthreadd-->create_kthread-->kernel_thread 

int kthreadd(void *unused)
{struct task_struct *tsk = current;/* Setup a clean context for our children to inherit. */set_task_comm(tsk, "kthreadd");ignore_signals(tsk);set_cpus_allowed_ptr(tsk, cpu_all_mask);set_mems_allowed(node_states[N_MEMORY]);current->flags |= PF_NOFREEZE;cgroup_init_kthreadd();/*其实就是一直检查kthread_create_list是否为空如果不为空,将不断的处理链表里面的任务处理,创建内核线程*/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,struct kthread_create_info, list);list_del_init(&create->list);spin_unlock(&kthread_create_lock);create_kthread(create);spin_lock(&kthread_create_lock);}spin_unlock(&kthread_create_lock);}return 0;
}

可以看到 内核线程的创建最终还是调用的kernel_thread。创建的内核线程会执行kthread,在函数kthread里面执行了我们设置的内核线程处理函数threadfun

static void create_kthread(struct kthread_create_info *create)
{int pid;#ifdef CONFIG_NUMAcurrent->pref_node_fork = create->node;
#endif/* We want our own signal handler (we take no signals by default). *//* 最终在kthread里面调用到我们设置的回调函数 */pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);if (pid < 0) {/* If user was SIGKILLed, I release the structure. */struct completion *done = xchg(&create->done, NULL);if (!done) {kfree(create);return;}create->result = ERR_PTR(pid);complete(done);}
}

kthread运行线程处理函数 

执行到这里,就算内核线程创建成功了.只不过它不会立即执行我们的threadfn(即创建内核线程时指定的函数),它会先释放completion,并让出cpu。这就是kthread_create后还需要wake_up_process的原因。

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);
}

经过实际验证确实是kthread调用了complete(done);,kthread_create才能返回,否则__kthread_create_on_node会一直等待completion

测试代码如下

起了个定时器,定时器里面唤醒了一个内核线程.内核线程里面做了两个事情,一个是将comp_block设置为true,即跳过complete(done),另外一个是创建一个内核线程,看看是否会阻塞

struct task_struct *task;
struct timer_list timer;
/* 通过该变量控制是否是否completion */
extern bool comp_block;int kill_thread(void* a)
{/* 不释放completion,然后再看看kthread_create是否会阻塞 */comp_block = true;printk(KERN_EMERG "\r\n before create thread\n");kthread_create(test_thread, NULL, "test_task");printk(KERN_EMERG "\r\n after create thread\n");return;
}
void timer_work(unsigned long data)
{wake_up_process(task);return;
}static int smsc911x_init(struct net_device *dev)
{
...............................printk(KERN_EMERG "\r\n softlockup simulate, in_interrupt %u in_softirq %u, NR_CPUS %d\n", in_interrupt(), in_softirq(), NR_CPUS);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");	task = kthread_create(kill_thread, NULL, "kill_task");printk(KERN_EMERG "\r\n create thread end\n");
....................................
}
bool comp_block = false;
static int kthread(void *_create)
{
.............................../* OK, tell user we're spawned, wait for stop or wakeup */__set_current_state(TASK_UNINTERRUPTIBLE);create->result = current;if (false == comp_block){complete(done);}schedule();
..........................................
}

效果展示 :可以看到并未打印kthread_create后面的log,并且内核线程kill_task也是一直无法退出

 

 如果定时器里面不设置comp_block的值,即正常释放completion,log如下

内核线程退出kthread_stop

kthread_stop:只是告诉内核线程应该退出了,但是要不要退出,还需要看内核线程处理函数是否检查该消息,并且检查到以后还必须主动退出。

1、设置内核线程为KTHREAD_SHOULD_STOP,当内核线程的处理函数用kthread_should_stop检查标记时,能感知到该事件(如果内核线程一直不检查,那么即使调用了kthread_stop也是没有用的)

2、重新唤醒内核线程,如何内核线程没有运行,那么也是无法感知到这个事件的

3、等待completion释放

int kthread_stop(struct task_struct *k)
{struct kthread *kthread;int ret;trace_sched_kthread_stop(k);get_task_struct(k);kthread = to_kthread(k);set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);kthread_unpark(k);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;
}

wait_for_completion(&kthread->exited); 

这个是在哪里释放的呢?

exited其实就是vfork_done,

static int kthread(void *_create)
{
........................................self->data = data;init_completion(&self->exited);init_completion(&self->parked);/* 此时的current就已经是我们创建好的内核线程了 */current->vfork_done = &self->exited;..............................do_exit(ret);
}

 那么vfork_done是在哪里释放的呢?

do_exit-->exit_mm-->exit_mm_release-->mm_release

static void mm_release(struct task_struct *tsk, struct mm_struct *mm)
{
.................................../** All done, finally we can wake up parent and return this mm to him.* Also kthread_stop() uses this completion for synchronization.*/if (tsk->vfork_done)complete_vfork_done(tsk);
}

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

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

相关文章

Linux 目录结构及其说明

Linux 操作系统遵循一种标准的目录结构&#xff0c;称为 Filesystem Hierarchy Standard&#xff08;文件系统层次结构标准&#xff09;&#xff0c;其定义了不同目录的用途和内容。 浅蓝色文字 /&#xff08;根目录&#xff09;&#xff1a; /根目录是整个文件系统的起点&…

力扣2807.在链表中插入最大公约数

思路&#xff1a;遍历链表&#xff0c;对于每一个结点求出它与下一个结点的最大公约数并插入到俩个结点之间 代码&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}…

【数据库系统概念】第7-14章集合

文章目录 第七章 数据库设计和E-R模型&#xff08;重点&#xff01;&#xff01;&#xff01;&#xff09;~~7.1 设计过程概览&#xff08;了解&#xff09;~~7.1.1 设计阶段7.1.2 设计选择 7.2 实体-联系模型&#xff08;重点掌握&#xff09;7.2.1 实体集7.2.2 联系集联系集的…

BLE Mesh蓝牙组网技术详细解析之Model Layer模型层(八)

目录 一、什么是BLE Mesh Model Layer模型层&#xff1f; 二、SIG Model 2.1 模型概念 2.2 消息格式 2.3 开关模型 四、资料获取 一、什么是BLE Mesh Model Layer模型层&#xff1f; Models Layer的作用是定义了一些通用的或特定的模型&#xff0c;用于实现网络节点设备…

Beauty algorithm(四)眼影

一、skills 前瞻 略 二、目标区域定位 1、 眼影区域 1、眼部关键点 左侧:36,37,38,39,40,41 右侧:42,43,44,45,46,47 2、计算roi区域的w,h,center 目的调整mask的比列。 FaceRegion left_es, right_es; left_es.w = landmarks.at(39).x - landmarks.at(36).x; left_es.…

书生·浦语大模型实战营第一次课堂笔记

书生浦语大模型全链路开源体系。大模型是发展通用人工智能的重要途径,是人工通用人工智能的一个重要途径。书生浦语大模型覆盖轻量级、重量级、重量级的三种不同大小模型,可用于智能客服、个人助手等领域。还介绍了书生浦语大模型的性能在多个数据集上全面超过了相似量级或相近…

并发(3)

目录 11.Synchronized本质上是通过什么保证线程安全的&#xff1f; 12.Synchronized使得同时只有一个线程可以执行&#xff0c;性能比较差&#xff0c;有什么提升的方法&#xff1f; 13.Synchronized由什么样的缺陷&#xff1f;Java Lock是怎么弥补这些缺陷的&#xff1f; 1…

看了致远OA的表单设计后的思考

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/n…

通信原理期末复习——计算大题(一)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

交换机04_远程连接

通过远程管理方式连接交换机 1、telnet简介 telnet 是应用层协议 基于传输层TCP协议的&#xff0c;默认端口&#xff1a;23 采用的是明文密码方式 不是很安全&#xff0c;一般用于内网管理。 2、ssh协议简介 ssh 是应用层的协议&#xff0c;基于传输层的TCP协议&#x…

打造清晰的日志管理策略:如何在 NestJS 中集成 winston 高级日志系统

前言 在Web应用程序的开发过程中&#xff0c;日志管理是不可或缺的一部分。日志可以帮助我们了解应用程序的运行状态&#xff0c;监控系统行为&#xff0c;以及在出现问题时快速定位和解决问题。 对于使用NestJS框架的项目来说&#xff0c;集成一个高效、可扩展的日志系统尤为…

面试官:说说接口和抽象类有什么区别

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

Python中的cls语法

在Python中&#xff0c;cls 是一个用于指代类本身的约定性名称&#xff0c;通常用作类方法&#xff08;class method&#xff09;中的第一个参数。cls 类似于 self&#xff0c;它是对类的引用&#xff0c;而不是对实例的引用。cls 通常在类方法中用于访问类级别的属性和方法。举…

LLM Agent之数据分析领域的应用

数据分析&#xff1a;Data-Copilot paper: Data-Copilot: Bridging Billions of Data and Humans with Autonomous Workflow github: https://github.com/zwq2018/Data-Copilot 先介绍下浙大提出的已扩展的数据分析框架&#xff0c;支持多种金融数据类型的查询&#xff0c;数…

论文笔记 Understanding Electricity-Theft Behavior via Multi-Source Data

WWW 2020 oral 1 INTRO 1.1 背景 1.1.1 窃电 窃电&#xff08;electricity theft&#xff09;指用户为了逃避电费而进行非法操作的一种行为 常用的反窃电方法可分为两类&#xff1a; 基于硬件驱动的反窃电方法 ​​​​​​​电表开盖检测、集中器检测。。。。 硬件驱动的…

MySQL之视图案例

目录 一.视图1.1 含义1.2 操作 二.案例三.思维导图 一.视图 1.1 含义 虚拟表&#xff0c;和普通表一样使用 1.2 操作 1.创建视图 create view 视图名 as 查询语句&#xff1b; 2.视图的修改 方式一&#xff1a; create or replace view 视图名 as 查询语句 方式二&#x…

图像分割-Grabcut法(C#)

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 本文的VB版本请访问&#xff1a;图像分割-Grabcut法-CSDN博客 GrabCut是一种基于图像分割的技术&#xff0c;它可以用于将图像中的…

Linux第15步_安装FTP客户端

安装完FTP服务器后&#xff0c;还需要安装FTP客户端&#xff0c;才可以实现Ubuntu系统和Windows系统进行文件互传。 1、在STM32MP157开发板A盘基础资料\03软件中&#xff0c;找到“FileZilla_3.51.0_win64-setup.exe”&#xff0c;双击它&#xff0c;就可以安装。 2、点击“I …

Fontfabric:一款字体与设计的完美结合

一、产品介绍 Fontfabric是一款由国际字体设计公司Fontfabric开发的字体设计软件。它提供了一整套完整的字体设计工具&#xff0c;让用户可以轻松地创建、设计和定制自己的字体。Fontfabric拥有丰富的字体库&#xff0c;包括各种风格和类型&#xff0c;能够满足用户在不同场景…

安全加密基础—基本概念、keytool、openssl

安全加密基础—基本概念、keytool、openssl 目录 前言 一、概念 明文通信 无密钥密文通信 对称加密 非对称加密 数字签名 消息摘要(MD5) CA数字证书(解决公钥分发的问题) HTTPS 相关文件扩展名 常用后缀名 普通的pem文件内容 二、keytool 2.1常用的命令如下 2…