Linux reboot全过程

article/2025/11/3 23:03:27

原文:https://blog.csdn.net/davion_zhang/article/details/52233043

一、版本说明
嵌入式Linux 下面的reboot命令看似简单,但出问题时定位起来发现别有洞天。

下面就按在shell下执行reboot命令之后程序的执行过程进行解析。

Busybox:1.23.2                        ——制作跟文件系统,/sbin/reboot程序的由来

Libc:2.6.1                                  ——标准C库

Linux kernel:2.6.35                 ——内核版本

 

二、流程简介

         如图所示是reboot的简要流程图。


 普通的reboot是通过busybox为入口,进入halt_main函数,然后给init进程发送SIGTERM信号,init进程接收到信号后给其他进程发送终止信号,最后调用C库函数reboot,reboot通过系统调用sys_reboot进入内核,内核将整个系统重启。其中在shell中执行reboot –f则通过halt_main直接调用C函数reboot,不经过init进程。

三、代码详解

1.reboot命令端

执行reboot命令,busybox检查当前命令为reboot,进入函数halt_main,

reboot,halt和poweroff都会进入这个函数,不同的命令发送的信号和执行的操作不同。

现只分析reboot的情况。

代码如下

int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int halt_main(int argc UNUSED_PARAM, char **argv)
{static const int magic[] = {RB_HALT_SYSTEM,RB_POWER_OFF,RB_AUTOBOOT};static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };int delay = 0;int which, flags, rc;/* Figure out which applet we're running */for (which = 0; "hpr"[which] != applet_name[0]; which++)continue;/* Parse and handle arguments */opt_complementary = "d+"; /* -d N *//* We support -w even if !ENABLE_FEATURE_WTMP,* in order to not break scripts.* -i (shut down network interfaces) is ignored.*/flags = getopt32(argv, "d:nfwi", &delay);sleep(delay);write_wtmp();if (flags & 8) /* -w */return EXIT_SUCCESS;if (!(flags & 2)) /* no -n */sync();/* Perform action. */rc = 1;if (!(flags & 4)) { /* no -f */
//TODO: I tend to think that signalling linuxrc is wrong
// pity original author didn't comment on it...if (ENABLE_FEATURE_INITRD) {/* talk to linuxrc *//* bbox init/linuxrc assumed */pid_t *pidlist = find_pid_by_name("linuxrc");if (pidlist[0] > 0)rc = kill(pidlist[0], signals[which]);if (ENABLE_FEATURE_CLEAN_UP)free(pidlist);}if (rc) {/* talk to init */if (!ENABLE_FEATURE_CALL_TELINIT) {/* bbox init assumed */rc = kill(1, signals[which]);} else {/* SysV style init assumed *//* runlevels:* 0 == shutdown* 6 == reboot */execlp(CONFIG_TELINIT_PATH,CONFIG_TELINIT_PATH,which == 2 ? "6" : "0",(char *)NULL);bb_perror_msg_and_die("can't execute '%s'",CONFIG_TELINIT_PATH);}}} else {rc = reboot(magic[which]);}if (rc)bb_perror_nomsg_and_die();return rc;
}


该函数判断reboot是否带了 -f 参数,如果带了,直接调用reboot调用C函数库

如果没带,则通过

kill(1, signals[which]);

给init进程发送SIGTERM信号。

2.init进程端

init进程初始化函数init_main将部分信号进行重定义

        bb_signals_recursive_norestart(0+ (1 << SIGINT)  /* Ctrl-Alt-Del */+ (1 << SIGQUIT) /* re-exec another init */
#ifdef SIGPWR+ (1 << SIGPWR)  /* halt */
#endif+ (1 << SIGUSR1) /* halt */+ (1 << SIGTERM) /* reboot */+ (1 << SIGUSR2) /* poweroff */
#if ENABLE_FEATURE_USE_INITTAB+ (1 << SIGHUP)  /* reread /etc/inittab */
#endif, record_signo);

 

void record_signo(int signo)
{bb_got_signal = signo;
}


 
将SIGUSR1(halt) SIGUSR2(poweroff) SIGTERM(reboot)信号存入全局变量bb_got_signal中。 
在init_main的最后进入一个while(1)循环,不断检查信号和等待子进程的退出
其中check_delayed_sigs就是用来检查这个全局变量的,如下:

    

while (1) {int maybe_WNOHANG;maybe_WNOHANG = check_delayed_sigs();/* (Re)run the respawn/askfirst stuff */run_actions(RESPAWN | ASKFIRST);maybe_WNOHANG |= check_delayed_sigs();/* Don't consume all CPU time - sleep a bit */sleep(1);maybe_WNOHANG |= check_delayed_sigs();/* Wait for any child process(es) to exit.** If check_delayed_sigs above reported that a signal* was caught, wait will be nonblocking. This ensures* that if SIGHUP has reloaded inittab, respawn and askfirst* actions will not be delayed until next child death.*/if (maybe_WNOHANG)maybe_WNOHANG = WNOHANG;while (1) {pid_t wpid;struct init_action *a;/* If signals happen _in_ the wait, they interrupt it,* bb_signals_recursive_norestart set them up that way*/wpid = waitpid(-1, NULL, maybe_WNOHANG);if (wpid <= 0)break;a = mark_terminated(wpid);if (a) {message(L_LOG, "process '%s' (pid %d) exited. ""Scheduling for restart.",a->command, wpid);}/* See if anyone else is waiting to be reaped */maybe_WNOHANG = WNOHANG;}} /* while (1) */


而里面的while(1)一般会阻塞在waitpid中,那么信号检查是不是会有问题?

WNOHANG        如果没有可用的子进程退出状态,立即返回而不是阻塞

但maybe_WNOHANG的值应该是0,不是WNOHANG(=1)感觉还是会阻塞。我这样理解的,因为所有的用户进程都是init进程的子进程,我判断前面执行reboot时也是一个子进程,halt_main发送完信号后就会退出,init接收到信号而且waitpid成功,然后跳出循环检查信号。
下面看一下信号的处理部分

static int check_delayed_sigs(void)
{int sigs_seen = 0;while (1) {smallint sig = bb_got_signal;if (!sig)return sigs_seen;bb_got_signal = 0;sigs_seen = 1;
#if ENABLE_FEATURE_USE_INITTABif (sig == SIGHUP)reload_inittab();
#endifif (sig == SIGINT)run_actions(CTRLALTDEL);if (sig == SIGQUIT) {exec_restart_action();/* returns only if no restart action defined */}if ((1 << sig) & (0
#ifdef SIGPWR+ (1 << SIGPWR)
#endif+ (1 << SIGUSR1)+ (1 << SIGUSR2)+ (1 << SIGTERM))) {halt_reboot_pwoff(sig);}}
}


判断为SIGTERM进入halt_reboot_pwoff函数

static void halt_reboot_pwoff(int sig)
{const char *m;unsigned rb;/* We may call run() and it unmasks signals,* including the one masked inside this signal handler.* Testcase which would start multiple reboot scripts:*  while true; do reboot; done* Preventing it:*/reset_sighandlers_and_unblock_sigs();run_shutdown_and_kill_processes();m = "halt";rb = RB_HALT_SYSTEM;if (sig == SIGTERM) {m = "reboot";rb = RB_AUTOBOOT;} else if (sig == SIGUSR2) {m = "poweroff";rb = RB_POWER_OFF;}message(L_CONSOLE, "Requesting system %s", m);pause_and_low_level_reboot(rb);/* not reached */
}

reset_sighandlers_and_unblock_sigs函数将信号重置回默认处理。

static void reset_sighandlers_and_unblock_sigs(void)
{bb_signals(0+ (1 << SIGUSR1)+ (1 << SIGUSR2)+ (1 << SIGTERM)+ (1 << SIGQUIT)+ (1 << SIGINT)+ (1 << SIGHUP)+ (1 << SIGTSTP)+ (1 << SIGSTOP), SIG_DFL);sigprocmask_allsigs(SIG_UNBLOCK);
}

run_shutdown_and_kill_processes函数给所有进程发送SIGTERM信号并执行sync(保存数据)
延时后再次发送SIGKILL信号,这里说明一下为什么要发送SIGKILL信号,一般的SIGINT和SIGTERM信号都可以屏蔽或转作他用,SIGKILL信号是不可被屏蔽的,

这样告诉其他进程必须终止。

static void run_shutdown_and_kill_processes(void)
{/* Run everything to be run at "shutdown".  This is done _prior_* to killing everything, in case people wish to use scripts to* shut things down gracefully... */run_actions(SHUTDOWN);message(L_CONSOLE | L_LOG, "The system is going down NOW!");/* Send signals to every process _except_ pid 1 */kill(-1, SIGTERM);message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");sync();sleep(1);kill(-1, SIGKILL);message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");sync();/*sleep(1); - callers take care about making a pause */
}


最终进入函数pause_and_low_level_reboot,起一个轻量级进程执行reboot标准C函数

static void pause_and_low_level_reboot(unsigned magic)
{pid_t pid;/* Allow time for last message to reach serial console, etc */sleep(1);/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)* in linux/kernel/sys.c, which can cause the machine to panic when* the init process exits... */pid = vfork();if (pid == 0) { /* child */reboot(magic);_exit(EXIT_SUCCESS);}while (1)sleep(1);
}


到这里busybox里面的内容全部处理完。


3.标准C函数reboot

前面执行reboot -f 就是直接执行的这个函数
reboot函数比较简单,直接进行系统调用进入内核。(0xffe1dead  feeldead这个魔术还是比较有意思的)

其中参数howto为RB_AUTOBOOT=0x01234567

sysdeps/unix/sysv/linux/reboot.c

int
reboot (int howto)
{return INLINE_SYSCALL (reboot, 3, (int) 0xfee1dead, 672274793, howto);
}

4.内核系统调用
kernel/sys.c

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,void __user *, arg)
{
。。。mutex_lock(&reboot_mutex);switch (cmd) {case LINUX_REBOOT_CMD_RESTART:kernel_restart(NULL);break;case LINUX_REBOOT_CMD_CAD_ON:C_A_D = 1;break;case LINUX_REBOOT_CMD_CAD_OFF:C_A_D = 0;break;case LINUX_REBOOT_CMD_HALT:kernel_halt();do_exit(0);panic("cannot halt");case LINUX_REBOOT_CMD_POWER_OFF:kernel_power_off();do_exit(0);break;。。。default:ret = -EINVAL;break;}mutex_unlock(&reboot_mutex);return ret;
}

进入
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
调用kernel_restart函数

——>machine_restart

void machine_restart(char *cmd)
{machine_shutdown();if (ppc_md.restart)ppc_md.restart(cmd);
#ifdef CONFIG_SMPsmp_send_stop();
#endifprintk(KERN_EMERG "System Halted, OK to turn off power\n");local_irq_disable();while (1) ;
}

这个函数之后就与具体的架构有关系了。
下面是powerpc P1020芯片的复位

ppc_md.restart(cmd);的函数原型在/arch/powerpc/platforms/85xx中定义

define_machine(p2020_rdb_pc) {.name            = "P2020RDB-PC",.probe            = p2020_rdb_pc_probe,.setup_arch        = mpc85xx_rdb_setup_arch,.init_IRQ        = mpc85xx_rdb_pic_init,
#ifdef CONFIG_PCI.pcibios_fixup_bus    = fsl_pcibios_fixup_bus,
#endif.get_irq        = mpic_get_irq,.restart        = fsl_rstcr_restart,.calibrate_decr        = generic_calibrate_decr,.progress        = udbg_progress,
};
void fsl_rstcr_restart(char *cmd)
{local_irq_disable();if (rstcr)/* set reset control register */out_be32(rstcr, 0x2);    /* HRESET_REQ */while (1) ;
}

最终cpu往寄存器Reset control register(0x000E_00B0)中写2
也就是往管脚HRESET_REQ发出了一个信号,该信号应该与HRESET硬复位管脚相连

这样就实现了CPU的复位

------------------------------------------------------------------------------------
下面是 TI am5728的reboot内核调用的调用过程

SyS_rebootkernel_restartmachine_restartarm_pm_restartomap44xx_restartomap_prm_reset_systemprm_ll_data->reset_system();omap4_prminst_global_warm_sw_reset

最终往PRM_RSTCTRL[0]写1,触发一个全局软件热复位。


http://chatgpt.dhexx.cn/article/b7rbxgq0.shtml

相关文章

linux嵌入式reboot不生效,Embeded linux之reboot

reboot命令 //kernel\sys.c magic1、magic2&#xff0c;两个int类型的“魔力数”&#xff0c;用于防止误操作。具体在“include/uapi/linux/reboot.h”中定义。 cmd&#xff0c;reboot方式。 //include\linux\reboot.h #define LINUX_REBOOT_CMD_RESTART 0x01234567 #define LI…

reboot 流程

前言 对于生在智能时代的我们&#xff0c;对关机和重启这两个概念肯定不会陌生&#xff0c;尤其经历早期Android智能机的人们&#xff0c;印象最深恐怕就是重启&#xff0c;当然现在不会了&#xff0c;还没有改过来的都被淘汰了。在Linux系统中我们使用reboot完成这个动作,下面…

Vue路由和路由器简介

前言 路由(route)是vue中非常重要的技术&#xff0c;几乎每一个用vue所写的项目都会用到路由&#xff0c;它是一个vue的插件库&#xff0c;专门实现SPA应用 路由(route)的简介 说到路由&#xff0c;大多数人会想到路由器(router),可以这么说&#xff0c;路由器上的每一个口都…

路由传递数据

1.路由可以传递query和params参数。路由组件可以通过$route获取query和params参数。 2.路由传递params参数 &#xff08;1&#xff09;当to为字符串形式时。直接在路径后边添加数据 但是在路由中&#xff0c;path属性必须给数据占位。 &#xff08;2&#xff09;当to为对象形…

vue-router 路由传参方式/缓存路由/路由跳转

基础 前端路由 用来开发 SPA&#xff08;单页面应用&#xff09; 单页面应用 整个应用只有一个完整页面&#xff0c;页面变化都是在这一个页面更新的点击链接不会刷新整个页面&#xff0c;会局部更新&#xff0c;也会更新浏览历史&#xff08;地址&#xff09;点击链接也不…

Vue路由的$router.back(-1)回退时如何判断有没有上一个路由

每个页面的左上角有一个返回按钮< 点击时的代码是this.$router.back(-1)&#xff0c;返回上一个路由 但是用户点开其中一页&#xff0c;用户打开时并没有上一条路由的历史记录&#xff0c;所以点击<按钮时没有反应。 所以应该怎么判断有没有上一条路由的历史记录。 1.在页…

回程静态路由及trunk简单链路

六、trunk链路 可以实现连个交换机间不同vlan相同网段间的互访 [one]vlan 2 [one-vlan2]vlan 3 [one-vlan3]quit [one]port-group 1 [one-port-group-1]group-memberGigabitEthernet 0/0/1 Gigab&#xff09;itEthernet 0/0/3 [one-port-group-1]port link-type access …

Vue路由(vue-router)

一、Vue路由的相关概念 1、路由&#xff08;routing&#xff09;&#xff1a;是指从源到目的地时&#xff0c;决定端到端路径的决策过程。 2、前端路由&#xff1a;即由前端来维护一个路由规则。实现模式有两种。 (1)利用URL的Hash模式&#xff1a;就是常说的锚点&#xff0c;J…

Vue Router路由详解

文章目录 1. 介绍2. Vue Router 和 Vue 项目集成3. 声明式导航4. 编程式导航5. 重定向和4046. 嵌套路由7. 动态路由匹配 1. 介绍 Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成&#xff0c;让构建单页面应用变得易如反掌。包含的功能有&#xff1a; 嵌…

Vue 路由(Router)详细介绍(切换,传参,通信)

目录 前言 准备工作 基本使用 router-link router-view 基本流程 扩展内容 动态路由匹配 ​编辑 编程式导航 重定向 路由组件传参 HTML5 History模式 前言 在一个vue项目中&#xff0c;免不了对组件&#xff08;页面&#xff09;进行切换与跳转。而用 Vue.js Vu…

使用Best Trace工具查看VPS的去程和回程线路的路由

​ 去程线路查询操作比较简单&#xff0c;这里主要先说回在Linux VPS上安装Best Trace工具查询回程线路的方法 ​ Best Trace官网 回程线路路由查询 Linux版本Best Trace Ubuntu/Debian准备工作 apt install -y wget zip CentOS准备工作 yum -y install wget zip 下载 …

Vue路由(vue-router)详细讲解指南

中文文档&#xff1a;https://router.vuejs.org/zh/ Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成&#xff0c;让构建单页面应用变得易如反掌。路由实际上就是可以理解为指向&#xff0c;就是我在页面上点击一个按钮需要跳转到对应的页面&#xff0c;这…

静态路由配置

静态路由配置 一、静态路由简单配置方法 一、静态路由简单配置方法 设置静态路由步骤&#xff1a; 1.设置每个设备&#xff08;PC、路由器&#xff09;接口IP&#xff1a; 如图所示配置好各个接口的IP地址: 配置路由器AR1 0/0/0接口IP&#xff1a; [Huawei]interface Gigabi…

【接口篇 / Lan】(5.4) ❀ 02. 与交换机连接 (回程路由) ❀ FortiGate 防火墙

【简介】大多数企业都会配备三层交换机&#xff0c;通过划分VLAN更好的管理内部网络&#xff0c;三层交换机功能强大&#xff0c;本身带路由功能&#xff0c;可以控制VLAN之间的互相访问&#xff0c;这里要介绍的是三层交换机与防火墙连接上网。 回程路由 在没有等动态路由的情…

静态路由的配置以及简单分析

我们先把每个pc、路由器端口的ip配置好 然后在配置静态路由 在配置静态路由的时候&#xff0c;我们先配去程路由 在路由器R1 的命令行输入ip route-static 192.168.6.0 30 192.168.2.2 再去路由器R2 的命令行输入 ip route-static 192.168.1.0 30 192.168.2.1 配置回程路由 配置…

路由回环配置

一、拓扑模型 二、要求及分析 要求 配置pc等设备&#xff0c;要求互通 分析 1、配置IP地址 2、路由配置 三、配置IP r1配置 [r1]interface GigabitEthernet 0/0/0 [r1-GigabitEthernet0/0/0]ip address 192.168.1.100 24 [r1]interface GigabitEthernet 0/0/1 [r1-Gigab…

路由-回指路由

回指路由&#xff0c;是一个相对的概念。顾名思义&#xff0c;是指“回去的路由”。 当原路由是A区域-->B区域时&#xff0c;A向B发送访问请求&#xff0c;以A为源B为目标&#xff0c;此时B需要以A为目标地址&#xff0c;B为源配置一个静态路由&#xff0c;作为回指路由。 …

【计算机网络】Linux下路由配置总结

文章目录 路由的基础知识Linux内核路由表使用route -n命令查看Linux内核路由表三种路由类型说明(Flags)配置路由route的命令设置包转发静态路由配置 参考 路由的基础知识 1&#xff09;路由概念 路由&#xff1a; 跨越从源主机到目标主机的一个互联网络来转发数据包的过程路由…

Message消息框

//示例1.默认右下角 $.messager.show({title:我的消息,//可以用span标签拼接改变字体样式msg:data.msg,timeout:2000,showType:slide });//示例2.消息将显示在顶部中间 自定义消息框位置 $.messager.show({title:我的消息,msg:消息将在4秒后关闭。,showType:show,style:{right:…

简单的message提示框插件

下载&#xff1a;https://download.csdn.net/download/billhepeng/11929586