首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【openwrt】【procd】Openwrt系统启动流程分析

【openwrt】【procd】Openwrt系统启动流程分析

作者头像
知否知否应是绿肥红瘦
发布于 2025-02-19 13:38:13
发布于 2025-02-19 13:38:13
26900
代码可运行
举报
文章被收录于专栏:Openwrt知识Openwrt知识
运行总次数:0
代码可运行

在Openwrt系统中执行ps命令可以看到 1号进程就是procd.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root@OpenWrt:/# ps -w
  PID USER       VSZ STAT COMMAND
    1 root      1856 S    /sbin/procd

但实际上内核启动完成后,运行的第一个用户进程并不是procd,在运行procd之前还执行了其他准备工作,换句话说,procd并不一开始就是“老大”,它只是最终接替了老大的位置。

下面就从内核即将启动用户空间第一个进程开始介绍Openwrt系统的启动流程。

kernel_init

在kernel启动的尾声,内核会去查找并调用 用户空间的init进程,从而进行内核态到用户态的切换,init进程就是用户空间的第一个进程,它的进程号为1 。 init进程路径可以通过如下方式指定:

如下2种方式是通过cmdline或者设备树获取init进程路径

  • ramdisk_execute_command
  • execute_command

如下方式是通过内核配置指定init进程路径

  • CONFIG_DEFAULT_INIT

如下4种方式是直接运行指定的程序作为init进程(按顺序查找,如果同时存在也只会运行第一个):

  • /sbin/init
  • /etc/init
  • /bin/init
  • /bin/sh
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int __ref kernel_init(void *unused) 
{
	/*
	  省略部分初始化
	*/
	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);
	}
	
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}
	
	if (CONFIG_DEFAULT_INIT[0] != '\0') {
		ret = run_init_process(CONFIG_DEFAULT_INIT);
		if (ret)
			pr_err("Default init %s failed (error %d)\n",
			       CONFIG_DEFAULT_INIT, ret);
		else
			return 0;
	}

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

static int run_init_process(const char *init_filename) {
	argv_init[0] = init_filename;
	pr_info("Run %s as init process\n", init_filename);
	return do_execve(getname_kernel(init_filename),
		(const char __user *const __user *)argv_init,
		(const char __user *const __user *)envp_init);
}

static int try_to_run_init_process(const char *init_filename) {
	ret = run_init_process(init_filename);
}

我目前使用的平台就是使用execute_command方式指定init进程的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ cat /proc/cmdline
console=ttyserial0,115200,n8  init=/etc/preinit

启动log中也有相关打印:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[1:swapper/0][name:main&]Run /etc/preinit as init process

接下来就开始分析/etc/preinit

/etc/preinit

etc/preinit是openwrt openwrt/package/base-files/files目录下的一个shell 脚本,其内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/sh
[ -z "$PREINIT" ] && exec /sbin/init

export PATH="%PATH%"

. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh

boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs
boot_hook_init preinit_mount_root

for pi_source_file in /lib/preinit/*; do
	. $pi_source_file
done

boot_run_hook preinit_essential

pi_mount_skip_next=false
pi_jffs2_mount_success=false
pi_failsafe_net_message=false

boot_run_hook preinit_main

当前我们只需要关注第一行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ -z "$PREINIT" ] && exec /sbin/init

这行意思很明显,如果"

/sbin/init

那么这个/sbin/init又是从哪里来的呢? 查看procd的packge makefile(openwrt/package/system/procd/Makefile)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
define Package/procd/install
	$(INSTALL_DIR) $(1)/sbin $(1)/etc $(1)/lib/functions
  
  # /sbin/init 来自下面这一句
	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{init,procd,askfirst,udevtrigger,upgraded} $(1)/sbin/
	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libsetlbf.so $(1)/lib
	$(INSTALL_BIN) ./files/reload_config $(1)/sbin/
	$(INSTALL_CONF) ./files/hotplug*.json $(1)/etc/
	$(INSTALL_DATA) ./files/procd.sh $(1)/lib/functions/
endef

init,procd,askfirst,udevtrigger,upgraded这些命令都属于procd组件,最终都会被安装到/sbin. /sbin/init执行流程如下:

  • init服务首先初始化ulog,设置其log的TAG为init(debug log中带有init tag的log都是由init服务打印的)
  • 然后挂载一些必要的文件系统,例如proc、sysfs、tmpfs等,并设置初始环境变量,以便接下来可以运行/bin,/sbin/usr/bin等目录下面的命令
  • 接下来获取并设置默认log打印等级,从cmdline中的init_debug=xxx字段获取
  • 随后初始化并启动看门狗,看门狗超时时间为30s
  • 接下来会fork一个子进程执行/sbin/kmodloader,即加载内核模块,并且会一直等待所有模块加载完成(即子进程结束)
  • 接着调用preinit()
  • 最后进入uloop_run

到目前为止,我们的主角procd还没有出场,上述过程仍然是在进行预初始化。下面再接着分析preinit()过程:

  • preinit()使用了uloop_process功能,这是libubox提供的一个组件,用于管理子进程。
  • preinit()一共fork了2个子进程,一个运行/sbin/procd -h /etc/hotplug-preinit.json ,另一个执行/etc/preinit脚本,这些子进程在执行完毕后,都会调用对应的uloop_process.cb函数。
  • /sbin/procd -h /etc/hotplug-preinit.json 的作用是监听内核uevent事件,并根据不同事件做出相应的处理(例如创建/dev/null设备节点)
  • plugd_proc.cb函数执行的内容很简单,就是设置plugd_proc.pid=0
  • 在本文的一开始就提到内核运行的第一个启动的进程是/etc/preinit,这里又一次运行了这个脚本,只不过目前PREINIT=1,所以并不会再次执行/sbin/init,这里会执行/etc/preinit脚本后面的内容。
  • /etc/preinit执行完成后,会调用preinit_proc.cb函数,这个函数里非常重要的一个步骤就是execvp(/sbin/procd),execvp函数会将当前进程的可执行文件替换成/sbin/procd并执行/sbin/procd,这里实际上实现了initprocd的转换,procd在这里就成为了pid=1的进程。

/sbin/procd

/sbin/procd的执行流程如下:

  • procd执行过程中,最为关键的就是procd_state_next(),它会进行状态的流转,初始状态是STATE_EARLY
    • STATE_EARLY阶段调用hotplug("/etc/hotplug.json")监听内核uevent事件,然后调用procd_coldplug()函数执行udevtrigger命令触发uevent事件,这一步完成后创建/dev/xxx设备节点,然后进入STATE_UBUS
    • STATE_UBUS阶段主要做2件事:1.启动ubusd 2.连接ubusd,最后进入状态STATE_INIT
    • STATE_INIT阶段首先解析/etc/inittab脚本,然后依次运行respawnaskconsoleaskfirstsysinit对应的handler,最后进入STATE_RUNNING.
    • STATE_RUNNING阶段会依次运行respawnlate,askconsolelate对应的handler
    • 最终程序进入uloop_run

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
linux内核启动流程(文章最后流程图)
本文以Linux3.14版本源码为例分析其启动流程。各版本启动代码略有不同,但核心流程与思想万变不离其宗。
全栈程序员站长
2022/09/15
1.8K0
第4阶段——制作根文件系统之分析init_post()如何启动第1个程序(1)
本文介绍了Linux操作系统中init进程的分析,init进程是Linux系统启动的第一个进程,负责控制系统运行的其他进程。文章首先介绍了init进程的基本信息,包括其定义、作用、生命周期等,然后详细分析了init进程的启动过程,包括内核传递参数、解析命令行参数、执行/etc/init/start.d/S85mount_root文件、执行/etc/init/start.d/S100sysinit文件、执行/etc/init/start.d/S101mountnfs_root文件、执行/etc/init/start.d/S102nfsmount_dev文件、执行/etc/init/start.d/S103/etc/init/local.conf中的脚本。文章还介绍了init进程的结束过程,包括执行/etc/init/stop.d/S100sysinit、执行/etc/init/stop.d/S101mountnfs_root、执行/etc/init/stop.d/S102nfsmount_dev、执行/etc/init/stop.d/S103/etc/init/local.conf中的脚本。最后,文章分析了init进程的启动和结束过程,并给出了具体的代码示例。
诺谦
2018/01/03
1.6K0
第4阶段——制作根文件系统之分析init_post()如何启动第1个程序(1)
构建Linux根文件系统
类似于Windows下的C、D、E等各个盘,Linux系统也可以将磁盘、Flash等存储设备划分为若干个分区,在不同分区存放不同类别的文件。与Windows的C盘类似,Linux一样要在一个分区上存放系统启动所必需的文件,比如内核映象文件(在嵌入式系统中,内核一般单独存放在一个分区中)、内核启动后运行的第一个程序(init)、给用户提供操作界面的shell程序、应用程序所依赖的库等。这些必需的、基本的文件,合称为根文件系统,它们存放在一个分区中。Linux系统启动后首先挂接这个分区──称为挂接(mount)根文件系统。其他分区上所有目录、文件的集合,也称为文件系统,比如我们常说:“挂接硬盘第二个分区”、“挂接硬盘第二个分区上的文件系统”。
韦东山
2020/09/30
3.8K0
构建Linux根文件系统
Linux启动流程 梳理| 思维导图 | 流程图 | 值得收藏
嵌入式与Linux那些事
2024/06/11
4170
Linux启动流程 梳理| 思维导图 | 流程图  | 值得收藏
Linux0号进程,1号进程,2号进程
本节我们将从linux启动的第一个进程说起,以及后面第一个进程是如何启动1号进程,然后启动2号进程。然后系统中所有的进程关系图做个简单的介绍
DragonKingZhu
2020/03/24
7.3K1
Initramfs_正在生成initramfs
一、initramfs是什么 在2.6版本的linux内核中,都包含一个压缩过的cpio格式的打包文件。当内核启动时,会从这个打包文件中导出文件到内核的rootfs文件系统,然后内核检查rootfs中是否包含有init文件,如果有则执行它,作为PID为1的第一个进程。这个init进程负责启动系统后续的工作,包括定位、挂载“真正的”根文件系统设备(如果有的话)。如果内核没有在rootfs中找到init文件,则内核会按以前版本的方式定位、挂载根分区,然后执行 /sbin/init程序完成系统的后续初始化工作。 这个压缩过的cpio格式的打包文件就是initramfs。编译2.6版本的linux内核时,编译系统总会创建initramfs,然后把它与编译好的内核连接在一起。内核源代码树中的usr目录就是专门用于构建内核中的initramfs的,其中的initramfs_data.cpio.gz文件就是initramfs。缺省情况下,initramfs是空的,X86架构下的文件大小是134个字节。
全栈程序员站长
2022/09/22
3.5K0
【openwrt】Openwrt系统的reboot流程
当我们在openwrt系统的终端中敲下 reboot指令后,系统需要进行一系列动作后,才会真正的进行硬件重启。而这一系列的动作可以分为——应用程序的停止和内核的停止。在Openwrt系统中,应用程序的停止是1号进程(也就是 procd)完成的,应用程序全部停止后才会进入内核的重启流程。下面我们将分别来介绍应用程序和内核停止的流程。
知否知否应是绿肥红瘦
2025/02/19
3130
【openwrt】Openwrt系统的reboot流程
kernel启动过程详解(梳理流程的工具和方法)
uboot 打印完 Starting kernel . . .,就完成了自己的使命,控制权便交给了 kernel 的第一条指令,也就是下面这个函数 init/main.c
全栈程序员站长
2022/07/29
1.5K0
系统启动及内核管理
一、知识整理 1、nohup 命令: 不挂断地运行命令,远程连接当断网或断开shell时还可以继续运行。无论是否将nohup命令的输出重定向到终端,输出都将附加到当前目录下的nohup.out文件中,如果当前不可写,则重定向到家目录nohup.out文件,除非定义了输出文件。 nohup COMMAND 输出重定向至文件 nohup COMMAND & 使用jobs查看任务,按后台任务关闭方式关闭。 2、设置和调节nice值: [root@localhost ~]# nice -n -5 ping 127
小小科
2018/05/04
1.2K0
系统启动及内核管理
【openwrt】【overlayfs】Openwrt系统overlayfs挂载流程
overlayfs是一种叠加文件系统,在openwrt和安卓系统中都有很广泛的应用,overlayfs通常用于将只读根文件系统(rootfs)和可写文件系统(jffs2)进行叠加后形成一个新的文件系统,这个新的文件系统“看起来”是可读写的,这种做法的好处是:
知否知否应是绿肥红瘦
2025/02/19
5640
【openwrt】【overlayfs】Openwrt系统overlayfs挂载流程
第4阶段——制作根文件系统之分析init进程(2)
本节目标: (1) 了解busybox(init进程和命令都放在busybox中) (2) 创建SI工程,分析busybox源码来知道init进程做了哪些事情 (3)  分析busybox中init进
诺谦
2018/01/03
1.5K0
第4阶段——制作根文件系统之分析init进程(2)
Linux启动时间优化-内核和用户空间启动优化实践
从内核timestamp 0.000000作为内核启动起点,到free_initmem()输出"Freeing init memory"作为内核启动的终点。
233333
2023/07/09
1.2K0
Linux启动时间优化-内核和用户空间启动优化实践
详解-制作根文件系统,并使用yaffs,jffs,nfs挂载系统(2)
1.安装mkyaffsimage, mkyaffs2image命令(用来制作yaffs文件系统)
呆呆
2021/05/27
1.7K0
Android 用户态启动流程分析
从硬件上电启动到终端软件可用是一个漫长而复杂的过程,我们在开心享受着丰富的应用程序时候,可能并没想到这其中所包含的工程师心血。
evilpan
2023/02/12
1K0
07.构建根文件系统之init进程分析
  根文件系统有许多命令,比如 ls cp,cd,这些命令其实也就是相当于一个应用程序。这些程序都封装在busybox中。编译busybox后就可以得到busybox,ls等命令就是到busybox的链接,执行ls等命令其实执行的是busybox程序。   busybox应用程序中,ls对应的是ls.c,cp对应的是cp.c。ubbot目的是启动内核,内核的目的是启动应用程序。内核如何启动应用程序呢?内核启动的第一个是init进程。不同的设备有不同的驱动程序,init如何区分呢?下面我们分析程序。
嵌入式与Linux那些事
2021/05/20
1K0
图解CentOS系统启动流程
作者 | Linux运维之路 来源 | 51CTO博客 云豆贴心提醒,本文阅读时间6分钟,文末有秘密! 当我们按下开机键后,系统背后的秘密我们是否了解呢?这里,我带大家探索一下linux系统开
小小科
2018/05/04
1.4K0
图解CentOS系统启动流程
《Linux内核分析》之构造一个简单的Linux系统MenuOS 实验总结
在原来配置的基础上,make menuconfig选中如下选项重新配置Linux,使之携带调试信息
WindCoder
2018/09/20
2.3K0
《Linux内核分析》之构造一个简单的Linux系统MenuOS 实验总结
CentOS系统启动流程你懂否
一、Linux内核的组成 相关概念: Linux系统的组成部分:内核+根文件系统 内核:进程管理、内存管理、网络协议栈、文件系统、驱动程序。 IPC(Inter-Process Communication进程间通信):就是指多个进程之间相互通信,交换信息的方法。Linux IPC基本上都是从Unix平台上继承而来的。主要包括最初的Unix IPC,System V IPC以及基于Socket的IPC。另外,Linux也支持POSIX IPC。 运行中的系统环境可分为两层:内核空间、用户空间
小小科
2018/05/04
1.1K0
CentOS系统启动流程你懂否
第4阶段——制作根文件系统 (4)
在上节制作busybox后(位于/work/nfs_root/mini_fs), 然后根据以下5个来构建最小根文件系统: (1)/dev/console(终端控制台, 提供标准输入、标准输出以及标准错
诺谦
2018/01/03
2.7K0
第4阶段——制作根文件系统 (4)
在全志V851S开发板上使用SSH配置步骤分析
3.处理报错 报错Privilege separation user sshd does not exist 执行:
阿志小管家
2024/02/02
3800
在全志V851S开发板上使用SSH配置步骤分析
推荐阅读
相关推荐
linux内核启动流程(文章最后流程图)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验