[关闭]
@guhuizaifeiyang 2017-09-27T01:24:12.000000Z 字数 6621 阅读 1666

init进程的执行过程分析

开关机流程


Init进程,它是Linux内核启动之后运行的第一个进程。它的进程号是1,并且生命周期贯穿整个linux 内核运行的始终。
linux中所有其它的进程的共同祖先均为init进程,可以通过“adb shell ps | grep init”查看进程号。
Android init进程的入口文件在system/core/init/init.cpp中,由于init是命令行程序,所以分析init.cpp首先应从main函数开始:

时序图:
此处输入图片的描述

main()

system/core/init/init.cpp

  1. "ueventd"和"watchdogd"执行的是另外的入口
  2. 挂载文件系统
  3. 屏蔽标准的输入输出/初始化内核log系统,初始化log系统。
  4. selinux初始化
  5. 重新设置属性
  6. 处理子进程kill时的情况
  7. 加载default.prop中的属性
  8. 解析init.rc
  1. int main(int argc, char** argv) {
  2. // (1) "ueventd"和"watchdogd"执行的是另外的入口
  3. if (!strcmp(basename(argv[0]), "ueventd")) {
  4. return ueventd_main(argc, argv);
  5. }
  6. if (!strcmp(basename(argv[0]), "watchdogd")) {
  7. return watchdogd_main(argc, argv);
  8. }
  9. /* umask是Linux函数,用来控制权限。文件的默认权限是644,目录是755。umask(0)表示赋予文件和目录所有的默认权限,即不去除任何权限。*/
  10. // Clear the umask.
  11. umask(0);
  12. // 添加PATH=_PATH_DEFPATH _PATH_DEFPATH="/usr/bin:/bin"
  13. add_environment("PATH", _PATH_DEFPATH);
  14. bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
  15. // Get the basic filesystem setup we need put together in the initramdisk
  16. // on / and then we'll let the rc file figure out the rest.
  17. // (2) 挂载文件系统
  18. if (is_first_stage) {
  19. // 挂载tmpfs文件系统
  20. mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
  21. mkdir("/dev/pts", 0755);
  22. mkdir("/dev/socket", 0755);
  23. // 挂载devpts文件系统
  24. mount("devpts", "/dev/pts", "devpts", 0, NULL);
  25. #define MAKE_STR(x) __STRING(x)
  26. // 挂载proc文件系统
  27. mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
  28. // 挂载sysfs文件系统
  29. mount("sysfs", "/sys", "sysfs", 0, NULL);
  30. }
  31. // We must have some place other than / to create the device nodes for
  32. // kmsg and null, otherwise we won't be able to remount / read-only
  33. // later on. Now that tmpfs is mounted on /dev, we can actually talk
  34. // to the outside world.
  35. // (3) 屏蔽标准的输入输出/初始化内核log系统,初始化log系统。
  36. open_devnull_stdio();
  37. klog_init(); // 创建/dev/kmsg,保存内核log。
  38. klog_set_level(KLOG_NOTICE_LEVEL); // 设置log级别为5
  39. // 首次启动is_first_stage=first stage,再次启动is_first_stage=second stage。
  40. NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
  41. if (!is_first_stage) {
  42. // Indicate that booting is in progress to background fw loaders, etc.
  43. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
  44. // 初始化属性(具体实现,还有待探究)
  45. property_init();
  46. // If arguments are passed both on the command line and in DT,
  47. // properties set in DT always have priority over the command-line ones.
  48. // (待探究)
  49. process_kernel_dt();
  50. process_kernel_cmdline();
  51. // Propagate the kernel variables to internal variables
  52. // used by init as well as the current required properties.
  53. export_kernel_boot_props();
  54. }
  55. // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
  56. // (4) selinux初始化(待探究)
  57. /* selinux有两种工作模式:
  58. 1、"permissive",所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志
  59. 2、"enforcing",所有操作都会进行权限检查。在一般的终端中,应该工作于enforing模式
  60. adb shell getenforce 查看selinux模式
  61. adb shell setenforce 0 命令进入permissive模式
  62. adb shell setenforce 1 命令进入Enforcing模式 */
  63. selinux_initialize(is_first_stage);
  64. // If we're in the kernel domain, re-exec init to transition to the init domain now
  65. // that the SELinux policy has been loaded.
  66. // (5) 重新设置属性
  67. if (is_first_stage) {
  68. if (restorecon("/init") == -1) { // restorecon命令用来恢复SELinux文件属性
  69. 来自: http://man.linuxde.net/restorecon
  70. ERROR("restorecon failed: %s\n", strerror(errno));
  71. security_failure();
  72. }
  73. char* path = argv[0];
  74. char* args[] = { path, const_cast<char*>("--second-stage"), nullptr }; //设置参数--second-stage
  75. // 执行init进程,重新进入main函数
  76. if (execv(path, args) == -1) {
  77. ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
  78. security_failure();
  79. }
  80. }
  81. // These directories were necessarily created before initial policy load
  82. // and therefore need their security context restored to the proper value.
  83. // This must happen before /dev is populated by ueventd.
  84. NOTICE("Running restorecon...\n");
  85. restorecon("/dev");
  86. restorecon("/dev/socket");
  87. restorecon("/dev/__properties__");
  88. restorecon("/property_contexts");
  89. restorecon_recursive("/sys");
  90. // 创建epoll句柄(暂时不清楚用途)
  91. epoll_fd = epoll_create1(EPOLL_CLOEXEC);
  92. if (epoll_fd == -1) {
  93. ERROR("epoll_create1 failed: %s\n", strerror(errno));
  94. exit(1);
  95. }
  96. // (6) signal_handler_init函数就是处理子进程kill时的情况
  97. signal_handler_init();
  98. // (7) 加载default.prop中的属性
  99. property_load_boot_defaults();
  100. // 读取"ro.oem_unlock_supported"属性值
  101. export_oem_lock_status();
  102. // start_property_service函数创建了socket,然后监听,并且调用register_epoll_handler函数把socket的fd放入了epoll中。
  103. start_property_service();
  104. const BuiltinFunctionMap function_map;
  105. Action::set_function_map(&function_map);
  106. // (8) 解析init.rc
  107. Parser& parser = Parser::GetInstance();
  108. parser.AddSectionParser("service",std::make_unique<ServiceParser>());
  109. parser.AddSectionParser("on", std::make_unique<ActionParser>());
  110. parser.AddSectionParser("import", std::make_unique<ImportParser>());
  111. parser.ParseConfig("/init.rc");
  112. // ...
  113. return 0;
  114. }

open_devnull_stdio()

Util.cpp

  1. void open_devnull_stdio(void)
  2. {
  3. // Try to avoid the mknod() call if we can. Since SELinux makes
  4. // a /dev/null replacement available for free, let's use it.
  5. int fd = open("/sys/fs/selinux/null", O_RDWR);
  6. if (fd == -1) {
  7. // OOPS, /sys/fs/selinux/null isn't available, likely because
  8. // /sys/fs/selinux isn't mounted. Fall back to mknod.
  9. static const char *name = "/dev/__null__";
  10. if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
  11. fd = open(name, O_RDWR);
  12. unlink(name);
  13. }
  14. if (fd == -1) {
  15. exit(1);
  16. }
  17. }
  18. dup2(fd, 0); // 复制文件描述符fd到0(标准输入)
  19. dup2(fd, 1); // 复制文件描述符fd到1(标准输出)
  20. dup2(fd, 2); // 复制文件描述符fd到2(错误输出)
  21. if (fd > 2) {
  22. close(fd);
  23. }
  24. }

这个函数调用dup函数把标准输入,输出,错误输出都重定位到/dev/null,如果需要在后面的程序中看到打印的话需要屏蔽这个函数。

klog_init()

Klog.c

  1. void klog_init(void) {
  2. if (klog_fd >= 0) return; /* Already initialized */
  3. klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
  4. if (klog_fd >= 0) {
  5. return;
  6. }
  7. static const char* name = "/dev/__kmsg__";
  8. if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
  9. klog_fd = open(name, O_WRONLY | O_CLOEXEC);
  10. unlink(name);
  11. }
  12. }

创建/dev/kmsg/,保存内核的log。

property_init()

property_service.cpp

  1. void property_init() {
  2. if (__system_property_area_init()) {
  3. ERROR("Failed to initialize property area\n");
  4. exit(1);
  5. }
  6. }
  7. bionic\libc\bionic\System_properties.cpp
  8. int __system_property_area_init()
  9. {
  10. free_and_unmap_contexts();
  11. mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  12. if (!initialize_properties()) {
  13. return -1;
  14. }
  15. bool open_failed = false;
  16. bool fsetxattr_failed = false;
  17. list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
  18. if (!l->open(true, &fsetxattr_failed)) {
  19. open_failed = true;
  20. }
  21. });
  22. if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
  23. free_and_unmap_contexts();
  24. return -1;
  25. }
  26. initialized = true;
  27. return fsetxattr_failed ? -2 : 0;
  28. }

signal_handler_init()

Note:init是一个守护进程,为了防止init的子进程成为僵尸进程(zombie process),需要init在子进程结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。

system/core/init/Singal_handler.cpp

  1. void signal_handler_init() {
  2. // // 在linux当中,父进程是通过捕捉SIGCHLD信号来得知子进程运行结束的情况
  3. // Create a signalling mechanism for SIGCHLD.
  4. int s[2];
  5. if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
  6. ERROR("socketpair failed: %s\n", strerror(errno));
  7. exit(1);
  8. }
  9. signal_write_fd = s[0];
  10. signal_read_fd = s[1];
  11. // Write to signal_write_fd if we catch SIGCHLD.
  12. struct sigaction act;
  13. memset(&act, 0, sizeof(act));
  14. act.sa_handler = SIGCHLD_handler;
  15. act.sa_flags = SA_NOCLDSTOP;
  16. sigaction(SIGCHLD, &act, 0);
  17. ServiceManager::GetInstance().ReapAnyOutstandingChildren();
  18. register_epoll_handler(signal_read_fd, handle_signal);
  19. }

Android启动篇 — init原理(一)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注