Linux内核中的init_task进程和idle进程

本文详细解析了Linux内核初始化过程中的关键步骤,包括BIOS代码执行、任务(进程)特性的引入以及init_task进程的特殊作用,阐述了init_task如何在无进程概念的情况下启动进程,并最终通过执行/sbin/init文件实现多任务系统的初步建立。
部署运行你感兴趣的模型镜像

当Power on PC时,BIOS的代码开始执行,然后是Linux初始化的代码,这其中大约很长一段时间Linux都没有进程这一概念,但是这不影响CPU执行它的二进制代码。如果不是多任务以及进程调度的需要,Linux内核可以一直这样走下去。
但是因为多任务的需求,Linux必须能支持任务这一特性,任务即进程,或者更简单地说由task_struct对象实例所代表的一段代码的集合,用以完成特定的任务。所以Linux内核初始化过程中必须为进程以及进程调度做准备。

init_task进程在Linux中属于一个比较特殊的进程,它是内核开发者人为制造出来的,而不是其他进程通过do_fork来完成。init_task对象的初始化在内核代码中由下面代码来完成:

<arch/x86/kernel/init_task.c>

  1. struct task_struct init_task = INIT_TASK(init_task);
如果仔细考察INIT_TASK宏的细节,会发现很多有趣的东西,比如inti_task所对应的内核栈,在INIT_TASK宏中由下列代码指定:
.stack        = &init_thread_info
可以猜想init_task进程的内核栈一定是通过静态方式分配的,事实上也的确如此:

<
arch/x86/kernel/init_task.c>
  1. union thread_union init_thread_union __init_task_data =
  2.     { INIT_THREAD_INFO(init_task) };
init_thread_info定义中的__init_task_data表明该内核栈所在的区域位于内核映像的init data区,我们可以通过编译完内核后所产生的System.map来看到该变量及其对应的逻辑地址:

root@build-server:/boot# cat System.map-3.1.6 | grep init_thread_union
ffffffff81a00000 D init_thread_union

这意味着init_task.stack = 0x
ffffffff81a00000.

Linux在无进程概念的情况下将一直从初始化部分的代码执行到start_kernel,然后再到其最后一个函数调用rest_init。

从rest_init开始,Linux开始产生进程,因为init_task是静态制造出来的,pid=0,它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中。在rest_init函数中,内核将通过下面的代码产生第一个真正的进程(pid=1):

<init/main.c>
  1. static noinline void __init_refok rest_init(void)
  2. {
  3.     ...
  4.     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
  5.     ...
  6.     cpu_idle();
  7. }
kernel_init 函数最有意思的地方在于 它会通过调用kernel_execve来执行根文件系统下的/sbin/init文件(所以此前系统根文件系统必须已经就绪),kernel_execve对用户空间程序/sbin/init的调用发起自int $0x80,这是个从内核空间发起的系统调用,与call_usermodehelper函数本质上是完全一样的。

而此时init_task的任务基本上已经完全结束了,它将沦落为一个idle task,事实上在更早前的sched_init()函数中,通过init_idle(current, smp_processor_id())函数的调用就已经把init_task初始化成了一个idle task,init_idle函数的第一个参数current就是&init_task,在init_idle中将会把init_task加入到cpu的运行队列中,这样当运行队列中没有别的就绪进程时,init_task(也就是idle task)将会被调用,它的核心是一个while(1)循环,在循环中它将会调用schedule函数以便在运行队列中有新进程加入时切换到该新进程上。

您可能感兴趣的与本文相关的镜像

ComfyUI

ComfyUI

AI应用
ComfyUI

ComfyUI是一款易于上手的工作流设计工具,具有以下特点:基于工作流节点设计,可视化工作流搭建,快速切换工作流,对显存占用小,速度快,支持多种插件,如ADetailer、Controlnet和AnimateDIFF等

Linux 内核中,`init_task` 是初始进程任务结构体实例化的一部分。它定义了一个静态初始化的 `task_struct` 对象,用于表示系统的第一个进程(即 PID 为 0 的 idle 进程)。以下是与 `init_task` 相关的一些重要头文件及其作用: ### 头文件及相关说明 #### 1. `<linux/sched.h>` 该头文件包含了任务调度的核心数据结构函数声明,其中包括 `struct task_struct` `init_task` 的定义[^1]。 ```c #include <linux/sched.h> ``` 此文件通常会引入其他必要的头文件,并提供关于任务管理的基础接口。 --- #### 2. `<linux/init_task.h>` 这是专门针对 `init_task` 定义的一个独立头文件,在较新的内核版本中可能被包含到更广泛的头文件集合中。 ```c #include <linux/init_task.h> ``` 在这个头文件中,可以找到类似于以下的内容: ```c #define INIT_TASK(name) \ { \ .state = TASK_RUNNING, \ /* ... other fields initialized here */ \ } ``` 这表明了如何通过宏来初始化 `task_struct` 结构中的字段。 --- #### 3. `<asm/current.h>` 或 `<linux/thread_info.h>` 这些头文件提供了访问当前运行线程的信息方法,其中涉及到了一些辅助功能以支持 `current` 指针的操作。 ```c #include <asm/current.h> // or <linux/thread_info.h> ``` 它们间接关联到 `init_task`,因为所有的任务都需要能够快速定位自身的上下文环境。 --- #### 4. `<linux/rcupdate.h>` 虽然主要关注的是读复制更新 (RCU),但它也涉及到某些情况下对全局变量状态变化的通知机制,比如当检测到 CPU 停滞警告时的行为调整[^3]。 ```c #include <linux/rcupdate.h> ``` 尽管这不是直接联系于 `init_task` 自身,但在复杂场景下的调试或者性能优化过程中可能会用得上此类工具链组件。 --- ### 示例代码片段展示如何引用上述头文件并操作 init_task 数据成员 下面给出了一段伪代码样例演示怎样利用前述提到过的那些关键部分来进行实际开发工作: ```c // Example usage of headers and accessing elements within init_task structure. #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> static int __init example_init(void){ printk(KERN_INFO "Init Task State:%ld\n", atomic_read(&init_task.state)); return 0; } module_init(example_init); MODULE_LICENSE("GPL"); ``` 以上程序简单打印出了系统启动时刻默认状态下 Idle Process 所处的状态标志位数值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值