这一期我们来看一下
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
把这个服务重新启动,这个就完成了一个子进程异常退出到重启的一个过程。