Linux启动流程 | kernel执行第一个init应用程序的实现原理

1. 概述

Linux系统启动过程中通过init_task创建0号idle进程。然后通过kernel_thread创建1号init进程。创建该进程时通过系统调用,在内核空间执行用户空间的/sbin/init程序,通过该程序产生出shell,并依赖init衍生出其他进程。通过top命令查看当前系统环境下的进程列表,可以发现1号进程的为{linuxrc} init

[root@iTOP-4412]# top
Mem: 26404K used, 948572K free, 0K shrd, 3199543672K buff, 0K cached
CPU:  0.0% usr  6.0% sys  0.0% nic 94.0% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 0.00 0.00 0.00 1/78 162PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND162   132 root     R     3264  0.3   0  4.5 top3     2 root     IW       0  0.0   0  1.5 [kworker/0:0]132     1 root     S     3268  0.3   2  0.0 -/bin/sh1     0 root     S     3264  0.3   2  0.0 {linuxrc} init
...

我们在kernel代码中会发现,创建1号init进程的方式,主要包括以下3种,如下图所示:

0f61a51d2858aa31c63a3738ff8e3499.png

2. 创建init进程的方式

2.1 ramdisk方式

在ramdisk环境下创建init进程时,需要在kernel CMDLINE中设置init程序的路径位置,如下所示:

CONFIG_CMDLINE="...root=/dev/ram rdinit=/sbin/init..."

在kernel代码中通过rdinit_setup()解析kernel CMDLINErdinit=字符串,赋值给全局变量ramdisk_execute_command

static int __init rdinit_setup(char *str)
{unsigned int i;ramdisk_execute_command = str;/* See "auto" comment in init_setup */for (i = 1; i < MAX_INIT_ARGS; i++)argv_init[i] = NULL;return 1;
}
__setup("rdinit=", rdinit_setup);

当完成ramdisk_execute_command赋值后,在kernel_init_freeable()ramdisk_execute_command进行检查,若未检查到有效的字符串,则将ramdisk_execute_command赋值为/init。然后,对ramdisk_execute_command进行访问权限检查,若失败,则进行rootfs挂载。

static noinline void __init kernel_init_freeable(void)
{
...if (!ramdisk_execute_command)ramdisk_execute_command = "/init";if (ksys_access((const char __user *)ramdisk_execute_command, 0) != 0) {ramdisk_execute_command = NULL;prepare_namespace();}
...
}

ramdisk_execute_command检查成功,则进入kernel_init()中,执行指定的init程序。

static int __ref kernel_init(void *unused)
{int ret;kernel_init_freeable();...if (ramdisk_execute_command) {ret = run_init_process(ramdisk_execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d)\n",ramdisk_execute_command, ret);}
...
}

2.2 execute_command方式

通过kernel CMDLINE可以设定执行的init程序,例如:

CONFIG_CMDLINE="root=/dev/mmcblk1p2 rw console=ttySAC2,115200 init=/linuxrc rootwait"

在kernel代码中通过init_setup()解析命令行参数"init=",并赋值给execute_command

static int __init init_setup(char *str)
{unsigned int i;execute_command = str;...for (i = 1; i < MAX_INIT_ARGS; i++)argv_init[i] = NULL;return 1;
}
__setup("init=", init_setup);

最后,在kernel_init()中执行execute_command所指定的init程序。

static int __ref kernel_init(void *unused)
{
...if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (error %d).",execute_command, ret);}
...panic("No working init found.  Try passing init= option to kernel. ""See Linux Documentation/admin-guide/init.rst for guidance.");
}

2.3 默认方式

若以上两种指定init程序的方式均以失败告终,那么内核代码kernel_init()会执行如下4个默认的init程序,若也失败,则内核上报panic。

static int __ref kernel_init(void *unused)
{...if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;panic("No working init found.  Try passing init= option to kernel. ""See Linux Documentation/admin-guide/init.rst for guidance.");
}



推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

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

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

相关文章

m进制转换为n进制

m进制转换为n进制 void m2n(unsigned char *dest, unsigned int n, unsigned char *src, unsigned int m) {unsigned char ch, *p dest - 1;unsigned int i 0;if ((NULL dest) || (NULL src)){return ;}if ((m < 2 || m > 36) || (n < 2 || n > 36)){return ;}…

初始化linux-nginx的安装和使用

初始化linux-nginx的安装参考&#xff1a;https://blog.csdn.net/damys/article/details/68944070http://www.runoob.com/linux/nginx-install-setup.htmlhttps://www.cnblogs.com/crazylqy/p/6891929.html PS&#xff1a;为什么叫初始化linux&#xff0c;因为我用的是标准版的…

poj1484

简单题 View Code #include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define maxn 25int n, m, blow, power[maxn];bool on[maxn];void out(int a){int b m - a - 1;int x;for (int i 0; i < b; i…

memcpy()

memcpy()&#xff1a;把一块内存src的前count个字符复制到目的内存dest中 memcpy()用于对一块连续内存的拷贝&#xff0c;可用于对任何类型的数据拷贝&#xff0c;在拷贝时&#xff0c;需要指出要拷贝数据的大小 void *memcpy(void *dest, const void *src, unsigned int cou…

西安下雪了,做了一个室内温度计

摘要&#xff1a;最近各地都在下雪&#xff0c;湖南湖北西安都下雪了。养热带花草和宠物的同学们需要多留意室内温度。下面教一个实用性强、制作简单的温度计。使用DS18B20温度传感器和OLED模块构建&#xff0c;这里使用的开发板是Arduino。本项目的源码和3D文件在后台回复&…

oci mysql_Oracle常用的OCI函数

欢迎进入Oracle社区论坛&#xff0c;与200万技术人员互动交流 >>进入 sword OCIEnvInit ( OCIEnv **envhpp, ub4 mode, size_t xtramemsz, dvoid **usrmempp ); 注&#xff1a; 在8i以后&#xff0c;可用OCIEnvCreate一个函数就可以初始化环境了&#xff0c;相当于OCIIni…

sort注意事项

虽然是小事但是还是得注意一下&#xff1a;sort 区间左开右闭 从0开始 e.g: 10 9 8 7 6 5 4 3 2 1 sort(a6,a11) 区间 6 - 10 sort(a6,a10) 区间 6 - 9转载于:https://www.cnblogs.com/asdic/p/9758532.html

C语言预处理功能——关于字符串化和符号粘贴

在C语言开发中&#xff0c;宏定义是一个非常有用的工具&#xff0c;它可以使我们的代码更容易理解&#xff0c;更容易维护。如查一个常量在多处被使用&#xff0c;且今后可能会根据不同需要而修改的话&#xff0c;将其define一下那是再好不过了。除此之外&#xff0c;宏定义还有…

memmove()

memcpy()只是memmove()的一个子集 memcpy()在执行内存重叠的情况下时&#xff0c;就会发生错误&#xff0c;而memmove()不会 void *memmove(void *dest, const void *src, size_t count) {assert((dest!0)&&(src!0));unsigned char *pdest (char * )dest;unsigned char…

LINUX SHELL的神器,sed

我们知道&#xff0c;Vim 采用的是交互式文本编辑模式&#xff0c;你可以用键盘命令来交互性地插入、删除或替换数据中的文本。但本节要讲的 sed 命令不同&#xff0c;它采用的是流编辑模式&#xff0c;最明显的特点是&#xff0c;在 sed 处理数据之前&#xff0c;需要预先提供…

mysql使用shell脚本部署_shell脚本部署mysql主从

centos6两台Master IP&#xff1a;192.168.88.152Slave IP&#xff1a;192.168.88.153以下都是在主机上操作两机配置免密登录ssh-keygen -t rsassh-copy-id root192.168.88.153编写mysql_master脚本vim mysql_mster.sh#!/bin/bashservice iptables stopsetenforce 0yum -y inst…

ES6 数组、对象的扩展

8. 数组的扩展 扩展运算符&#xff08;...&#xff09;&#xff0c;将一个数组转为用逗号分隔的参数序列。 复制数组 const a2[...a1]合并数组 [...arr1, ...arr2, ...arr3]; arr1.concat(arr2, arr3);// ES5 的合并数组 这是浅拷贝,如果修改了原数组的成员&#xff0c;会同步反…

MVVM及MVVMLight相关资料

&#xff08;1&#xff09;dwahlin (2)http://blog.csdn.net/duanzilin/article/category/816515

我损失几百万换来的教训

大家好&#xff0c;我是写代码的篮球球痴&#xff0c;看到朱老师一篇抉择影响人生走向的文章&#xff0c;文中的我是作者本人。---我2009年初开始工作&#xff0c;2010年离开苏州来到深圳&#xff0c;那时候深圳的房价其实还不高&#xff0c;至少在全国来说深圳的房价还远没有现…

strcat()

把源字符串 连接 到目的字符串的后面。目的字符串的长度要足够长 char *strcat(char *dest, const char *src) {assert((NULL ! dest) && (NULL ! src));char *cp dest;while (*cp ! \0){cp;}while ((*cp *src) ! \0){;}return dest; }

android mysql 图片_android sqlite添加图片到数据库

sqlite图片是怎么储存的呢&#xff1f;其实是二进制的方式存储的blob代表使用二进制储存 (更多类型参考&#xff1a;数据库简介)在创建表的地方,VALUE_PIC代表存储图片字段&#xff0c;blob代表这个字段是以二进制储存的。/*创建表语句 语句对大小写不敏感 create table 表名(字…

MCU为什么内部不集成晶振

01MCU为什么不集成晶振本文将用STM32代替MCU。原因1&#xff1a;早些年&#xff0c;芯片的生产制作工艺也许还不能够将晶振做进芯片内部&#xff0c;但是现在可以了。这个问题主要还是实用性和成本决定的。实用性&#xff1a;如果封装进入STM32内部&#xff0c;不利于不同客户更…

[JSOI2007]建筑抢修 (贪心)

题目链接 Solution 可以考虑 \(dp\) ,但是很显然 \((n^2)\) 降不下来. 然后考虑贪心,首先,绝对的正确的是,在同等的情况下,给后面的留更多的时间. 首先按照 \(T_2\) 排序. 然后我们维护一个大根堆 每修理一栋建筑 我们就把这栋建筑的T1值加入堆 若当前无法修理 我们判断堆顶是否…

cad中怎么随意移动图形_cad中怎么随意移动图形_绘制CAD图纸的过程中CAD快捷键失灵了怎么办?...

很多人在使用笔记本绘制CAD图纸的过程中&#xff0c;当用到CAD快捷键的时候会发现部分CAD快捷键无法正常使用&#xff0c;比如按F8后并没有切换成正交状态&#xff0c;而是出来了连接投影仪的选项&#xff0c;而且其他的快捷键也无法使用了&#xff0c;如下图所示。在以前老的笔…

strcmp()

比较2个字符串&#xff0c;返回2个字符串的差值 int strcmp(const char *str1, const char *str2) {assert((NULL ! str1) && (NULL ! str2));while(*str1 && *str2 && (*str1 *str2)){str1;str2;}return (*str1 - *str2); }