原文地址:http://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的情况。

代码如下

[cpp] view plaincopyprint?
  1. int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  2. int halt_main(int argc UNUSED_PARAM, char **argv)
  3. {
  4. static const int magic[] = {
  5. RB_HALT_SYSTEM,
  6. RB_POWER_OFF,
  7. RB_AUTOBOOT
  8. };
  9. static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
  10. int delay = 0;
  11. int which, flags, rc;
  12. /* Figure out which applet we're running */
  13. for (which = 0; "hpr"[which] != applet_name[0]; which++)
  14. continue;
  15. /* Parse and handle arguments */
  16. opt_complementary = "d+"; /* -d N */
  17. /* We support -w even if !ENABLE_FEATURE_WTMP,
  18. * in order to not break scripts.
  19. * -i (shut down network interfaces) is ignored.
  20. */
  21. flags = getopt32(argv, "d:nfwi", &delay);
  22. sleep(delay);
  23. write_wtmp();
  24. if (flags & 8) /* -w */
  25. return EXIT_SUCCESS;
  26. if (!(flags & 2)) /* no -n */
  27. sync();
  28. /* Perform action. */
  29. rc = 1;
  30. if (!(flags & 4)) { /* no -f */
  31. //TODO: I tend to think that signalling linuxrc is wrong
  32. // pity original author didn't comment on it...
  33. if (ENABLE_FEATURE_INITRD) {
  34. /* talk to linuxrc */
  35. /* bbox init/linuxrc assumed */
  36. pid_t *pidlist = find_pid_by_name("linuxrc");
  37. if (pidlist[0] > 0)
  38. rc = kill(pidlist[0], signals[which]);
  39. if (ENABLE_FEATURE_CLEAN_UP)
  40. free(pidlist);
  41. }
  42. if (rc) {
  43. /* talk to init */
  44. if (!ENABLE_FEATURE_CALL_TELINIT) {
  45. /* bbox init assumed */
  46. rc = kill(1, signals[which]);
  47. } else {
  48. /* SysV style init assumed */
  49. /* runlevels:
  50. * 0 == shutdown
  51. * 6 == reboot */
  52. execlp(CONFIG_TELINIT_PATH,
  53. CONFIG_TELINIT_PATH,
  54. which == 2 ? "6" : "0",
  55. (char *)NULL
  56. );
  57. bb_perror_msg_and_die("can't execute '%s'",
  58. CONFIG_TELINIT_PATH);
  59. }
  60. }
  61. } else {
  62. rc = reboot(magic[which]);
  63. }
  64. if (rc)
  65. bb_perror_nomsg_and_die();
  66. return rc;
  67. }

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

如果没带,则通过

kill(1, signals[which]);

给init进程发送SIGTERM信号。

2.init进程端

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

[cpp] view plaincopyprint?
  1. bb_signals_recursive_norestart(0
  2. + (1 << SIGINT)  /* Ctrl-Alt-Del */
  3. + (1 << SIGQUIT) /* re-exec another init */
  4. fdef SIGPWR
  5. + (1 << SIGPWR)  /* halt */
  6. ndif
  7. + (1 << SIGUSR1) /* halt */
  8. + (1 << SIGTERM) /* reboot */
  9. + (1 << SIGUSR2) /* poweroff */
  10. f ENABLE_FEATURE_USE_INITTAB
  11. + (1 << SIGHUP)  /* reread /etc/inittab */
  12. ndif
  13. , record_signo);
[cpp] view plaincopyprint?
  1. void record_signo(int signo)
  2. {
  3. bb_got_signal = signo;
  4. }

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

其中check_delayed_sigs就是用来检查这个全局变量的,如下:

[cpp] view plaincopyprint?
  1. while (1) {
  2. int maybe_WNOHANG;
  3. maybe_WNOHANG = check_delayed_sigs();
  4. /* (Re)run the respawn/askfirst stuff */
  5. run_actions(RESPAWN | ASKFIRST);
  6. maybe_WNOHANG |= check_delayed_sigs();
  7. /* Don't consume all CPU time - sleep a bit */
  8. sleep(1);
  9. maybe_WNOHANG |= check_delayed_sigs();
  10. /* Wait for any child process(es) to exit.
  11. *
  12. * If check_delayed_sigs above reported that a signal
  13. * was caught, wait will be nonblocking. This ensures
  14. * that if SIGHUP has reloaded inittab, respawn and askfirst
  15. * actions will not be delayed until next child death.
  16. */
  17. if (maybe_WNOHANG)
  18. maybe_WNOHANG = WNOHANG;
  19. while (1) {
  20. pid_t wpid;
  21. struct init_action *a;
  22. /* If signals happen _in_ the wait, they interrupt it,
  23. * bb_signals_recursive_norestart set them up that way
  24. */
  25. wpid = waitpid(-1, NULL, maybe_WNOHANG);
  26. if (wpid <= 0)
  27. break;
  28. a = mark_terminated(wpid);
  29. if (a) {
  30. message(L_LOG, "process '%s' (pid %d) exited. "
  31. "Scheduling for restart.",
  32. a->command, wpid);
  33. }
  34. /* See if anyone else is waiting to be reaped */
  35. maybe_WNOHANG = WNOHANG;
  36. }
  37. } /* while (1) */

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

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

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

下面看一下信号的处理部分

[cpp] view plaincopyprint?
  1. static int check_delayed_sigs(void)
  2. {
  3. int sigs_seen = 0;
  4. while (1) {
  5. smallint sig = bb_got_signal;
  6. if (!sig)
  7. return sigs_seen;
  8. bb_got_signal = 0;
  9. sigs_seen = 1;
  10. #if ENABLE_FEATURE_USE_INITTAB
  11. if (sig == SIGHUP)
  12. reload_inittab();
  13. #endif
  14. if (sig == SIGINT)
  15. run_actions(CTRLALTDEL);
  16. if (sig == SIGQUIT) {
  17. exec_restart_action();
  18. /* returns only if no restart action defined */
  19. }
  20. if ((1 << sig) & (0
  21. #ifdef SIGPWR
  22. + (1 << SIGPWR)
  23. #endif
  24. + (1 << SIGUSR1)
  25. + (1 << SIGUSR2)
  26. + (1 << SIGTERM)
  27. )) {
  28. halt_reboot_pwoff(sig);
  29. }
  30. }
  31. }

判断为SIGTERM进入halt_reboot_pwoff函数

[cpp] view plaincopyprint?
  1. static void halt_reboot_pwoff(int sig)
  2. {
  3. const char *m;
  4. unsigned rb;
  5. /* We may call run() and it unmasks signals,
  6. * including the one masked inside this signal handler.
  7. * Testcase which would start multiple reboot scripts:
  8. *  while true; do reboot; done
  9. * Preventing it:
  10. */
  11. reset_sighandlers_and_unblock_sigs();
  12. run_shutdown_and_kill_processes();
  13. m = "halt";
  14. rb = RB_HALT_SYSTEM;
  15. if (sig == SIGTERM) {
  16. m = "reboot";
  17. rb = RB_AUTOBOOT;
  18. } else if (sig == SIGUSR2) {
  19. m = "poweroff";
  20. rb = RB_POWER_OFF;
  21. }
  22. message(L_CONSOLE, "Requesting system %s", m);
  23. pause_and_low_level_reboot(rb);
  24. /* not reached */
  25. }

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

[cpp] view plaincopyprint?
  1. static void reset_sighandlers_and_unblock_sigs(void)
  2. {
  3. bb_signals(0
  4. + (1 << SIGUSR1)
  5. + (1 << SIGUSR2)
  6. + (1 << SIGTERM)
  7. + (1 << SIGQUIT)
  8. + (1 << SIGINT)
  9. + (1 << SIGHUP)
  10. + (1 << SIGTSTP)
  11. + (1 << SIGSTOP)
  12. , SIG_DFL);
  13. sigprocmask_allsigs(SIG_UNBLOCK);
  14. }

run_shutdown_and_kill_processes函数给所有进程发送SIGTERM信号并执行sync(保存数据)

延时后再次发送SIGKILL信号,这里说明一下为什么要发送SIGKILL信号,一般的SIGINT和SIGTERM信号都可以屏蔽或转作他用,SIGKILL信号是不可被屏蔽的,

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

[cpp] view plaincopyprint?
  1. static void run_shutdown_and_kill_processes(void)
  2. {
  3. /* Run everything to be run at "shutdown".  This is done _prior_
  4. * to killing everything, in case people wish to use scripts to
  5. * shut things down gracefully... */
  6. run_actions(SHUTDOWN);
  7. message(L_CONSOLE | L_LOG, "The system is going down NOW!");
  8. /* Send signals to every process _except_ pid 1 */
  9. kill(-1, SIGTERM);
  10. message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
  11. sync();
  12. sleep(1);
  13. kill(-1, SIGKILL);
  14. message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
  15. sync();
  16. /*sleep(1); - callers take care about making a pause */
  17. }

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

[cpp] view plaincopyprint?
  1. static void pause_and_low_level_reboot(unsigned magic)
  2. {
  3. pid_t pid;
  4. /* Allow time for last message to reach serial console, etc */
  5. sleep(1);
  6. /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
  7. * in linux/kernel/sys.c, which can cause the machine to panic when
  8. * the init process exits... */
  9. pid = vfork();
  10. if (pid == 0) { /* child */
  11. reboot(magic);
  12. _exit(EXIT_SUCCESS);
  13. }
  14. while (1)
  15. sleep(1);
  16. }

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

3.标准C函数reboot

前面执行reboot -f 就是直接执行的这个函数

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

其中参数howto为RB_AUTOBOOT=0x01234567

sysdeps/unix/sysv/linux/reboot.c

[cpp] view plaincopyprint?
  1. int
  2. reboot (int howto)
  3. {
  4. return INLINE_SYSCALL (reboot, 3, (int) 0xfee1dead, 672274793, howto);
  5. }

4.内核系统调用

kernel/sys.c

[cpp] view plaincopyprint?
  1. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
  2. void __user *, arg)
  3. {
  4. 。。。
  5. mutex_lock(&reboot_mutex);
  6. switch (cmd) {
  7. case LINUX_REBOOT_CMD_RESTART:
  8. kernel_restart(NULL);
  9. break;
  10. case LINUX_REBOOT_CMD_CAD_ON:
  11. C_A_D = 1;
  12. break;
  13. case LINUX_REBOOT_CMD_CAD_OFF:
  14. C_A_D = 0;
  15. break;
  16. case LINUX_REBOOT_CMD_HALT:
  17. kernel_halt();
  18. do_exit(0);
  19. panic("cannot halt");
  20. case LINUX_REBOOT_CMD_POWER_OFF:
  21. kernel_power_off();
  22. do_exit(0);
  23. break;
  24. 。。。
  25. default:
  26. ret = -EINVAL;
  27. break;
  28. }
  29. mutex_unlock(&reboot_mutex);
  30. return ret;
  31. }

进入

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

——>machine_restart

[cpp] view plaincopyprint?
  1. void machine_restart(char *cmd)
  2. {
  3. machine_shutdown();
  4. if (ppc_md.restart)
  5. ppc_md.restart(cmd);
  6. #ifdef CONFIG_SMP
  7. smp_send_stop();
  8. #endif
  9. printk(KERN_EMERG "System Halted, OK to turn off power\n");
  10. local_irq_disable();
  11. while (1) ;
  12. }

这个函数之后就与具体的架构有关系了。

下面是powerpc P1020芯片的复位

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

[cpp] view plaincopyprint?
  1. define_machine(p2020_rdb_pc) {
  2. .name           = "P2020RDB-PC",
  3. .probe          = p2020_rdb_pc_probe,
  4. .setup_arch     = mpc85xx_rdb_setup_arch,
  5. .init_IRQ       = mpc85xx_rdb_pic_init,
  6. #ifdef CONFIG_PCI
  7. .pcibios_fixup_bus  = fsl_pcibios_fixup_bus,
  8. #endif
  9. .get_irq        = mpic_get_irq,
  10. .restart        = fsl_rstcr_restart,
  11. .calibrate_decr     = generic_calibrate_decr,
  12. .progress       = udbg_progress,
  13. };
[cpp] view plaincopyprint?
  1. void fsl_rstcr_restart(char *cmd)
  2. {
  3. local_irq_disable();
  4. if (rstcr)
  5. /* set reset control register */
  6. out_be32(rstcr, 0x2);   /* HRESET_REQ */
  7. while (1) ;
  8. }

最终cpu往寄存器Reset control register(0x000E_00B0)中写2

也就是往管脚HRESET_REQ发出了一个信号,该信号应该与HRESET硬复位管脚相连

这样就实现了CPU的复位

linux reboot 实现流程相关推荐

  1. I.MX6 Linux Qt 启动流程跟踪

    /*************************************************************************** I.MX6 Linux Qt 启动流程跟踪* ...

  2. 我的内核学习笔记13:x86平台linux系统重启流程跟踪

    一直以来,笔者只知道重启Linux系统性使用reboot,但对其过程却无所知,涉及到哪些知识点也无概念.本文就跟踪一下重启的流程,平台为Intel x86,Linux内核版本为3.17.行文中&quo ...

  3. linux的启动流程和加载程序

    linux的启动流程和加载程序 先来了解一下整个过程,大致知道linux是怎么启动的. 启动时要加载内核,让内核来驱动硬件,这样才算是一个真正的操作系统 但内核启动前是要做一些其它工作的.启动过过程如 ...

  4. Android 关机(reboot)流程 -- sys.powerctl

    Base On Android 4.2 recovery 和 reboot流程 part 1. reboot recovery流程. 1,RecoverySystem.java中     privat ...

  5. linux操作系统启动流程与kickstart文件制作

    文章目录 一.Linux操作系统启动流程 1.1.简单回顾linux系统组成以及内核作用 1.2.简单了解一下磁盘构成以及相关基础知识 二.CentOS 启动流程(只适用于MBR类型的PC架构主机) ...

  6. Linux开机启动流程分析

    Linux开机启动十步骤 收藏分享2012-2-6 11:15| 发布者: 红黑魂| 查看数: 1366| 评论数: 0|来自: 比特网 摘要: 开机过程指的是从打开计算机电源直到LINUX显示用户登 ...

  7. Arch Linux 安装简明流程

    Arch Linux 安装简明流程 这是一篇为 GPT/EFI 引导 的电脑安装 Arch Linux(双系统)的中文简明流程,尽可能省略了可以省略的流程与文字以使得篇幅尽量短小,基本上基于 Arch ...

  8. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  9. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  10. Linux的系统启动流程

    Linux的系统启动流程 一.systemd进程介绍 1.systemd服务介绍 2.systemd功能 3.服务单元 3.查看以激活状态的服务单元 4.查看服务之间的依赖关系 二.Linux的运行级 ...

最新文章

  1. 2015-03-19 My Appointment text read case
  2. 为什么要给计算机配置IP地址,更改ip地址 为何要重启电脑
  3. presto .vs impala .vs HAWQ query engine
  4. 最便宜的双模5G手机来了!其它单模5G瞬间无光
  5. mysql并发死锁问题解决
  6. java boolean 对象_为什么Java后端用Boolean属性筛选不出对象,但改成String类型就可以了?...
  7. 安装@vue/cli脚手架又又又出问题啦!!!
  8. 【小插件】文字镂空“LSP”制作空心文字CAD
  9. 服务器做中转站,搭建 git 本地中转站
  10. oracle锁表查询及释放进程
  11. html转微信公众号,通过htmlparse动态抓取微信公众号的文章
  12. 求助文,缺少stubs-n32_hard.h
  13. 产品分析报告 | 二手市场面临着什么痛点?
  14. k8s教程(pod篇)-污点与容忍
  15. C#给图片加水印文字或图片
  16. OC的单例模式的实现
  17. 分享一款国产并口PSRAM存储芯片EMI164NA16LM
  18. 中国方言地图的总结与展望
  19. 进程监控工具 Procmon有Linux版本了
  20. 《越狱》中的项目管理——两个版本的对比

热门文章

  1. 大一高数下册笔记整理_大学高等数学知识点整理(全)
  2. python+selenium自动化软件测试(第1章)
  3. iPhone删除的照片能恢复吗?苹果手机照片怎么恢复
  4. ionic5 ion-refresher下拉更新
  5. T229473 D. 背单词的小智(二分)
  6. 根据地址查询经纬度Js
  7. 计算机保研个人简介,保研个人简历模板
  8. WordPress中用户头像不显示,如何解决
  9. 设置PyCharm背景图片
  10. 各类常用食物的热能含量及营养价值