Linux reboot全过程
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fzs333/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_INITTAB
if (sig == SIGHUP)
reload_inittab();
#endif
if (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_SMP
smp_send_stop();
#endif
printk(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的复位
---------------------
原文:https://blog.csdn.net/davion_zhang/article/details/52233043
Linux reboot全过程相关推荐
- Linux reboot命令
Linux reboot命令用于用来重新启动计算机. 若系统的 runlevel 为 0 或 6 ,则重新开机,否则以 shutdown 指令(加上 -r 参数)来取代 语法 reboot [-n] ...
- 【Linux】一步一步学Linux——reboot命令(197)
00. 目录 文章目录 00. 目录 01. 命令概述 02. 命令格式 03. 常用选项 04. 参考示例 05. 附录 01. 命令概述 reboot命令用于用来重新启动计算机.但是机器重启必须要 ...
- 当linux reboot不成功时怎么办
问题现象:当linux终端输入reboot命令,没有任何作用 问题分析:reboot命令没有起作用,可能是有相应的进程在死循环 问题解决:可以reboot -f 这是强制重启命令
- Linux: reboot:shutdown: systemctl
文章目录 简介 命令 系统调用 reboot 命令的参数 通知列表:notifer.c dracut shut down 简介 介绍linux的reboot过程 命令 vl2048-oam-a:roo ...
- linux reboot流程,从命令行到内核全解析
针对对象 这篇文章我尽量写的细一些,主要针对于不太懂得嵌入式命令行到系统层过程的新手看,如果觉得有用可以关注一下,不定期写一些新手需要看的文章. 正文 引言 我们在做嵌入式设备的时候,经常会输入一些命 ...
- Linux系统启动全过程
分为两部分,第一部分是硬件本身需要加载的信息,之后才是加载Linux相关信息,因为有装有双系统的机器嘛 1.计算机加电 2.BIOS开始运行,检测硬件:cpu.内存.硬盘等 3.BIOS读取CMOS存 ...
- linux reboot命 过程,IDRAC安装dell服务器操作系统(linux or windows),用到生命周期管理器...
1.首先给服务器装上idrac模块,然后给idrac配置一个远程IP,用作管理 2.在web界面输入IP,弹出以下界面,输入账号密码登录 3.认证成功后,点击虚拟控制台启动 4.进入如下界面,点击菜单 ...
- 【学习资料】VMware安装到Linux系统安装全过程2021版 【手把手教,包教包会】
一.安装虚拟机软件VMware workstation pro 12 1.双击安装图标,启动安装程序.如果安装时,登录的windows系统用户不是管理员,则需要单击右键,在弹出的菜单中选择" ...
- 移植wifi无线网卡到arm linux上全过程
原文地址:http://www.arm9home.net/read.php?tid-1305.html 前段时间移植了U-boot和linux2.6.27.9到mini2440上,最近有点空,手头有一 ...
- Linux系统调用全过程详解
系统调用(SYSTEM CALL) OS内核中都有一组实现系统功能的过程,系统调用就是对上述过程的调用.编程人员利用系统调用,向OS提出服务请求,由OS代为完成. 一般情况下,进程是不能够存取系统内核 ...
最新文章
- 【引用】在Eclipse中将java Project转换成Dynamic Web Project
- 收集一些好的技术文档
- git add 回滚_Git的reset命令详解
- matchShapes函数
- 剑指offer03.数组中重复的数字
- 专业的LaTeX: 在Linux下编写高质量的文档
- 哈利波特检索_语料库ing说: 难怪哈利波特与混血王子那么好看
- 百度智能云发布『乘风』 端云一体化方案 助力人脸应用10分钟落地
- 罗格斯大学计算机科学排名,罗格斯大学计算机工程硕士专业排名读完这篇立即秒懂...
- 常用的分析方法论及分析框架
- 基于SSM实现的房屋租赁系统【附源码】(毕设)
- 基于JSP的旅游网站系统
- 关于CCSP云安全专家考试你一定要知道的事!
- 安装系统或者进PE蓝屏 代码:IRQL NOT LESS OR EQUAL
- Failed to compile../public/UEditor/dialogs/template/template.html 1:0Module parse failed: Unexpec
- parcel打包报错Error opening directory
- 记录一下线上高并发情况下 SimpleDateFormat 出现异常问题【项目】
- h5 invoke android,uniapp安卓版本11.0.0以上真机调试App: onLaunch have been invoked
- 八皇后问题的进化(4)-python写的八皇后
- 接口测试二(App抓包)
热门文章
- Sql Server 02
- c语言调用min()函数求最小值,min函数(min函数多条件求最小值)
- moya + RxSwift 进行网络请求
- python tokenize()_tokenize (Language) – Python 中文开发手册 - Break易站
- Summation Prove (Intro. to Algo. Open Course Episode 5) in Quick Sort
- putty连接服务器显示连接超时,putty连接云服务器超时连接
- 关于PIN只能更改不能删除的解决方法
- Dynamical Isometry and a Mean Field Theory of CNNs
- 计算机竞赛等级,美国计算机奥林匹克竞赛等级
- SolidWorks打开step.文件显示模板无效的解决办法