其他
我是init进程
https://blog.csdn.net/niurenwo?type=blog
{
struct task_struct *tsk;
int pid;
rcu_scheduler_starting();
//kernel_thread方法在内核创建一个进程,创建完毕会返回值为1的pid,并且会执行 kernel_init 方法
pid = kernel_thread(kernel_init, NULL, CLONE_FS); //niu kernel 开始创建init进程
省略代码......
//同样调用kernel_thread方法在内核创建一个进程,pid为2,并且会执行 kthreadd 方法
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
省略代码......
}
static int __ref kernel_init(void *unused)
{
省略代码......
//依次执行下面目录的init程序(init程序就是个so库),哪个执行成功就退出
[1.2]
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;
省略代码......
}
[1.2]
static int try_to_run_init_process(const char *init_filename)
{
省略代码......
[1.3]
ret = run_init_process(init_filename);
省略代码......
return ret;
}
[1.3]
static int run_init_process(const char *init_filename)
{
省略代码......
//执行这个函数后,init进程进入用户空间运行
return do_execve(getname_kernel(init_filename),
(const char __user *const __user *)argv_init,
(const char __user *const __user *)envp_init);
}
//A子进程的配置信息
A进程在创建前需要提前执行的一些动作
A进程的名字
A进程对应的二进制可执行文件路径
//B子进程的配置信息
B进程在创建前需要提前执行的一些动作
B进程的名字
B进程对应的二进制可执行文件路径
......
其他子进程的配置信息
配置子进程基础信息:这一步主要用来配置子进程的基础信息,比如子进程的名字、可执行文件路径等,init进程就可以立马明白是哪个子进程被创建 配置触发条件:主要配置子进程何时或者满足什么条件的情况下被创建,因为不同子进程的创建条件都是不一样的,因此init进程可以从这一步得知是在“什么时候”或者“什么条件满足”的时候来创建子进程 配置前置命令:主要配置子进程在创建之前需要执行一些提前操作或者提前执行的命令,比如有的子进程在创建之前需要提前创建一些目录等操作 配置创建子进程命令:这一步非常的简单,init进程遇到这个命令,就开始执行创建子进程的操作
# pathname:可执行文件路径
# argument:可执行文件的main方法被执行的时候,参数会传递到main方法
# option:其他的一些配置信息,比如子进程是否可重启等
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
on 触发条件
# 下面是一些简单的例子
# 在init启动时候做哪些事情
on init
# 在init启动前期做哪些事情
on early-init
# 在init启动后期做哪些事情
on late-init
on 触发条件
command1
command2
......
# 例子
on post-fs
# 调用exec执行对应操作
exec - system system -- /system/bin/vdc checkpoint markBootAttempt
# mount操作
mount rootfs rootfs / remount bind ro nodev
# 创建目录操作
mkdir /cache/recovery 0770 system cache
on 触发条件
start servicename
service servicename <pathname> [ <argument> ]*
<option>
<option>
...
# 在xxx/init.rc 脚本文件中引入上面的脚本文件,并配置对应的触发条件和前置命令
import xxx/xx.rc
on 触发条件
command1
command2
...
start servicename
# 下面配置了servicemanager子进程
# servicemanager是子进程的名字
# /system/bin/servicemanager 是可执行文件的路径
# onrestart critical等是其他的配置项
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
onrestart restart apexd
onrestart restart audioserver
onrestart restart gatekeeperd
onrestart class_restart main
onrestart class_restart hal
onrestart class_restart early_hal
writepid /dev/cpuset/system-background/tasks
shutdown critical
# 在init阶段触发 copy、symlink等这些命令后,开始创建servicemanager子进程
on init
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /system/etc/prop.default /dev/urandom
symlink /proc/self/fd/0 /dev/stdin
symlink /proc/self/fd/1 /dev/stdout
symlink /proc/self/fd/2 /dev/stderr
省略代码......
# 下面命令代表创建servicemanager子进程
start servicemanager
on property:perf.drop_caches=3
write /proc/sys/vm/drop_caches 3
setprop perf.drop_caches 0
on init && property:ro.debuggable=1
start console
on userspace-reboot-resume
trigger userspace-reboot-fs-remount
trigger post-fs-data
trigger zygote-start
trigger early-boot
trigger boot
# boot条件达成的时候,开始执行下面的各种命令
on boot
# basic network init
ifup lo
hostname localhost
domainname localdomain
# IPsec SA default expiration length
write /proc/sys/net/core/xfrm_acq_expires 3600
# Memory management. Basic kernel parameters, and allow the high
# level system server to be able to adjust the kernel OOM driver
# parameters to match how it is managing things.
write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
# System server manages zram writeback
chown root system /sys/block/zram0/idle
省略其他的配置......
on init
# 会触发 property:ro.debuggable=1 的命令执行
setprop ro.debuggable 1
# 属性类型的触发条件,property:ro.debuggable=1的时候开始执行它的命令
on property:ro.debuggable=1
mkdir xxxx
......
class Service {
// servicename
const std::string name_;
//配置的classname
std::set<std::string> classnames_;
unsigned flags_;
//子进程创建成功后会保存它的pid
pid_t pid_;
//子进程开始运行的时间
android::base::boot_clock::time_point time_started_; // time of last start
//子进程崩溃的时间
android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
//子进程崩溃的次数
int crash_count_;
省略其他的属性......
}
class ServiceList {
//存储Service实例
private:
std::vector<std::unique_ptr<Service>> services_;
bool post_data_ = false;
std::vector<std::string> delayed_service_names_;
省略其他代码......
}
class Action {
private:
//property类型的触发条件
std::map<std::string, std::string> property_triggers_;
//event类型的触发条件
std::string event_trigger_;
//包含的所有命令
std::vector<Command> commands_;
//为true 则代表子进程死掉的话就不会被重新创建;否则重新创建
bool oneshot_;
省略其他属性......
}
class ActionManager {
private:
//所有的Action实例
std::vector<std::unique_ptr<Action>> actions_;
省略其他属性......
}
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
//创建脚本解析器
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
//若ro.boot.init_rc属性没有设置具体的init.rc文件,则进入下面逻辑
if (bootscript.empty()) {
//解析 /system/etc/init/hw/init.rc 文件,会把on关键字解析为Action对象,会把service关键字解析为Service对象
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
总结
首先子进程在以.rc的脚本文件中,使用service关键字来配置子进程相关的信息 其次 在init.rc文件中(init.rc文件到底是在哪个目录这个是不确定的)使用import关键字引入脚本文件,使用on关键字来配置子进程的触发条件 触发条件配置完毕后,如若子进程在创建之前需要配置一些前置操作或命令,则基于触发条件下配置这些信息 最后使用start关键字来配置创建子进程的命令。
监听子进程死掉
首先我先使用sigaction函数来注册SIGCHLD信号,这样就可以监听到子进程的状态了 其次使用signalfd函数为SIGCHLD信号生成一个fd(文件描述符) 再次使用epoll来见监听上一步生成的fd是否有可读数据 如监听到fd上有可读数据,则证明子进程的状态发生了变化,还需要使用waitpid函数来获取是哪个子进程死掉了
//初始化sigaction,SIG_DFL:代表使用默认的信号处理行为。
const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
//注册SIGCHLD信号
sigaction(SIGCHLD, &act, nullptr);
//声明mask信号集
sigset_t mask;
//初始化并清空一个信号集,使其不包含任何信号
sigemptyset(&mask);
//把SIGCHLD信号加入mask信号集中
sigaddset(&mask, SIGCHLD);
省略代码......
//SIG_BLOCK:代表将mask添加到当前的信号屏蔽集中
if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
PLOG(FATAL) << "failed to block signals";
}
// Register a handler to unblock signals in the child processes.
//在子进程创建成功后,恢复SIGCHLD为非屏蔽
const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
if (result != 0) {
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
//调用signalfd函数为mask生成一个fd
signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
if (signal_fd == -1) {
PLOG(FATAL) << "failed to create signalfd";
}
constexpr int flags = EPOLLIN | EPOLLPRI;
//使用epoll来监听signal_fd上的数据
if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
LOG(FATAL) << result.error();
}
}
//如果fd上有数据就会调用这个方法
static void HandleSignalFd() {
//读取到siginfo信息
signalfd_siginfo siginfo;
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
if (bytes_read != sizeof(siginfo)) {
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
return;
}
//判断当前的ssi_signo
switch (siginfo.ssi_signo) {
case SIGCHLD:
//只看SIGCHLD
ReapAnyOutstandingChildren();
break;
省略无关代码......
}
}
system/core/init/sigchld_handler.cpp
void ReapAnyOutstandingChildren() {
while (ReapOneProcess() != 0) {
}
}
static pid_t ReapOneProcess() {
siginfo_t siginfo = {};
//调用waitpid方法来获取死掉的子进程的信息
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
PLOG(ERROR) << "waitid failed";
return 0;
}
const pid_t pid = siginfo.si_pid;
if (pid == 0) {
DCHECK_EQ(siginfo.si_signo, 0);
return 0;
}
省略无关代码......
}
//调用SystemProperties的set方法可以把 key value存储起来
SystemProperties.set(key,value);
//调用SystemProperties的get方法可以把key对应的valu值获取到
SystemProperties.get(key)
//命令行也可以设置和获取key value
//获取root权限后,进入shell
adb root
adb shell
//设置 xxx.xx 为value
setprop xxx.xx value
//获取xxx.xx的值
getprop xxx.xx
若是关机或重启的消息,则执行关机或重启操作,后面的操作不会在执行,关机或重启的优先级最高的 若不是关机或重启的状态,则调用ActionManager的ExecuteOneCommand方法 若不是关机或重启的状态,若有control类型的message,则把对应的control message传递给对应的子进程
关机或重启
在低电量情况下,PowerManagerService服务(这个服务位于systemserver进程)的对应方法lowLevelShutdown或者lowLevelReboot方法被调用,这俩方法都会调用SystemProperties的set方法来为sys.powerctl的key设置‘shutdown’ + reason或者‘reboot’ + reason 属性服务会监听到sys.powerctl的property属性信息,把这个属性信息保存下来,在每次循环的时候都会检查是否有关机或重启的属性信息,有的话就开始执行关机或重启操作。
调用ActionManager的ExecuteOneCommand方法
int SecondStageMain(int argc, char** argv) {
省略代码......
//触发器触发 "early-init" Action开始执行
am.QueueEventTrigger("early-init");
省略代码......
//触发器触发 "init" Action开始执行
am.QueueEventTrigger("init");
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
//触发器触发 "late-init” Action开始执行
am.QueueEventTrigger("late-init");
}
}
处理control类型的message
adb shell
//启动开机动画子进程,在屏幕上会显示开机动画
setprop ctl.start bootanim
"ctl.sigstop_on":代表在启动的时候发送SIGTOP信号 "ctl.sigstop_off":与sigstop_on相反 "ctl.oneshot_on":代表子进程死掉的时候需要重新启动 "ctl.oneshot_off"与oneshot_on相反 "ctl.start":创建子进程 "ctl.stop":停止子进程 "ctl.restart":先停止子进程在创建子进程
小结
是否有关机或重启的消息,有的话执行关机或重启 调用ActionManager的ExecuteOneCommand方法,ActionManager会检查是否有触发器,有的话触发对应的Action执行,有些Action会包含创建子进程的start命令,根据start命令后面的servicename,开始创建对应的子进程 若有control类型的message,则把它交给对应的Service