安卓底层开发学习经验第十四期

这一期我们来看一下 init 进程是如何守护我们的服务
我们 init 进程在守护阶段做的工作有三个,第一个是启动我们的服务,执行我们脚本的命令,第二个是接受 shell 或系统中的消息,来设置我们系统的 prop ,第三个就是守护我们的系统服务,如果服务退出,那么就会根据服务的设置状态重启推出的服务
下面我们来看一下 init 进程是如何处理消息与守护服务的

这就我们init 进程与其他进程进行交互的一个大概示意图,首先我们 init 进程会创建两个套接字,一个是 PROP_SERVICE_NAME ,它主要用于监听其他进程所发送的消息,这个消息主要是设置我们的环境变量,第二个套接字是设置信号操作函数,主要用于监听子进程退出的函数,它主要用于我们的子进程服务和 init 进程进行通讯,当我们把这两个套接字创建完之后呢,我们就在 for 循环中监听这两个 scoket ,我们监听分为两种,一种是设置我们的 prop ,另一个是监听我们子进程是否退出,在 setprop 这个事件中,我们分为两种情况,第一种是在 shell 下,通过 setprop 操作,或者 start stop class 这些操作来向我们的 PROP_SERVICE_NAME 来发送消息,第二个是在代码中通过 setprop 这个函数向我们的 init 进程发送消息,当我们的进程通过 PROP_SERVICE_NAME 收到消息之后就会做消息的解析,然后根据我们消息设置的格式去设置我们的环境变量,或者重启我们的服务,停止我们的服务,或者启动我们的服务,这个是 prop 处理消息的一个过程。
下面我们再来看一下我们的子进程也就是我们的服务异常退出的一个过程,当我们的服务退出之后,我们的子进程就会截获到我们子进程所获得的信号,向我们的进程创建的 scoket 发送消息,我们的 init 进程截获到这个消息之后,就会根据退出服务的一个 pid ,找到我们 service 列表中所对应的服务,把这个服务重新启动,这个是我们 init 进程守护我们服务的一个过程。
下面我们来看一下我们的 setprop 的一个具体操作过程

首先我们会在init 进程中创建一个 scoket, 这个 scoket 就是 PROP_SERVICE_NAME ,然后我们在 shell 下,通过 start stop restart class 向我们 scoket 发送消息,我们也可以使用我们的 setprop 向我们的 scoket 发送消息,而且我们也可以在我们的应用中调用我们的函数向 scoket 发送消息,当我们的 init 进程通过 scoket 收到消息之后,就会调用 handle_property_set_fd() 这个函数来处理消息,处理方式分为两种,第一是 prop 环境变量的设置,第二个就是我们服务的一个处理。
下面我们来看一下代码的具体实现过程,首先打开 init.c ,找到我们的 property_service_init_action 函数,他是以 action 的方式添加到我们的 queue_builtin_action 中的

他在这里主要是调用的是start_property_service ()
他首先会创建一个PROP_SERVICE_NAME scoket ,他是一个本地 scoket ,创建完之后他就把这个 scoket 赋给了一个全局变量 property_set_fd ,这时我们的 init 进程就可以通过监听这个 scoket 来收其他进程所发送的消息。那我们来看一下这个消息的具体处理过程

在这里会通过Poll 对我们的 scoket 进行监听,如果在这里收到一个 handle_property_set_fd 的消息,他首先会把这个消息接收下来,接收下来之后会做一个处理,如果说我们这个消息是 PROP_MSG_SERPROP ,他就会分为两种方式进行处理,第一种是处理我们的 handle_control_message ,我们来看一下他都做了哪些事情

它主要处理三种消息,分别是start stop restart start 后面要跟我们 service 的一个名字,在 msg_start 中其实传进来的其实就是我们服务的名称,然后首先找到我们服务的节点,然后通过我们的 service_start 来把这个服务给启动,另外两种也是相同的处理方式
最后我们来看一下 init 进程是如何守护我们的服务的

在init 进程中有一个 signal_init_action 的函数,它主要是设置我们信号的一个处理函数,这个信号也就是我们的 SIGCHLD ,然后我们子进程退出的时候就会触发这个设置的函数,这个函数是我们的 sigchld_handler (),同时也会在这里创建两个 scoket ,分别是 signal_fd signal_recv_fd ,当我们创建完之后,我们的 init 进程就会在这里监听我们的 signal_recv_fd ,当 init 进程去处理 action_queue 创建服务的时候,我们这个服务的子进程会继承所有父进程的所有资源,包括我们信号量的设置,以及我们创建的两个 scoket ,这样我们通过两个 scoket 就可以使我们的子进程和父进程进行通讯,当我们的子进程异常退出的时候,我们会处理 SIGCHLD 这个信号,而这个信号量就会触发我们的 sigchld_handler 函数,这个函数所做的事情就是通过 signal_fd 向我们的父进程 signal_recv_fd 发送消息,发送完消息,我们的 init 进程就会知道有一个子进程退出了,这时候我们就可以通过 wait_for_one_process 来处理我们退出的服务,并重启服务。
下面我们来看一下代码的具体实现,在我们 init.rc 中我们会创建一个 signal_init_action 的一个操作节点,我们会把这个节点添加到 action_queue 中,下面我们来看一下 signal_init_action 的一个具体实现

他首先调用的是一个signal_init 的一个函数我们来看一下这个函数

这个函数首先会调用我们的sigaction 这个函数,来设置 SA_NOCLDSTOP 这个信号的处理方式,当我们这个进程捕获到这个信号时,就会调用我们的 sigchld_handler 来处理这个信号,第二个就是他会调用 socketpair 这个函数,来创建一对未命名的相互连接的套接字,通过这个创建我们可以得到两个文件描述符,一个是 S0 、一个是 S1 ,我们分别将他们赋值到 signal_fd signal_recv_fd 中,这样我们就有了一对可以通讯的套接字,如果我们创建了一个子进程,那么这个子进程也会继承这对套接字,这时我们就可以通过这对套接字来进行父子进程之间的通讯。 Sigchld_handler 他的实现就是向我们的 signal_fd 中写一个消息,写完之后我们的父进程就会收到这个消息,并对这个消息进行处理。
我们再来看一下 init 进程如何处理子进程的消息,如果说我们无使用 Poll 这个函数等到了子进程发送的消息,我们就会调用 handle_signal 这个函数,来处理我们的消息

读到之后我们就会在这里等wait_for_one_process

首先我们会调用waitpid 来回收我们的子进程,如果说没有子进程退出那我们就直接返回,如果说有的话,那么我们就会根据进程的 pid 查找到我们 service 列表中所对应的 service 节点,然后将 service 的一些资源清理掉,清理完之后我们会在最后调用 notify_service_state 把这个服务重新启动,这个就完成了一个子进程异常退出到重启的一个过程。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值