Linux内核启动---init进程

 一.  Linux内核启动

我们接下来重点看一下 init 进程,kernel_init 就是 init 进程的进程函数。

本文继上一篇文章的学习,地址如下:

Linux内核启动流程-第二阶段rest_init函数_凌肖战的博客-优快云博客

二.  Linux内核启动---init进程

1.  init进程

kernel_init 函数就是 init 进程具体做的工作,定义在文件 init/main.c 中,函数内容如下:

928 static int __ref kernel_init(void *unused)
929 {
930 int ret;
931
932 kernel_init_freeable(); /* init 进程的一些其他初始化工作 */
933 /* need to finish all async __init code before freeing the 
memory */
934 async_synchronize_full(); /* 等待所有的异步调用执行完成 */
935 free_initmem(); /* 释放 init 段内存 */
936 mark_rodata_ro();
937 system_state = SYSTEM_RUNNING; /* 标记系统正在运行 */
938 numa_default_policy();
939
940 flush_delayed_fput();
941
942 if (ramdisk_execute_command) {
943 ret = run_init_process(ramdisk_execute_command);
944 if (!ret)
945 return 0;
946 pr_err("Failed to execute %s (error %d)\n",
947 ramdisk_execute_command, ret);
948 }
949
950 /*
951 * We try each of these until one succeeds.
952 *
953 * The Bourne shell can be used instead of init if we are
954 * trying to recover a really broken machine.
955 */
956 if (execute_command) {
957 ret = run_init_process(execute_command);
958 if (!ret)
959 return 0;
960 panic("Requested init %s failed (error %d).",
961 execute_command, ret);
962 }
963 if (!try_to_run_init_process("/sbin/init") ||
964 !try_to_run_init_process("/etc/init") ||
965 !try_to_run_init_process("/bin/init") ||
966 !try_to_run_init_process("/bin/sh"))
967 return 0;
968
969 panic("No working init found. Try passing init= option to 
kernel. "
970 "See Linux Documentation/init.txt for guidance.");
971 }

第 932 行,kernel_init_freeable 函数用于完成 init 进程的一些其他初始化工作,稍后再来具体看一下此函数。

第 940 行,ramdisk_execute_command 是一个全局的 char 指针变量,此变量值为“/init”,也就是根目录下的 init 程序。ramdisk_execute_command 也可以通过 uboot 传递,在 bootargs 中使用“rdinit=xxx”即可,xxx 为具体的 init 程序名字。

第 943 行,如果存在“/init”程序的话就通过函数 run_init_process 来运行此程序。

第 956 行,如果 ramdisk_execute_command 为空的话就看 execute_command 是否为空,反 正不管如何一定要在根文件系统中找到一个可运行的 init 程序。execute_command 的值是通过 uboot 传递,在 bootargs 中使用“init=xxxx”就可以了,比如“init=/linuxrc”表示根文件系统中 的linuxrc 就是要执行的用户空间 init 程序。

第 963~966 行,如果 ramdisk_execute_command 和 execute_command 都为空,那么就依次

查找“/sbin/init”、“/etc/init”、“/bin/init”和“/bin/sh”,这四个相当于备用 init 程序,如果这四

个也不存在,那么 Linux 启动失败!

第 969 行,如果以上步骤都没有找到用户空间的 init 程序,那么就提示错误发生!

2.  kernel_init_freeable 函数

最后来简单看一下 kernel_init_freeable 函数,前面说了,kernel_init 会调用此函数来做一些 init 进程初始化工作。kernel_init_freeable函数 定义在文件 init/main.c 中,缩减后的函数内容如下:

973 static noinline void __init kernel_init_freeable(void)
974 {
975 /*
976 * Wait until kthreadd is all set-up.
977 */
978 wait_for_completion(&kthreadd_done);/* 等待 kthreadd 进程准备就绪 */
......
998 
999 smp_init(); /* SMP 初始化 */
1000 sched_init_smp(); /* 多核(SMP)调度初始化 */
1001
1002 do_basic_setup(); /* 设备初始化都在此函数中完成 */
1003
1004 /* Open the /dev/console on the rootfs, this should never fail */
1005 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) <
0)
1006 pr_err("Warning: unable to open an initial console.\n");
1007
1008 (void) sys_dup(0);
1009 (void) sys_dup(0);
1010 /*
1011 * check if there is an early userspace init. If yes, let it do 
1012 * all the work
1013 */
1014
1015 if (!ramdisk_execute_command)
1016 ramdisk_execute_command = "/init";
1017
1018 if (sys_access((const char __user *) ramdisk_execute_command, 
0) != 0) {
1019 ramdisk_execute_command = NULL;
1020 prepare_namespace();
1021 }
1022
1023 /*
1024 * Ok, we have completed the initial bootup, and
1025 * we're essentially up and running. Get rid of the
1026 * initmem segments and start the user-mode stuff..
1027 *
1028 * rootfs is available now, try loading the public keys
1029 * and default modules
1030 */
1031
1032 integrity_load_keys();
1033 load_default_modules();
1034 }

1002 行, do_basic_setup 函数用于完成 Linux 下设备驱动初始化工作!非常重要。
do_basic_setup 会调用 driver_init 函数完成 Linux 下驱动模型子系统的初始化。

1005 行,打开设备“ /dev/console ”,在 Linux 中一切皆为文件!因此“ /dev/console ”也是一个文件,此文件为控制台设备。每个文件都有一个文件描述符,此处打开的“ /dev/console 文件描述符为 0 ,作为标准输入 (0)
1008 1009 行, sys_dup 函数将标准输入 (0) 的文件描述符复制了 2 次,一个作为标准 (1) ,一个作为标准错误 (2) 。这样标准输入、输出、错误都是 /dev/console 了。
console 通过 uboot bootargs 环境变量设置,“ console=ttymxc0,115200 ”表示将 /dev/ttymxc0 设置为 console 也就是 I.MX6U 的串口 1 。当然,也可以设置其他的设备为 console ,比如虚拟控制台 tty1 ,设 tty1 console 就可以在 LCD 屏幕上看到系统的提示信息。

1020 行,调用函数 prepare_namespace 来挂载根文件系统。根文件系统也是由命令行参
数指定的,就是 uboot bootargs 环境变量。比如“ root=/dev/mmcblk1p2 rootwait rw ”就表示根
文件系统在 /dev/mmcblk1p2 中,也就是 EMMC 的分区 2 中。

Linux 内核启动流程就分析到这里,Linux 内核最终是需要和根文件系统打交道的,需要挂载根文件系统,并且执行根文件系统中的 init 程序,以此来进去用户态。

<think>好的,我现在需要处理用户提出的关于Docker的另一个错误问题。用户遇到了以下错误信息: “docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/usr/sbin/init": stat /usr/sbin/init: no such file or directory: unknown. ERRO[0000] error waiting for container:” 首先,我要理解这个错误的含义。用户可能在运行某个容器时指定了入口点为"/usr/sbin/init",但Docker容器中找不到这个文件。这通常是因为镜像中不包含该路径的可执行文件,或者用户错误地配置了启动命令。 接下来,我需要回忆Docker容器启动的流程。当用户执行`docker run`时,Docker会使用镜像中的默认入口点(ENTRYPOINT)和命令(CMD)来启动容器。如果用户在运行命令时覆盖了这些设置,可能会引发问题。例如,某些教程可能会建议使用`--entrypoint /usr/sbin/init`来运行systemd,但这取决于基础镜像是否包含该文件。 可能的错误原因有几个方面: 1. 基础镜像的问题:比如使用Alpine镜像,默认没有systemd,所以缺少/usr/sbin/init。 2. 用户手动指定了错误的入口点,导致容器找不到可执行文件。 3. 镜像本身被构建时删除了某些必要组件,导致路径不存在。 需要分析用户使用的镜像和运行命令。例如,如果用户使用的是CentOS或Rocky Linux镜像,并且尝试启用systemd,可能需要特定的Dockerfile配置或运行参数。而如果用户错误地在Alpine镜像中尝试运行systemd,就会出现这个问题。 接下来,解决方案可能包括: - 检查使用的镜像是否支持所需的入口点,比如确认镜像中是否存在/usr/sbin/init- 修改Docker运行命令,使用正确的入口点或命令。 - 在Dockerfile中正确配置ENTRYPOINT和CMD。 - 如果确实需要使用systemd,确保使用适合的基础镜像,并正确配置特权模式和卷挂载。 另外,用户可能是在尝试运行需要初始化系统的容器,比如需要systemd来管理服务,这时候需要特定的配置,比如添加`--privileged`、`--volume /sys/fs/cgroup:/sys/fs/cgroup:ro`等参数。 需要进一步考虑用户的具体使用场景,是否在容器中运行需要特权或特定初始化进程的服务,并给出相应的建议。例如,如果用户只是想运行一个普通的服务,可能不需要指定自定义入口点,而应该使用镜像默认的启动命令。如果是需要systemd,则需要确保镜像支持,并进行正确的配置。 最后,总结步骤:确认镜像内容,检查入口点配置,调整运行参数或Dockerfile,提供示例命令供用户参考。</think>--- ### Docker容器启动失败问题解析 当遇到 **"exec: '/usr/sbin/init': no such file or directory"** 错误时,表明Docker容器尝试执行不存在的系统初始化程序。以下是具体原因与解决方案: --- #### 一、核心问题定位 1. **直接原因** Docker容器启动时尝试执行 `/usr/sbin/init`,但该文件在镜像中不存在。 2. **常见触发场景** - 使用 `--entrypoint` 或 `command` 参数强制指定了错误的入口程序 - 基础镜像缺少系统初始化工具(如Alpine镜像无systemd) - 容器需运行systemd但未正确配置(常见于CentOS/Rocky Linux镜像) 3. **关键验证方法** ```bash # 检查镜像中是否存在目标文件 docker run --rm your_image ls -l /usr/sbin/init ``` --- #### 二、解决方案步骤 ##### 方案1:修正容器启动命令 **适用场景**:错误指定了入口程序 ```bash # 删除自定义入口点参数 docker run -d your_image # 或明确指定有效入口程序 docker run -d --entrypoint /bin/bash your_image ``` ##### 方案2:更换基础镜像 **适用场景**:镜像本身缺失关键组件 ```dockerfile # 错误示例(Alpine无systemd) FROM alpine:latest # 正确示例(使用完整系统镜像) FROM rockylinux:9 ``` ##### 方案3:正确配置systemd容器 **适用场景**:需在容器内运行systemd ```bash docker run -d --name systemd_ctn \ --privileged \ --cap-add SYS_ADMIN \ --volume /sys/fs/cgroup:/sys/fs/cgroup:ro \ rockylinux:9 \ /usr/sbin/init ``` --- #### 三、进阶排查指南 ##### 检查镜像文件结构 ```bash # 交互式进入镜像检查 docker run -it --rm your_image bash ls -l /usr/sbin/init # 确认文件存在性 which init # 查看初始化程序路径 ``` ##### 分析Docker镜像构建过程 ```dockerfile # 错误配置示例(删除关键文件) RUN apt-get purge -y systemd # 导致/usr/sbin/init被删除 # 正确配置示例(保留必要组件) RUN apt-get install -y systemd ``` ##### 查看容器元数据 ```bash docker inspect your_container | grep -A 5 Entrypoint ``` --- #### 四、典型错误场景对照表 | 现象 | 可能原因 | 解决方案 | |------|----------|----------| | 使用Alpine镜像时出错 | Alpine使用OpenRC而非systemd | 改用`/sbin/init`或换用其他镜像 | | 自定义Dockerfile构建后出错 | 构建过程误删`systemd`包 | 添加安装命令:`RUN dnf install -y systemd` | | Kubernetes Pod中报错 | Pod安全策略限制特权模式 | 添加`securityContext.privileged: true` | --- #### 五、关联知识扩展 1. **容器初始化系统选择** - `systemd`:完整系统镜像(如Rocky Linux- `runit`:轻量级替代方案(部分定制镜像) - `直接启动服务`:单进程容器最佳实践 2. **特权模式风险控制** ```bash # 最小化权限配置 docker run --cap-add SYS_ADMIN --security-opt="no-new-privileges" ... ``` 3. **多阶段构建优化** ```dockerfile # 构建阶段使用完整镜像 FROM rockylinux:9 as builder RUN dnf install -y systemd # 运行时阶段仅保留必要文件 FROM rockylinux:9-minimal COPY --from=builder /usr/sbin/init /usr/sbin/ ``` --- ### 操作验证示例 成功运行systemd容器的完整流程: ```bash # 启动支持systemd的容器 docker run -d --name systemd_test \ --privileged \ rockylinux:9 \ /usr/sbin/init # 进入容器验证服务状态 docker exec -it systemd_test systemctl status ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值