openwrt reboot流程

openwrt 系统中,当执行了 reboot 命令,系统将会发生什么事情呢?如何进行重启的呢?下面来一起看一下。

reboot 应用层操作

首先,reboot 是由busybox(它是一个集成了常用Linux命令和工具的软件)提供的一个Linux命令。reboot 命令存在在系统的 /sbin 目录下,到该目录查看可以发现它是一个软链接,指向 /bin 目录下的 busybox 可执行程序。

当执行 reboot 命令,将会调用到busybox中的init/halt.c文件,实际上,关机、重启都是由该文件实现的。

reboot 其他参数
  • -w:仅保存系统用户使用信息,不会进行重启等操作;
  • -n:执行reboot命令前不会执行sync()函数;
  • -f:是否通过init进程关机,如果没有-f参数,则通过init进程关机;
no -f 关机

如果执行 reboot 命令时没有带 -f 参数,将会按照下面流程进行。

  1. 如果配置了 ENABLE_LINUXRC,将会kill linuxrc 程序(默认ENABLE_LINUXRC为0);
  2. 如果 ENABLE_FEATURE_CALL_TELINIT 配置为0,则给1号进程进行发送信号,signals[] = { SIGUSR1, SIGUSR2, SIGTERM }。(默认ENABLE_FEATURE_CALL_TELINIT 为0);
  3. 如果 ENABLE_FEATURE_CALL_TELINIT 配置为1,则通过execlp()函数进行reboot等操作;

下面进行分析第2步,看看给1号进程发送信号之后会发生什么。

1号进程是/sbin/procd,通过查看 procd 的代码可以看到在procd_signal()函数中有各个信号的处理。最终的在procd中,如果是SIGINT、SIGTERM信号,将会执行重启;如果是SIGUSR1、SIGUSR2信号,将执行关机,相应的操作都是通过procd_shutdown()函数完成的,只是传递的参数不一样而已。

procd_shutdown()

在该函数中,将init进程的state置为STATE_SHUTDOWN,再state_enter()中执行STATE_SHUTDOWN操作。通过 set_console() 函数重定向输出到终端,而后执行procd_inittab_run("shutdown")操作,实际上该操作就是执行 /etc/inittab 中的shutdown操作,而在openwrt平台,该操作就是执行 /etc/rc.d 中的shutdown操作(自启动脚本的shutdown操作),最后再执行sync()操作。

在这里回头看,从我们执行reboot命令,到busybox中的处理reboot参数,而后给1号进程发信号,1号进程执行 procd_shutdown() 函数的STATE_SHUTDOWN操作,但是似乎都没有最终的调用到 reboot 操作,那么,具体的reboot操作又是那里实现的呢?

奥秘就在procd_inittab_run("shutdown")

干活的procd_inittab_run(“shutdown”)

要知道procd_inittab_run()函数进行了什么操作,得先分析一下procd_inittab()函数(开机过程会调用该函数)。

procd_inittab()函数将会打开/etc/inittab文件,将里面的操作添加到系统的操作链表中,待procd_inittab_run()函数调用。

/etc/inittab文件是init守护进程在系统运行过程中会读取信息来启动或退出进程的配置文件。inittab文件的每一项具有以下字段:

id:runlevels:action:process

id:它是每个登记项的标识符,用于唯一标识每个登记项,不能重复;

runlevels:系统的运行级别,表示process的action要在哪个级别下运行,该段中可以定义多个运行级别,各级别之间直接写不用分隔符;如果为空,表示在所有的运行级别运行;

action:表示对应登记项的process在一定条件下所要执行的动作;

具体动作有:

  • respawn:当process终止后马上启动一个新的

  • wait:当进入指定的runlevels后process才会启动一次,并且到离开这个runlevels终止

  • initdefault:设定默认的运行级别,即我们开机之后默认进入的运行级别,不能是0,6,你懂的

  • sysinit:系统初始化,只有系统开机或重新启动的时候,这个process才会被执行一次

  • powerwait:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,会等待这个进程完成之后,再执行相应的process

  • powerfail:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,不会等待这个进程完成,它会直接执行相应的process

  • powerokwait:电源已经故障,但是在等待执行对应操作的时候突然来电了就执行对应的process

  • powerfailnow:当电源故障并且init被通知UPS电源已经快耗尽执行相对应的process

  • ctrlaltdel:当用户按下ctrl+alt+del这个组合键的时候执行对应的process

  • boot:只有在引导过程中,才执行该进程,但不等待该进程的结束;当该进程死亡时,也不重新启动该进程

  • bootwait:只有在引导过程中,才执行该进程,并等待进程的结束;当该进程死亡时,也不重新启动该进程

  • off:如果process正在运行,那么就发出一个警告信号,等待20秒后,再通过杀死信号强行终止该process。如果process并不存在那么就忽略该登记项

  • once:启动相应的进程,但不等待该进程结束便继续处理/etc/inittab文件中的下一个登记项;当该进程死亡时,init也不重新启动该进程

  • shutdown:当系统重启、重启时将会执行的程序

process:表示启动哪个程序或脚本或执行哪个命令等;

简单了解上面信息之后,我们来看看procd_inittab_run("shutdown")的操作,而在openwrt系统,/etc/inittab文件中标识shutdown的信息如下:

::shutdown:/etc/init.d/rcS K shutdown

所以通过上面可以得到,将会执行 /etc/init.d/rcS 程序,但是在系统中并没有看到该文件,同时再回到procd_inittab_run()函数的实现将会看到,调用的将会是相应handler的cb()函数,shutdown操作的将会是调用runrc()函数。而在runrc()中,实际上就是执行rcS(K, shutdown, rcdone),再查看rcS()的实现,就发现是执行/etc/rc.d目录下的K*脚本中的shutdown操作。将K*脚本中的shutdown操作添加到任务队列中,然后再运行,任务逐个运行,到最后一个任务的时候,将会设置最后一个任务是运行工作队列中的empty_cb(),实际上,empty_cb() 就是工作队列中的任务都运行完时调用的。而上面在调用rcS()函数的时候,将empty_cb()函数设置为rcdone(),下面来看看rcdone()的操作。

rcdone()的操作比较简单,只是将init进程的state变量加1,再调用state_enter()。上面在procd_shutdown()中已经将state置为STATE_SHUTDOWN了,当前再加1,那state就是STATE_HALT。

state_enter() 的STATE_HALT

在HALT操作中,先signal(SIGCHLD, SIG_IGN)通知内核,init进程忽略子进程结束,子进程结束后由内核回收。而后通过kill(-1, SIGTERM)、kill(-1, SIGKILL)操作,发信号给除1号进程外的所有进程,使它们退出。最后,将创建子进程调用reboot(reboot_event),至此,reboot应用层操作转到内核。

reboot内核流程

在应用层调用reboot()函数之后,将会到内核的kernel/reboot.c中的SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)函数。

在内核中,先检查当前是否具有root权限,因为reboot只能由root权限才可以执行,而后重启的话将会执行kernel_restart(),关机则执行kernel_power_off()。

kernel_restart()

在该函数中,主要进行下面的操作:

  1. kernel_restart_prepare():调用device_shutdown()函数,逐个调用device的shutdown()函数;
  2. migrate_to_reboot_cpu():将当前运行程序切换到CPU0;
  3. syscore_shutdown():调用通过register_syscore_ops()注册的shutdown的回调函数,比如irq、cpufreq、clock等核心模块的shutdown函数;
  4. machine_restart():调用到平台的restart()函数;

machine_restart() 的实现依赖不同的平台,如ARM64将会调用到arch/arm64/kernel/process.c中的machine_restart()。在ARM64平台中,将会先关闭中断使能,再关闭除CPU0外的其他CPU核,最后将会通过 arm_pm_restart() 函数指针调用到各自平台实现。

总结

通过上面的简单介绍,总结一下reboot流程。

  1. 接着将会发信号到1号进程procd,而procd将会执行shutdown操作,实际上就是调用/etc/rc.d目录下的K*的shutdown操作;
  2. 上面的K*都操作完之后,将会更新init进程的state为STATE_HALT;
  3. 在STATE_HALT中,将会退出除一号进程外的其他进程,接着执行reboot函数进入内核;
  4. 在内核中,调用device、irq、clock等的shutdown操作,关中断,关核,调用相应平台的重启接口;
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
OpenWrt启动流程如下: 1. 加电启动:当设备加电后,CPU从ROM(只读存储器)中读取启动代码(bootloader),并将其加载到内存中。 2. bootloader:bootloader是一个小型程序,用于初始化各种硬件设备和加载内核。它通常会检查外部存储设备(如Flash)是否存在,并尝试从中加载内核。 3. 内核启动:一旦bootloader加载内核,内核开始启动。内核首先初始化硬件设备,如网卡、USB等。接下来,它会挂载文件系统,并启动init进程。 4. init进程:init进程是Linux系统的第一个进程。它是用户空间的第一个进程,负责启动其他进程。在OpenWrt中,init进程是一个特殊的版本,称为procd。它启动其他系统服务进程,如网络管理、防火墙、NAT等。 5. 系统服务进程:一旦procd启动,它会启动其他系统服务进程。这些进程管理网络、文件系统、硬件等方面的任务。例如,network进程管理网络接口,dnsmasq进程提供DHCP和DNS服务。 6. 用户空间:一旦系统服务进程启动,用户空间就启动了。用户空间是系统中所有用户进程运行的地方。用户空间中的进程通常是由用户启动的,例如telnetd(远程登录)、samba(文件共享)等。 总的来说,OpenWrt启动流程包括bootloader、内核、init进程、系统服务进程和用户空间。这些组件协同工作,使OpenWrt系统在启动时完成各种任务并运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengwei_peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值