• Author       : Toney
  • Email         : vip_13031075266@163.com
  • Date          : 2020.12.04
  • Copyright : 未经同意不得转载!!!
  • Version    : Linux-4.19.y
  • Referencehttps://www.linux.org/

目录

一、Linux的2号进程

二、kthreadd进程的创建

三、kthreadd进程执行体

四、create_kthread函数

五、小结


一、Linux的2号进程

说起Linux进程,学习Linux系统的大部分人都知道1号进程为init进程,人们就是这样只记得第一,却很少人记得第二。(经典问题:第一个宇航员是加加林,第二呢? 世界最高峰为珠穆朗玛峰,第二、第三、第四呢?…经典翻车问题)。下面直接看看2号进程:

UID         PID   PPID  C STIME TTY          TIME CMD

root          1      0  0 11月21 ?      00:00:17 /sbin/init splash

root          2      0  0 11月21 ?      00:00:00 [kthreadd]

root          3      2  0 11月21 ?      00:00:00 [rcu_gp]

root          4      2  0 11月21 ?      00:00:00 [rcu_par_gp]

root          6      2  0 11月21 ?      00:00:00 [kworker/0:0H-kb]

root         10      2  0 11月21 ?      00:00:15 [ksoftirqd/0]

root         24      2  0 11月21 ?      00:00:00 [khugepaged]

root         71      2  0 11月21 ?      00:00:00 [kblockd]

root         78      2  0 11月21 ?      00:00:00 [watchdogd]

root        138      2  0 11月21 ?      00:00:00 [kworker/u257:0]

root        151      2  0 11月21 ?      00:00:00 [charger_manager]

root        228      2  0 11月21 ?      00:00:01 [irq/16-vmwgfx]

root        229      2  0 11月21 ?      00:00:00 [scsi_tmf_7]

root        230      2  0 11月21 ?      00:00:00 [ttm_swap]

是的,kthreadd就是Linux的2号进程,这个进程在Linux内核中非常的重要,他是其他内核线程的父进程或者祖先进程(这个可以通过上面的PPID为2的进程可以看出,这些重要线程包括kworker、kblockd、khugepaged…),下面便慢慢来介绍下kthreadd进程。

二、kthreadd进程的创建

kthreadd进程是在内核初始化start_kernel()的最后rest_init()函数中,由0号进程(swapper进程)创建了两个进程:

  • init进程(PID = 1, PPID = 0
  • kthreadd进程(PID = 2, PPID = 0

内核中的其他线程PPID都是2, 说明这些线程都是由kthreadd进程创建的,因此可以说kthreadd进程负责内核线程的创建、维护等工作,是其他线程的基础。实际上也确实如此,kthreadd就是专门负责内核线程管理工作的。

static noinline void __ref rest_init(void)

{

struct task_struct *tsk;

int pid;

… …

rcu_scheduler_starting();

pid = kernel_thread(kernel_init, NULL, CLONE_FS);

… …

pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

… …

complete(&kthreadd_done);/*等待kthreadd创建完毕*/

}/*linux-4.19*/

由于Linux-2.6.12版本中rest_init函数中只创建了init进程,而kthreadd进程的创建我没有找到,因此这里引用了Linux-4.19版本中的rest_init函数部分代码。这个版本中清楚的显示了创建init进程和kthreadd进程。

创建完毕后,Linux系统需要借助ktheadd进程实现腾飞,因此在这里等待kthreadd进程创建完毕。

三、kthreadd进程执行体

在创建线程时,是需要传递线程执行函数的,从rest_init()中使用kernel_thread创建线程可知kthreadd线程执行体是kthreadd()函数。

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);/*允许kthreadd在任意cpu上执行*/
set_mems_allowed(node_states[N_MEMORY]); current->flags |= PF_NOFREEZE;
cgroup_init_kthreadd(); for (;;) {
set_current_state(TASK_INTERRUPTIBLE);/*将当前状态设置为可中断*/
if (list_empty(&kthread_create_list))/*如果没有线程需要创建,则主动出让cpu*/
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;
}

从上述代码中可以看出:kthreadd进程的任务就是等待创建线程,如果任务队列为空,则线程主动让出cpu(调用schedule后会让出cpu,本线程会睡眠):如果不为空,则依次从任务队列中取出任务,然后创建相应的线程。如此往复,直到永远…

四、create_kthread函数

create_kthread函数中会通过调用kernel_thread函数来创建新进程,且新进程的执行函数为kthread(所有经过kthreadd进程创建的进程执行体都为kthead, 看名字有点晕哈…)。

static void create_kthread(struct kthread_create_info *create)
{
int pid; #ifdef CONFIG_NUMA
current->pref_node_fork = create->node;
#endif
/* 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) {
/* 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);
}
}

kernel_thread接口刚才在rest_init接口中遇到过,内核就是通过kernel_thread接口创建的init进程和kthreadd进程。这里再次使用它创建新线程,新的线程执行体统一为kthead。下面我们看看kthread函数的内容:

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. */
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->vfork_done = &self->exited; /* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE);
create->result = current;
complete(done);
schedule();/*睡眠,一直。直到被唤醒*/ ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {/*唤醒后如果此线程不需要stop*/
cgroup_kthread_ready();
__kthread_parkme(self);
ret = threadfn(data);/*执行指定的函数体*/
}
do_exit(ret);
}

从kthread函数可以看出,新线程创建成功后,会一直睡眠(使用schedule主动让出CPU并睡眠),直到有人唤醒它(wake_up_process);线程被唤醒后,并且不需要stop, 则执行指定的函数体( threadfn(data) )。

五、小结

我使用一幅图来简单的描述下内核中kthreadd的工作流程:

上图中显示了内核创建线程的基本流程:

①某一个线程A(左上那个圈)调用kthread_create函数来创建新线程,调用后阻塞;kthread_create会将任务封装后添加到kthreadd监控的工作队列中;

②kthreadd进程检测到工作队列中有任务,则结束休眠状态,通过调用create_kthread函数创建线程,最后调用到kernel_thread --> do_fork来创建线程,且新线程执行体为kthead

③新线程创建成功后,执行kthead,kthreadd线程则继续睡眠等待创建新进程;

④线程A调用kthread_create返回后,在合适的时候通过wake_up_process(pid)来唤醒新创建的线程

⑤新创建的线程在kthead执行体中被唤醒,检测到是否需要stop,在不需要stop时,执行用户指定的线程执行体。(线程执行体发生了变化:先执行默认的kthead,然后才是用户指定的threadfn,当然也可能直接执行do_exit退出线程)

Linux内核学习之2号进程kthreadd的更多相关文章

  1. Linux内核学习笔记-2.进程管理

    原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  2. Linux内核学习笔记二——进程

    Linux内核学习笔记二——进程   一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...

  3. Linux内核学习笔记-1.简介和入门

    原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  4. Linux内核分析——Linux内核学习总结

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核学习总结 一 ...

  5. Linux内核学习总结(final)

    Linux内核学习总结 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ...

  6. (笔记)Linux内核学习(四)之系统调用

    一 用户空间和内核空间 Linux内核将这4G字节虚拟地址空间的空间分为两部分: l  将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”. l  ...

  7. Linux 内核学习的经典书籍及途径

    from:http://www.zhihu.com/question/19606660 知乎 Linux 内核学习的经典书籍及途径?修改 修改 写补充说明 举报   添加评论 分享 • 邀请回答   ...

  8. 关于Linux内核学习的误区以及相关书籍介绍

    http://www.hzlitai.com.cn/article/ARM9-article/system/1605.html 写给Linux内核新手-关于Linux内核学习的误区 先说句正经的:其实 ...

  9. linux内核学习之一:环境搭建--安装Debian7.3

    本系列文章假设读者已对linux有一定的了解,其实学习linux内核不需要有很深的关于linux的知识,只需要了解以下内容:linux基础知识及基本shell命令:现代操作系统的基本概念:C语言和gc ...

随机推荐

  1. thinkphp5 原生sql带分页方法

    1.先在顶部引入use think\paginator\driver\Page; 2.使用下例代码 $pageNumber = input('page')? input('page'):'0';//客 ...

  2. Docker限制

    前言 Docker系列文章: 此篇是Docker系列的第十篇,大家一定要按照我做的Demo都手敲一遍,印象会更加深刻的,马上就开始Kubernetes,加油!一起前行! 为什么要学习Docker Do ...

  3. 博客CSS样式 二

    预览 可自行更改颜色 背景图 页面定制 CSS 代码中加入: url为背景图地址,可下载心仪背景图后上传到博客园相册后获取地址 body { color: #000; background: url( ...

  4. SQL 练习15

    检索" 01 "课程分数小于 60,按分数降序排列的学生信息 SELECT Student.* ,SC.score from Student,SC WHERE sc.cid = ' ...

  5. Java实现WebSocket服务

    一.使用Tomcat提供的WebSocket库  Java可以使用Tomcat提供的WebSocket库接口实现WebSocket服务,代码编写也非常的简单.现在的H5联网游戏基本上都是使用WebSo ...

  6. Python实现发送邮件(实现单发/群发邮件验证码)

    Python smtplib 教程展示了如何使用 smtplib 模块在 Python 中发送电子邮件. 要发送电子邮件,我们使用 Python 开发服务器,Mailtrap 在线服务和共享的网络托管 ...

  7. virtualenv 创建python虚拟环境

    为什么要创建python虚拟环境 在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.4.所有第三方的包都会被pip安装到Python3的site-packages目录下. 如 ...

  8. C# prism 框架

    定义Region (RegionManager) 定义Region 的方式有两种,一个是在XMAL界面指定,另一种这是代码当中指定. RegionManager.RegionName(XMAL) Re ...

  9. uwp之图片旋转动画实现

    参考网址:https://blog.csdn.net/hzw2945/article/details/72467820 https://www.cnblogs.com/changbaishan/p/3 ...

  10. .net下Global.asax使用

    Global.asax 文件,有时候叫做 ASP.NET 应用程序文件,提供了一种在一个中心位置响应应用程序级或模块级事件的方法.你可以使用这个文件实现应用程序安全性以及其它一些任务.下面让我们详细看 ...