linux启动第一个应用程序init && init启动android过程解析 && init.rc与inittab的关系 && android property和linux 环境变量
来源:互联网 发布:移动网络优化工作 编辑:程序博客网 时间:2024/05/16 15:42
linux启动第一个应用程序init
linux的运行顺序为uboot传递参数到内核,内核的第一个c编写的函数为start_kernel(),start_kernel来启动内核,最后到到rest_init()函数处完成内核启动过程。
rest_init()中启动第一个应用程序init,init程序的进程号为1,Linux使用了init进程来对组成Linux的服务和应用程序进行初始化。
init进程还负责了:
Linux运行时:init进程会负责收取孤儿进程。如果某个进程创建子进程之后,在子进程终止之前终止,则子进程成为孤儿进程。在Linux中所有的进程必须属于单棵进程树,所以孤立进程必须被收取。一旦进程成为孤儿,它会立即成为init进程的子进程。这是为了保持进程树的完整性。
系统关闭:init负责杀死所有其它的进程,卸载所有的文件系统并停止处理器的工作,以及任何其它被配置成要做的工作。
等等很多使命
完成linux初始化的rest_init()函数中会调用kernel_init,kernel_init函数代码如下:
其中“/init”的来源是在kernel_init_freeable函数中赋值到ramdisk_execute_command变量中的。
try_to_run_init_process一旦执行就不会再返回到此函数中了,而是作为linux 1号进程长期存活,直至关机时它关掉其他所有进行最后才会退出。
android系统中linux 1号进程init的源码在system\core\init\init.cpp(注:linux kernel源码中没有init进程的代码,而是在根文件系统之中,嵌入式中一般使用了busybox中含有的init.c)
详细的函数调用过程见下图:
init.rc文件结构介绍
init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字即每一行的第一列)来区分,这三个关键字是 on、service、import。
1、on类型的section表示一系列命令的组合。当on后面的action被触发时执行这个section后面的所有命令。如am.QueueEventTrigger("charger")。on后面也可以是properties的变化再触发执行相关命令,如on property:sys.boot_from_charger_mode=1
2、service类型的section表示一个可执行程序
3、import类型的section表示引入另外一个.rc文件
init.rc实例:
在原生linux中我们发现init进程是读取一个inittab的脚本而不是android中的init.rc。那这个脚本是干啥的呢?
inttab实例
inittab为linux初始化文件系统时init初始化程序用到的配置文件。这个文件负责设置init初始化程序初始化脚本?在哪里;每个运行级初始化时运行的命令;?开机、关机、重启对应的命令;各运行级登陆时所运行的命令
property是android在标准linux基础之上新增的一种系统全局可访问的属性机制。和linux的环境变量机制相似,可看做是android系统的环境变量。
在android上层可以同时使用这两种机制
linux的运行顺序为uboot传递参数到内核,内核的第一个c编写的函数为start_kernel(),start_kernel来启动内核,最后到到rest_init()函数处完成内核启动过程。
rest_init()中启动第一个应用程序init,init程序的进程号为1,Linux使用了init进程来对组成Linux的服务和应用程序进行初始化。
init进程还负责了:
Linux运行时:init进程会负责收取孤儿进程。如果某个进程创建子进程之后,在子进程终止之前终止,则子进程成为孤儿进程。在Linux中所有的进程必须属于单棵进程树,所以孤立进程必须被收取。一旦进程成为孤儿,它会立即成为init进程的子进程。这是为了保持进程树的完整性。
系统关闭:init负责杀死所有其它的进程,卸载所有的文件系统并停止处理器的工作,以及任何其它被配置成要做的工作。
等等很多使命
完成linux初始化的rest_init()函数中会调用kernel_init,kernel_init函数代码如下:
kernel\init\main.cstatic int __ref kernel_init(void *unused){int ret;kernel_init_freeable();/* need to finish all async __init code before freeing the memory */async_synchronize_full();free_initmem();mark_readonly();system_state = SYSTEM_RUNNING;numa_default_policy();flush_delayed_fput();if (ramdisk_execute_command) {ret = run_init_process(ramdisk_execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret);}/* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d). Attempting defaults...\n",execute_command, ret);}if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh"))return 0;panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance.");}其中最重要的函数就是try_to_run_init_process,run_init_process切换到用户空间,运行/init 第一个可执行程序
其中“/init”的来源是在kernel_init_freeable函数中赋值到ramdisk_execute_command变量中的。
try_to_run_init_process一旦执行就不会再返回到此函数中了,而是作为linux 1号进程长期存活,直至关机时它关掉其他所有进行最后才会退出。
android系统中linux 1号进程init的源码在system\core\init\init.cpp(注:linux kernel源码中没有init进程的代码,而是在根文件系统之中,嵌入式中一般使用了busybox中含有的init.c)
system\core\init\init.cppint main(int argc, char** argv) { if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (!strcmp(basename(argv[0]), "watchdogd")) { return watchdogd_main(argc, argv); } if (REBOOT_BOOTLOADER_ON_PANIC) { install_reboot_signal_handlers(); } add_environment("PATH", _PATH_DEFPATH); bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); if (is_first_stage) {////////////////////////////////////////////////////////////////////初始化第一阶段 boot_clock::time_point start_time = boot_clock::now(); // Clear the umask. umask(0); // Get the basic filesystem setup we need put together in the initramdisk // on / and then we'll let the rc file figure out the rest. mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");////////////////////////////挂载相关的文件系统 mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); #define MAKE_STR(x) __STRING(x) mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)); // Don't expose the raw commandline to unprivileged processes. chmod("/proc/cmdline", 0440); gid_t groups[] = { AID_READPROC }; setgroups(arraysize(groups), groups); mount("sysfs", "/sys", "sysfs", 0, NULL); mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL); mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)); mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)); mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)); // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually // talk to the outside world... InitKernelLogging(argv); LOG(INFO) << "init first stage started!"; if (!DoFirstStageMount()) { LOG(ERROR) << "Failed to mount required partitions early ..."; panic(); } SetInitAvbVersionInRecovery(); // Set up SELinux, loading the SELinux policy. selinux_initialize(true);///////////////////////////////////////////////seLinux初始化 // We're in the kernel domain, so re-exec init to transition to the init domain now // that the SELinux policy has been loaded. if (restorecon("/init") == -1) { PLOG(ERROR) << "restorecon failed"; security_failure(); } setenv("INIT_SECOND_STAGE", "true", 1); static constexpr uint32_t kNanosecondsPerMillisecond = 1e6; uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond; setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1); char* path = argv[0]; char* args[] = { path, nullptr }; execv(path, args); // execv() only returns if an error happened, in which case we // panic and never fall through this conditional. PLOG(ERROR) << "execv(\"" << path << "\") failed"; security_failure(); } // At this point we're in the second stage of init. InitKernelLogging(argv); LOG(INFO) << "init second stage started!";////////////////////////////////////////初始化第二阶段 // Set up a session keyring that all processes will have access to. It // will hold things like FBE encryption keys. No process should override // its session keyring. keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 1); // Indicate that booting is in progress to background fw loaders, etc. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); property_init(); // If arguments are passed both on the command line and in DT, // properties set in DT always have priority over the command-line ones. process_kernel_dt(); process_kernel_cmdline(); // Propagate the kernel variables to internal variables // used by init as well as the current required properties. export_kernel_boot_props(); // Make the time that init started available for bootstat to log. property_set("ro.boottime.init", getenv("INIT_STARTED_AT")); property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK")); // Set libavb version for Framework-only OTA match in Treble build. const char* avb_version = getenv("INIT_AVB_VERSION"); if (avb_version) property_set("ro.boot.avb_version", avb_version); // Clean up our environment. unsetenv("INIT_SECOND_STAGE"); unsetenv("INIT_STARTED_AT"); unsetenv("INIT_SELINUX_TOOK"); unsetenv("INIT_AVB_VERSION"); // Now set up SELinux for second stage. selinux_initialize(false); selinux_restore_context(); epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) { PLOG(ERROR) << "epoll_create1 failed"; exit(1); } signal_handler_init(); property_load_boot_defaults(); export_oem_lock_status(); start_property_service(); set_usb_controller(); const BuiltinFunctionMap function_map; Action::set_function_map(&function_map); Parser& parser = Parser::GetInstance(); parser.AddSectionParser("service",std::make_unique<ServiceParser>()); parser.AddSectionParser("on", std::make_unique<ActionParser>()); parser.AddSectionParser("import", std::make_unique<ImportParser>()); std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/init.rc");/////////////////////////////////读取启动脚本,将命令加入执行队列 parser.set_is_system_etc_init_loaded( parser.ParseConfig("/system/etc/init")); parser.set_is_vendor_etc_init_loaded( parser.ParseConfig("/vendor/etc/init")); parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init")); } else { parser.ParseConfig(bootscript); parser.set_is_system_etc_init_loaded(true); parser.set_is_vendor_etc_init_loaded(true); parser.set_is_odm_etc_init_loaded(true); } // Turning this on and letting the INFO logging be discarded adds 0.2s to // Nexus 9 boot time, so it's disabled by default. if (false) parser.DumpState(); ActionManager& am = ActionManager::GetInstance(); am.QueueEventTrigger("early-init"); // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev... am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done"); // ... so that we can start queuing up actions that require stuff from /dev. am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits"); am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict"); am.QueueBuiltinAction(keychord_init_action, "keychord_init"); am.QueueBuiltinAction(console_init_action, "console_init"); // Trigger all the boot actions to get us started. am.QueueEventTrigger("init");////////////////////////////////触发init.rc中的init动作,在init.rc中可以找到 “on init” section 其中的命令就会执行,后文相同 // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random // wasn't ready immediately after wait_for_coldboot_done am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); // Don't mount filesystems or start core system services in charger mode. std::string bootmode = GetProperty("ro.bootmode", ""); if (bootmode == "charger") { am.QueueEventTrigger("charger");///////////////////////////启动模式为charger则启动关机充电,不启动android } else if ((strncmp(bootmode.c_str(), "ffbm-00", 7) == 0) || (strncmp(bootmode.c_str(), "ffbm-01", 7) == 0)) { am.QueueEventTrigger("ffbm");//////////////////////////////启动模式为ffbm则进入ffbm模式 } else { am.QueueEventTrigger("late-init");/////////////////////////其它的启动模式都继续之后的初始化流程,从而启动android } // Run all property triggers based on current state of the properties. am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");///////////////根据当前所有properties的状态执行相应命令 while (true) { // By default, sleep until something happens. int epoll_timeout_ms = -1; if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) { am.ExecuteOneCommand();/////////////////////////////////////////执行加入队列中的命令 } if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) { restart_processes(); // If there's a process that needs restarting, wake up in time for that. if (process_needs_restart_at != 0) { epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000; if (epoll_timeout_ms < 0) epoll_timeout_ms = 0; } // If there's more work to do, wake up again immediately. if (am.HasMoreCommands()) epoll_timeout_ms = 0; } epoll_event ev; int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms)); if (nr == -1) { PLOG(ERROR) << "epoll_wait failed"; } else if (nr == 1) { ((void (*)()) ev.data.ptr)(); } } return 0;}从上面的注释可以看到在init进程中挂载了相关文件系统;读取并解析了init.rc脚本;根据不同的bootmode来选择进入的模式,如果没有特别指定模式则开启android初始化流程。
详细的函数调用过程见下图:
init.rc文件结构介绍
init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字即每一行的第一列)来区分,这三个关键字是 on、service、import。
1、on类型的section表示一系列命令的组合。当on后面的action被触发时执行这个section后面的所有命令。如am.QueueEventTrigger("charger")。on后面也可以是properties的变化再触发执行相关命令,如on property:sys.boot_from_charger_mode=1
2、service类型的section表示一个可执行程序
3、import类型的section表示引入另外一个.rc文件
init.rc实例:
import /init.environ.rcimport /init.usb.rcimport /init.${ro.zygote}.rcon early-init # Set init and its forked children's oom_adj. write /proc/1/oom_score_adj -1000 # Set the security context of /postinstall if present. restorecon /postinstall start ueventdon init sysclktz 0 # Mix device-specific information into the entropy pool copy /proc/cmdline /dev/urandom copy /default.prop /dev/urandomon property:security.perf_harden=1 write /proc/sys/kernel/perf_event_paranoid 3service ueventd /sbin/ueventd class core critical seclabel u:r:ueventd:s0 shutdown criticalinit.rc与inittab的关系
在原生linux中我们发现init进程是读取一个inittab的脚本而不是android中的init.rc。那这个脚本是干啥的呢?
inttab实例
::sysinit:/etc/init.d/rcS::respawn:-/bin/shtty2::askfirst:-/bin/sh::ctrlaltdel:/bin/umount -a -rinittab的作用:
inittab为linux初始化文件系统时init初始化程序用到的配置文件。这个文件负责设置init初始化程序初始化脚本?在哪里;每个运行级初始化时运行的命令;?开机、关机、重启对应的命令;各运行级登陆时所运行的命令
android简化了这部分处理直接使用init.rc脚本了。
android property和linux 环境变量property是android在标准linux基础之上新增的一种系统全局可访问的属性机制。和linux的环境变量机制相似,可看做是android系统的环境变量。
在android上层可以同时使用这两种机制
shell中查看linux中所有环境变量
env
getprop
阅读全文
1 0
- linux启动第一个应用程序init && init启动android过程解析 && init.rc与inittab的关系 && android property和linux 环境变量
- Android中init.rc文件的解析&&Android init进程启动过程分析
- MTD系列 - android平台上linux启动时init进程解析init.rc文件分析
- MTD系列 - android平台上linux启动时init进程解析init.rc文件分析
- 解析Android启动脚本init.rc && Android的启动模式
- Android 启动分析 init进程 init.rc
- Android系统init进程启动及init.rc全解析
- android init进程解析init.rc过程
- linux的开机启动过程(rc.local、init.d、rc.x、init的作用)
- Android启动脚本init.rc
- Android启动脚本init.rc
- Android启动脚本init.rc
- Android启动脚本init.rc
- Android启动脚本init.rc
- Android启动脚本init.rc
- android启动文件init.rc
- Android启动脚本init.rc
- Android启动脚本init.rc
- EF Code First 学习笔记:表映射
- iptables 详解(NAT)
- codevs 3983 调整(tweak)
- 读取web项目下text图片地址并在网页显示
- Android_广播机制总结
- linux启动第一个应用程序init && init启动android过程解析 && init.rc与inittab的关系 && android property和linux 环境变量
- js字符串类型的数字比较的大小
- activemq配置多个消费者
- 锁
- java客户端调用Memcached的三种方及mc的原理
- 复数型
- erlang 数据结构 -- queue
- 智能合约如何可信的与外部世界交互
- js中函数与变量名重名