前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Framework分析(1)-init

Android Framework分析(1)-init

作者头像
老马的编程之旅
发布2022-06-23 14:09:13
9870
发布2022-06-23 14:09:13
举报
文章被收录于专栏:深入理解Android

分析基于28的源码,涉及源码:

代码语言:javascript
复制
system/core/init/
  - init.cpp
  - init_parser.cpp
  - signal_handler.cpp

init进程是Linux系统中用户空间的第一个进程,进程号固定为1。Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责。对于init进程的功能分为4部分:

1.解析并运行所有的init.rc相关文件 2.根据rc文件,生成相应的设备驱动节点 3.处理子进程的终止(signal方式) 4.提供属性服务的功能

接下来从main()方法说起。

main

代码语言:javascript
复制
int main(int argc, char** argv) {
	//第一次挂载
	if (is_first_stage){
		......
		//挂载和创建一系列文件夹
		// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
		// talk to the outside world...
		// 初始化Kernel日志
        InitKernelLogging(argv);
        LOG(INFO) << "init first stage started!";
	}
	// At this point we're in the second stage of init.
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";
	......
	//创建一块共享的内存空间,用于属性服务【见小节5.1】
	property_init();
	......
	//初始化epoll功能
	epoll_fd = epoll_create1(EPOLL_CLOEXEC);
	if (epoll_fd == -1) {
        PLOG(FATAL) << "epoll_create1 failed";
    }
	//初始化子进程退出的信号处理函数,并调用epoll_ctl设置signal fd可读的回调函数		
    sigchld_handler_init();
     //加载default.prop文件
    property_load_boot_defaults();
    //启动属性服务器,此处会调用epoll_ctl设置property fd可读的回调函数【见小节5.2】
    start_property_service();  
   	....... 
	ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
	//该方法内解析init.rc文件
    LoadBootScripts(am, sm);
    //one so we know ueventd has set up all of /dev...
    //执行rc文件中触发器为on early-init的语句
    am.QueueEventTrigger("early-init");
    //等冷插拔设备初始化完成
    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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    //设备组合键的初始化操作,此处会调用epoll_ctl设置keychord fd可读的回调函数
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
      // 屏幕上显示Android静态Logo 【见小节1.3】
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    //执行rc文件中触发器为on init的语句
    am.QueueEventTrigger("init");
	
	// 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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");

    // Don't mount filesystems or start core system services in charger mode.
    //当处于充电模式,则charger加入执行队列;否则late-init加入队列。
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
	
    while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;

        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }

        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            if (!shutting_down) {
            	//根据需要重启服务【见小节1.4】
                auto next_process_restart_time = RestartProcesses();

                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_restart_time) {
                    epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
                                           *next_process_restart_time - boot_clock::now())
                                           .count();
                    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;
}

看的LoadBootScripts方法内,逻辑就是去解析init.rc文件

代码语言:javascript
复制
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
		}
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

函数解释: 1.umask(0) 其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0777 & mode了,这样就是给后面的代码调用函数mkdir给出最大的权限,避免了创建目录或文件的权限不确定性。

2.strcmp strcmp() 用来比较字符串(区分大小写),其原型为: int strcmp(const char *s1, const char *s2);

【返回值】若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值。s1 若小于s2 则返回小于0 的值。

3.setenv Linux setenv命令用于查询或显示环境变量。

4.mount Linux mount命令是经常会使用到的命令,它用于挂载Linux系统外的文件。

5.mkdir 建立一个目录

6.emplace_back emplace, 比如如果你想要向 std::vector 的末尾添加一个数据,你可以:

代码语言:javascript
复制
std::vector<int> nums;
nums.push_back(1)

你也可以使用:

代码语言:javascript
复制
std::vector<int> nums;
nums.empace_back(1);

emplace 最大的作用是避免产生不必要的临时变量

log.cpp

代码语言:javascript
复制
void InitKernelLogging(char* argv[]) {
    // Make stdin/stdout/stderr all point to /dev/null.
    int fd = open("/sys/fs/selinux/null", O_RDWR);
    if (fd == -1) {
        int saved_errno = errno;
        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
        errno = saved_errno;
        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
    }
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
	if (fd > 2) close(fd);

    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
}

上述代码将stdin、stdout、stderr全部重定向到/sys/fs/selinux/null(黑洞)中了,下面我们继续分析InitLogging函数

logging.cpp部分源码

代码语言:javascript
复制
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
  SetLogger(std::forward<LogFunction>(logger));
  SetAborter(std::forward<AbortFunction>(aborter));
  ......
}

void SetLogger(LogFunction&& logger) {
  std::lock_guard<std::mutex> lock(LoggingLock());
  Logger() = std::move(logger);
}

void SetAborter(AbortFunction&& aborter) {
  std::lock_guard<std::mutex> lock(LoggingLock());
  Aborter() = std::move(aborter);
}

属性服务

现在我们来分析init进程很重要的一个功能属性服务 system/core/init/init.cpp

代码语言:javascript
复制
......
property_init();//初始化属性服务
......

在Android系统中,所有的进程共享系统设置值,为此提供一个名称为属性的保存空间。init进程调用property_init函数,在共享内存区域中创建并初始化属性域。而后通过执行进程所提供的API访问属性域中的设置值。但更改属性域时,要预先向init进程提交值变更申请,然后init进程处理该申请,并修改属性值(用propert_set()和property_get()来处理)

代码语言:javascript
复制
......
epoll_fd = epoll_create1(EPOLL_CLOEXEC);//创建轮询的描述符
......
start_property_service();//开启属性服务
......

epoll_create1用来创建轮询监听子进程终止和属性变更请求的文件描述符,后面会看到监听子进程终止套接字fd和属性变更请求套接字fd通过epoll_ctl注册到epoll_fd中,然后通过epoll_wait监听轮询这两个fd。

start_property_service()开启属性服务

代码语言:javascript
复制
void start_property_service() {
    ......
    //创建用于接收属性变更请求的socket (/dev/socket/property_service)
    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,false, 0666, 0, 0, nullptr);
    ......
    
    listen(property_set_fd, 8);//同时可以监听八个请求
    //注册property_set_fd到epoll_fd
    register_epoll_handler(property_set_fd, handle_property_set_fd);
}

如前所述,属性值的更改仅能在init进程中进行,即一个进程若想修改属性值,必须向init进程提交申请,为此init进程生成“/dev/socket/property_service”套接字,以接收其它进程提交的申请。

注册属性变更socket文件描述符(fd)到epoll_fd

system/core/init/init.cpp

代码语言:javascript
复制
void register_epoll_handler(int fd, void (*fn)()) {
    epoll_event ev;
    ev.events = EPOLLIN; //触发事件是当文件描述符可读时
    ev.data.ptr = reinterpret_cast<void*>(fn); //触发后的回调函数
    //注册属性socket fd到epoll_fd
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        PLOG(ERROR) << "epoll_ctl failed";
    }
}

register_epoll_handler函数主要的作用是注册属性socket文件描述符到轮询描述符epoll_fd,当property_set_fd可读时,会调用上面register_epoll_handler函数的第二个参数(处理函数handle_property_set_fd())

system/core/init/property_service.cpp

代码语言:javascript
复制
static void handle_property_set_fd() {
    ......
    uint32_t cmd = 0;
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {//读取命令
        return;
    }

    switch (cmd) {
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];

        //读取属性名和属性值
        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) || 
        	!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms))
        {
          return;
        }
        ......
        //设置属性值
        uint32_t result = HandlePropertySet(prop_name, prop_value,socket.source_context(), cr, &error);
        break;
      }
    case PROP_MSG_SETPROP2: 
    ......

当监听到属性变更时,从socket读取命令和属性名与属性值,设置新的属性或者更改属性值。HandlePropertySet最后会调用ActionManager::QueuePropetyChange将属性变更(属性触发器)添加到已触发队列中,最后会执行相应command命令(init.rc中定义的command)

处理子进程终止

现在我们来讲解init进程的另外一个重要功能,处理子进程终止。 system/core/init/init.cpp

代码语言:javascript
复制
......
sigchld_handler_init();//注册子进程死亡监听socket
......
system/core/init/sigchld_hanlder.cpp

```c
void sigchld_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    //创建套接字对,往一个socket中写,就可以从另外一个套接字中读取到数据
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
    }

    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;//收到SIGCHLD信号后的处理函数
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);//注册SIGCHLD信号

    ReapAnyOutstandingChildren();

    //注册signal_read_fd到epoll_fd
    register_epoll_handler(signal_read_fd, handle_signal);
}

当init子进程意外终止时,会向init进程发送SIGCHLD信号,这个时候会调用SIGCHLD_handler这个处理函数。

system/core/init/sigchld_hanlder.cpp

代码语言:javascript
复制
static void SIGCHLD_handler(int) {
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        PLOG(ERROR) << "write(signal_write_fd) failed";
    }
}

SIGCHLD_handler的作用是当init进程接收到SIGCHLD信号时,往signal_write_fd中写入数据,这个时候套接字对中的另外一个signal_read_fd就可读了。

通过register_epoll_handler(这个函数上面已经分析过了)将signal_read_fd注册到epoll_fd中,当signal_read_fd可读时,会调用register_epoll_handler的第二个参数(handle_signal处理函数)。

handle_signal()函数分析 system/core/init/sigchld_hanlder.cpp

代码语言:javascript
复制
static void handle_signal() {
    // 清空signal_read_fd中的数据
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    ReapAnyOutstandingChildren();//见下
}

void ReapAnyOutstandingChildren() {
    while (ReapOneProcess()) {//见下
    }
}

static bool ReapOneProcess() {
    ......
    //等待子进程终止
    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
    ......
    //获取到终止子进程对应的Service对象
    service = ServiceList::GetInstance().FindService(pid, &Service::pid);
    ......
    //处理退出后的后续事项
    service->Reap(siginfo);
    ......
}

收到子进程的SIGCHLD信号后,找出该进程对应的Service对象,调用Reap函数。

service->Reap处理退出后进程的事项,如下: system/core/init/service.cpp

代码语言:javascript
复制
void Service::Reap(const siginfo_t& siginfo) {
    //如果标志位是onshot或者是重启的子进程就杀掉整个进程组
    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
        KillProcessGroup(SIGKILL);
    }
    
    // Oneshot processes go into the disabled state on exit,
    // except when manually restarted.
    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
        flags_ |= SVC_DISABLED;
    }
    ......
    //后续Init进程重启子进程会通过这个标志位来判断是否需要重启该子进程
    flags_ |= SVC_RESTARTING;
    ......
    return;
}

当init进程收到子进程意外终止SIGCHLD信号后,会根据对应进程Service对象的flags_标志位来判断该进程能不能重启,如果需要重启,就给flags_标志位添加SVC_RESTARTING标志位。

RestartProcesses

/system/core/init/init.cpp

代码语言:javascript
复制
static std::optional<boot_clock::time_point> RestartProcesses() {
    std::optional<boot_clock::time_point> next_process_restart_time;
    for (const auto& s : ServiceList::GetInstance()) {
        if (!(s->flags() & SVC_RESTARTING)) continue;

        auto restart_time = s->time_started() + 5s;
        if (boot_clock::now() > restart_time) {
            if (auto result = s->Start(); !result) {
                LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
            }
        } else {
            if (!next_process_restart_time || restart_time < *next_process_restart_time) {
                next_process_restart_time = restart_time;
            }
        }
    }
    return next_process_restart_time;
}

检查service_list中的所有服务,对于带有SVC_RESTARTING标志的服务,则都会调用其相应的start()方法。

函数说明: 1.const auto& 当只想读取range中元素时,使用const auto&,如:for(const auto&x:range),它不会进行拷贝,也不会修改range

代码语言:javascript
复制
Result<Success> Service::Start() {
    bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
    // Starting a service removes it from the disabled or reset state and
    // immediately takes it out of the restarting state if it was in there.
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));


    if (flags_ & SVC_RUNNING) {
        if ((flags_ & SVC_ONESHOT) && disabled) {
            flags_ |= SVC_RESTART;
        }
        // It is not an error to try to start a service that is already running.
        return Success();
    }

    bool needs_console = (flags_ & SVC_CONSOLE);
    if (needs_console) {
        if (console_.empty()) {
            console_ = default_console;
        }

        // Make sure that open call succeeds to ensure a console driver is
        // properly registered for the device node
        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
        if (console_fd < 0) {
            flags_ |= SVC_DISABLED;
            return ErrnoError() << "Couldn't open console '" << console_ << "'";
        }
        close(console_fd);
    }

    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        flags_ |= SVC_DISABLED;
        return ErrnoError() << "Cannot find '" << args_[0] << "'";
    }
	........
    pid_t pid = -1;
     //如果该进程未启动,调用fork启动该进程
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

    if (pid == 0) {
        umask(077);
		......
        }

        // priority. Aborts on failure.
        SetProcessAttributes();

        if (!ExpandArgsAndExecv(args_)) {
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }

        _exit(127);
    }

    if (pid < 0) {
        pid_ = 0;
        return ErrnoError() << "Failed to fork";
    }
	.......

    time_started_ = boot_clock::now();
    pid_ = pid;
    flags_ |= SVC_RUNNING;
    start_order_ = next_start_order_++;
    process_cgroup_empty_ = false;
	.......
    
    NotifyStateChange("running");
    return Success();
}

其主要流程如下: 是否正在运行,如正在运行,直接返回; 子进程是否启动,如未启动,调用fork并返回pid值; 如果以上正常,则调用ExpandArgsAndExecve启动该进程,完成Zygote的启动。

信号处理

init.cpp的main()方法中通过signal_handler_init()来初始化信号处理过程。

主要工作: 初始化signal句柄; 循环处理子进程; 注册epoll句柄; 处理子进程的终止;

[-> signal_handler.cpp]

代码语言:javascript
复制
void sigchld_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    // 创建socket pair
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
    }

    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    //当捕获信号SIGCHLD,则写入signal_write_fd
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    //SA_NOCLDSTOP使init进程只有在其子进程终止时才会受到SIGCHLD信号
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);
	//进入waitpid来处理子进程是否退出的情况
    ReapAnyOutstandingChildren();
	//调用epoll_ctl方法来注册epoll的回调函数
    register_epoll_handler(signal_read_fd, handle_signal);
}

每个进程在处理其他进程发送的signal信号时都需要先注册,当进程的运行状态改变或终止时会产生某种signal信号,init进程是所有用户空间进程的父进程,当其子进程终止时产生SIGCHLD信号,init进程调用信号安装函数sigaction(),传递参数给sigaction结构体,便完成信号处理的过程。

这里有两个重要的函数:SIGCHLD_handler和handle_signal,如下:

sigchld_handler.cpp

代码语言:javascript
复制
//写入数据
static void SIGCHLD_handler(int) {
	//向signal_write_fd写入1,直到成功为止
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        PLOG(ERROR) << "write(signal_write_fd) failed";
    }
}
代码语言:javascript
复制
//读取数据
static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    //读取signal_read_fd中的数据,并放入buf
    read(signal_read_fd, buf, sizeof(buf));

    ReapAnyOutstandingChildren();
}

ReapAnyOutstandingChildren()

代码语言:javascript
复制
void ReapAnyOutstandingChildren() {
    while (ReapOneProcess()) {
    }
}

ReapOneProcess

代码语言:javascript
复制
static bool ReapOneProcess() {
    siginfo_t siginfo = {};
    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
    // It does NOT reap the pid; that is done below.
    //等待任意子进程,如果子进程没有退出则返回0,否则则返回该子进程pid。
    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
        PLOG(ERROR) << "waitid failed";
        return false;
    }

    auto pid = siginfo.si_pid;
    if (pid == 0) return false;

    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
    // whenever the function returns from this point forward.
    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
    // want the pid to remain valid throughout that (and potentially future) usages.
    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });

    std::string name;
    std::string wait_string;
    Service* service = nullptr;

    if (PropertyChildReap(pid)) {
        name = "Async property child";
    } else if (SubcontextChildReap(pid)) {
        name = "Subcontext";
    } else {
        service = ServiceList::GetInstance().FindService(pid, &Service::pid);

        if (service) {
            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
            if (service->flags() & SVC_EXEC) {
                auto exec_duration = boot_clock::now() - service->time_started();
                auto exec_duration_ms =
                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
            }
        } else {
            name = StringPrintf("Untracked pid %d", pid);
        }
    }

    if (siginfo.si_code == CLD_EXITED) {
        LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
    } else {
        LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
    }

    if (!service) return true;

    service->Reap(siginfo);

    if (service->flags() & SVC_TEMPORARY) {
        ServiceList::GetInstance().RemoveService(*service);
	 }

    return true;
}

重点逻辑在service->Reap(siginfo)

service.cpp

代码语言:javascript
复制
void Service::Reap(const siginfo_t& siginfo) {
	//当flags为RESTART,且不是ONESHOT时,先kill进程组内所有的子进程或子线程
    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
        KillProcessGroup(SIGKILL);
    }

	......
    // Oneshot processes go into the disabled state on exit,
    // except when manually restarted.
    //禁用和重置的服务,都不再自动重启
    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
        flags_ |= SVC_DISABLED;
    }

    // Disabled and reset processes do not get restarted automatically.
		//禁用和重置的服务,都不再自动重启
    if (flags_ & (SVC_DISABLED | SVC_RESET))  {
        NotifyStateChange("stopped");
        return;
    }

	    // If we crash > 4 times in 4 minutes, reboot into recovery.
		//服务在4分钟内重启次数超过4次,则重启手机进入recovery模式
    boot_clock::time_point now = boot_clock::now();
    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
        if (now < time_crashed_ + 4min) {
            if (++crash_count_ > 4) {
                LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
            }
        } else {
            time_crashed_ = now;
            crash_count_ = 1;
        }
    }

    flags_ &= (~SVC_RESTART);
    flags_ |= SVC_RESTARTING;

    // Execute all onrestart commands for this service.
     //执行当前service中所有onrestart命令
    onrestart_.ExecuteAllCommands();
	 //设置相应的service状态为restarting
    NotifyStateChange("restarting");
    return;
}

另外:通过getprop | grep init.svc 可查看所有的service运行状态。状态总共分为:running, stopped, restarting

register_epoll_handler

代码语言:javascript
复制
void register_epoll_handler(int fd, void (*fn)()) {
    epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.ptr = reinterpret_cast<void*>(fn);
    //将fd的可读事件加入到epoll_fd的监听队列中
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        PLOG(ERROR) << "epoll_ctl failed";
    }
}

当fd可读,则会触发调用(*fn)函数。

rc文件语法

前面说了初始化init.rc,那么就得知道rc文件的语法 rc文件语法是以行尾单位,以空格间隔的语法,以#开始代表注释行。rc文件主要包含Action、Service、Command、Options,其中对于Action和Service的名称都是唯一的,对于重复的命名视为无效。

Action

Action: 通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:

on early-init; 在初始化早期阶段触发; on init; 在初始化阶段触发; on late-init; 在初始化晚期阶段触发; on boot/charger: 当系统启动/充电时触发,还包含其他情况,此处不一一列举; on property:=: 当属性值满足条件时触发;

Service 服务Service,以 service开头,由init进程启动,一般运行在init的一个子进程,所以启动service前需要判断对应的可执行文件是否存在。init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。

例如: service servicemanager /system/bin/servicemanager代表的是服务名为servicemanager,服务执行的路径为/system/bin/servicemanager。

Command 下面列举常用的命令

class_start <service_class_name>: 启动属于同一个class的所有服务; start <service_name>: 启动指定的服务,若已启动则跳过; stop <service_name>: 停止正在运行的服务 setprop :设置属性值 mkdir :创建指定目录 symlink <sym_link>: 创建连接到的<sym_link>符号链接; write : 向文件path中写入字符串; exec: fork并执行,会阻塞init进程直到程序完毕; exprot :设定环境变量; loglevel :设置log级别

Options Options是Service的可选项,与service配合使用

disabled: 不随class自动启动,只有根据service名才启动; oneshot: service退出后不再重启; user/group: 设置执行服务的用户/用户组,默认都是root; class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default; onrestart:当服务重启时执行相应命令; socket: 创建名为/dev/socket/的socket critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

default: 意味着disabled=false,oneshot=false,critical=false。

init.rc文件的路径如下: /system/core/rootdir/init.rc 下面截取重要的代码段:

代码语言:javascript
复制
on early-init
on init
on late-init
    trigger early-fs
    trigger fs
    trigger post-fs  
    trigger late-fs
    trigger post-fs-data
    trigger zygote-start
    trigger load_persist_props_action
    trigger firmware_mounts_complete
	trigger early-boot
    trigger boot

on post-fs
    # Load properties from
    #     /system/build.prop,
    #     /odm/build.prop,
    #     /vendor/build.prop and
    #     /factory/factory.prop
    load_system_props
    # start essential services
    start logd
    start servicemanager
    start hwservicemanager
    start vndservicemanager
   	mount rootfs rootfs / remount bind ro
    # Mount shared so changes propagate into child namespaces
    mount rootfs rootfs / shared rec
    # Mount default storage into root namespace
    mount none /mnt/runtime/default /storage bind rec
    mount none none /storage slave rec
    ......

on boot      //启动核心服务
    ......
    class_start core //启动core class

触发器的执行顺序为on early-init -> init -> late-init,从上面的代码可知,在late-init触发器中会触发文件系统挂载以及on boot。再on boot过程会触发启动core class。至于main class的启动是由vold.decrypt的以下4个值的设置所决定的, 该过程位于system/vold/cryptfs.c文件。

解析并执行init.rc文件

system/core/init/init.cpp

代码语言:javascript
复制
//am中的actions_成员变量用来保存action list(动作列表)
ActionManager& am = ActionManager::GetInstance();
//sm中的service_成员变量用来保存service list(服务列表)
ServiceList& sm = ServiceList::GetInstance();

//解析rc文件,并将动作和服务分别添加到am和sm中
LoadBootScripts(am, sm);

LoadBootScripts解析rc文件,将动作和服务添加到am的动作列表和sm的服务列表中去。 system/core/init/init.cpp

代码语言:javascript
复制
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    //创建解析器
    Parser parser = CreateParser(action_manager, service_list);
    ......
    //解析init.rc文件
    parser.ParseConfig("/init.rc");
    ......
}

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

    //添加ServiceParser触发器
    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
    //添加ActionParser触发器
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    //添加ImportParser触发器
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
    //以键值对的形式添加到section_parsers_中
    section_parsers_[name] = std::move(parser);
}

分别将service(ServiceParser),on(ActionParser)和import(ImportParser)以键值对的形式添加到section_parsers_中。这三个Parser都继承自SectionParser,可以看到这里使用了组合和面向抽象编程的设计原则,将具体的解析器和解析行为解耦。下面会分析它们是如何使用相同的代码,不同的解析器解析不同的关键字(on,service,import等)。

现在分析ParseConfig("/init.rc")方法 system/core/init/parser.cpp

代码语言:javascript
复制
//ParseConfig->ParseConfigFile
bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
    ......
    //ReadFile将init.rc文件转换为config_contents字符串
    auto config_contents = ReadFile(path);
    ......
    //解析config_contents字符串
    ParseData(path, *config_contents, parse_errors);
    ......
}

void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
    std::vector<char> data_copy(data.begin(), data.end());
    data_copy.push_back('\0');

    parse_state state;
    .......
    state.ptr = &data_copy[0];
    ......
    std::vector<std::string> args;

    //lambda表达式,函数的引用,每次解析新的一行之前
    //都会调用这个将上一行解析的结果放到对应的数组中
    auto end_section = [&] {//⑤
        if (section_parser == nullptr) return;

        if (auto result = section_parser->EndSection(); !result) {
            (*parse_errors)++;
            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
        }

        //重置
        section_parser = nullptr;
        section_start_line = -1;
    };

    for (;;) {
        switch (next_token(&state)) { // ①
            case T_EOF:
                end_section();// ⑤ 上一行的解析工作
                return;
            case T_NEWLINE: // ⑦
                state.line++;
                ......
                if (section_parsers_.count(args[0])) {//②
                    end_section(); // ⑤ 上一行解析的收尾工作
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    auto result = section_parser->
                        ParseSection(std::move(args), filename, state.line); // ③
                } else if (section_parser) {
                    auto result = section_parser->
                        ParseLineSection(std::move(args), state.line); // ④
                args.clear();
                break;
            case T_TEXT: // ⑥
                args.emplace_back(state.text);
                break;
        }
    }
}

下面我们分析一下,ParseData是如何解析的: ①: next_token扫描init.rc中的token 找到其中的文件结束(EOF)、文本(TEXT)、新行(NEWLINE),其中的空格’ ‘、’\t’、’\r’会被忽略掉;对于TEXT,空格’ ‘、’\t’、’\r’、’\n’都是TEXT的结束标志。

例如:service ueventd /system/sbin/ueventd 中有三个TEXT (“servcie”、“ueventd”、"/system/sbin/") 这些都会加入到args[]数组中⑥,然后就是⑦解析args数组

②: 会判断args[]第一个字符是不是on、service或import 如果是就拿到对应的解析器,例如是service开头的新行,section_parser就是对应之前添加的ServcieParser解析器,并且执行③,否则就执行④ ③: 这里会执行ServiceParser解析器的ParseSection代码 会新建一个Service对象,变量名service_ ④: 这里会执行ServiceParser解析器的ParseLineSection代码 这里是解析service下面附带的option(例如oneshot,class等等),并将这些属性添加到Service对象中)

小提示: 如果args[0]是"on",section_parser对应的就是ActionParser;如果是"import",section_parser对应的就是ImportParser;本文只会分析ServiceParser,另外两个解析器,读者可以参照着分析。

⑤: 解析完一个Service对象后 end_section()中的section_parser->EndSection()将Service对象添加到Service List(services_)中,并且重置section_parser = null,为解析下一个Service做准备。

system/core/init/service.cpp 分析③的代码:

代码语言:javascript
复制
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
    const std::string& name = args[1];
    ......
    std::vector<std::string> str_args(args.begin() + 2, args.end());
    //新建一个Service对象,args[1]表示服务名,后面的是运行参数
    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
    return Success();
}

system/core/init/service.cpp 分析④的代码:

代码语言:javascript
复制
Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    return service_ ? service_->ParseLine(std::move(args)) : Success();
}

Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
    static const OptionParserMap parser_map;
    //找到service中option对应的函数
    auto parser = parser_map.FindFunction(args);

    if (!parser) return parser.error();

    //执行相应的函数,添加对应的属性到Service对象中
    return std::invoke(*parser, this, args);
}

下面我们看一下paser_map中option和函数名的对应关系。 system/core/init/service.cpp

代码语言:javascript
复制
static const Map option_parsers = {
        ......
        {"class",       {1,     kMax, &Service::ParseClass}},
        {"console",     {0,     1,    &Service::ParseConsole}},
        {"disabled",    {0,     0,    &Service::ParseDisabled}},
        ......
        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
        ......
        {"user",        {1,     1,    &Service::ParseUser}},
    };

这个我们分析oneshot为例,oneshot对应的函数是Service::ParseOneshot。

代码语言:javascript
复制
Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
    flags_ |= SVC_ONESHOT;
    return Success();
}

std::invoke调用这个函数后,会将SVC_ONESHOT添加到这个Service对象中,当这个服务意外终止时,init进程会根据这个标志位而不去重启这个Service。

system/core/init/service.cpp 分析⑤的代码

代码语言:javascript
复制
Result<Success> ServiceParser::EndSection() {
    if (service_) {
        ......
        //service_对象存在的时候,就添加到service list中
        service_list_->AddService(std::move(service_));
    }

    return Success();
}

void ServiceList::AddService(std::unique_ptr<Service> service) {
    //将服务添加到Service List(services_)中
    services_.emplace_back(std::move(service));
}

到这里一个服务就解析完成了,on和import依葫芦画瓢,可以自行分析。

on开头的使用ActionParser解析器解析,该解析器的ParseSection方法主要是生成action对象,并将action对象添加到actions_数组中,ParseLineSection方法主要是解析command对应的函数,然后添加到action对象的commands_数组中。细心的读者可能已经发现,ServiceParser的ParseLineSection是直接执行option对应函数,而ActionParser是将对应函数保存到commands_数组中,当Action触发时,才会依次执行command函数。

我们继续往下分析源码 system/core/init/init.cpp

代码语言:javascript
复制
// 解析init.rc文件,上面已经分析完
LoadBootScripts(am, sm);
......
// ① 触发early-init Action
am.QueueEventTrigger("early-init");

// ② 新建一个"wait_for_coldboot_done"触发器,并且触发该触发器
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
......
// ③ 触发"init",触发器
am.QueueEventTrigger("init");

①: 使用QueueEventTrigger方法触发early-init(还有一种触发方式是在rc文件中使用trigger命令,如trigger early-init) system/core/init/action_manager.cpp

代码语言:javascript
复制
void ActionManager::QueueEventTrigger(const std::string& trigger) {
    event_queue_.emplace(trigger);
}

event_queue_是一个保存以触发事件的队列,QueueEventTrigger(“early-init”)就表示触发了"early-init"事件 system/core/init/action_manager.h

代码语言:javascript
复制
std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;

system/core/init/action.h

代码语言:javascript
复制
using EventTrigger = std::string;
using PropertyChange = std::pair<std::string, std::string>;
using BuiltinAction = class Action*;

event_queue_可以存储三种触发器,EventTrigger(事件触发器,如on early-init),PropertyChange(属性触发器,如on property: = ),例如:属性服务监听到属性变更请求时会添加该触发器,BuiltinAction(内建触发器,代码实时添加的,不在rc文件中配置)

②:创建内置触发器,并且触发 system/core/init/action_manager.cpp

代码语言:javascript
复制
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
    //创建内置action对象
    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name, std::map<std::string, std::string>{});
    std::vector<std::string> name_vector{name};

    //添加func到action对象的commands_数组中
    action->AddCommand(func, name_vector, 0);

    //触发内置触发器
    event_queue_.emplace(action.get());
    //添加action对象到action list(actions_数组中)
    actions_.emplace_back(std::move(action));
}

③:触发事件触发器 on init

继续向下分析,就是依次执行已经触发的触发器action对象的command命令,并且监听属性服务和子进程的终止。 system/core/init/init.cpp

代码语言:javascript
复制
while (true) {
    ......
    // ① 
    am.ExecuteOneCommand();
    ......
    // ②
    auto next_process_restart_time = RestartProcesses();
    ......
    // ③
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
    }

①:依次执行已触发aciton的command命令 system/core/init/action_manager.cpp

代码语言:javascript
复制
void ActionManager::ExecuteOneCommand() {
    // Loop through the event queue until we have an action to execute
    while (current_executing_actions_.empty() && !event_queue_.empty())
    {
        for (const auto& action : actions_) {
            //如果该触发(event,就是前面说的三个触发器)匹配到当前action,就添加到current_excuting_actions_队列中
            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },event_queue_.front())) {
                current_executing_actions_.emplace(action.get());
            }
        }
        //从已触发事件弹出
        event_queue_.pop();
    }

    //如果没有可执行action,就直接返回
    if (current_executing_actions_.empty()) {
        return;
    }

    auto action = current_executing_actions_.front();
    ......
    //每次执行一个命令
    action->ExecuteOneCommand(current_command_);

    // If this was the last command in the current action, then remove
    // the action from the executing list.
    ++current_command_;//命令索引加一
    //如果是当前action最后一个命令,就从执行列表中移除action
    if (current_command_ == action->NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = 0;
        ......
        }
    }
}

从触发事件队列event_queue_中取出一个触发事件,从actions_中寻找匹配的action,然后添加到当前正在执行事件的队列current_executing_actions_中,取出aciton,依次执行action中commands_数组中的命令。遍历的执行上述操作,直至event_queue_和current_executing_actions_为空。

system/core/init/action.cpp

代码语言:javascript
复制
oid Action::ExecuteOneCommand(std::size_t command) const {
    Command cmd = commands_[command];
    //每次调用一个command命令
    ExecuteCommand(cmd);
}

void Action::ExecuteCommand(const Command& command) const {
    ......
    auto result = command.InvokeFunc(subcontext_);
    ......
}

②:重启需要重启的子进程 system/core/init/init.cpp

代码语言:javascript
复制
static std::optional<boot_clock::time_point> RestartProcesses() {
    ......
    for (const auto& s : ServiceList::GetInstance()) {
        //如果是需要重启的服务,会添加SVC_RESTARTING标志位,前面已经介绍过
        if (!(s->flags() & SVC_RESTARTING)) continue;

        ......
        //重启进程
        if (auto result = s->Start(); !result) {
            LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
        }
        ......
}

③:上面通过epoll_ctl注册了属性服务和子进程终止,现在通过epoll_wait来真正的监听。当有属性申请和子进程终止时,就执行前面分析的对应的处理函数。

总结

init进程(pid=1)是Linux系统中用户空间的第一个进程,主要工作如下:

创建一块共享的内存空间,用于属性服务器; 解析各个rc文件,并启动相应属性服务进程; 初始化epoll,依次设置signal、property、keychord这3个fd可读时相对应的回调函数; 进入无限循环状态,执行如下流程: 检查action_queue列表是否为空,若不为空则执行相应的action; 检查是否需要重启的进程,若有则将其重新启动; 进入epoll_wait等待状态,直到系统属性变化事件(property_set改变属性值),或者收到子进程的信号SIGCHLD,再或者keychord 键盘输入事件,则会退出等待状态,执行相应的回调函数。 可见init进程在开机之后的核心工作就是响应property变化事件和回收僵尸进程。当某个进程调用property_set来改变一个系统属性值时,系统会通过socket向init进程发送一个property变化的事件通知,那么property fd会变成可读,init进程采用epoll机制监听该fd则会 触发回调handle_property_set_fd()方法。回收僵尸进程,在Linux内核中,如父进程不等待子进程的结束直接退出,会导致子进程在结束后变成僵尸进程,占用系统资源。为此,init进程专门安装了SIGCHLD信号接收器,当某些子进程退出时发现其父进程已经退出,则会向init进程发送SIGCHLD信号,init进程调用回调方法handle_signal()来回收僵尸子进程。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • main
  • 属性服务
  • 处理子进程终止
  • RestartProcesses
  • 信号处理
  • rc文件语法
  • 解析并执行init.rc文件
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档