鸟人的Android揭秘(13)——Init进程源代码分析(四)

来源:互联网 发布:充电桩软件 编辑:程序博客网 时间:2024/05/18 01:29

      Android系统的文件节点都使用SELinux管理权限,前面介绍的init进程第一阶段初始化创建的文件节点,以及属性初始化过程创建的文件节点,是在加载sepolicy之前已经被创建了,在加载完sepolicy之后,需要重新设置相关的属性。代码3-13所示是根据file_contexts[1]文件的内容设置相应文件节点的上下文。

restorecon("/dev");restorecon("/dev/socket");restorecon("/dev/__properties__");restorecon("/property_contexts");restorecon_recursive("/sys");

代码 3-13 main()-设置SELinux访问权限

      接下来创建套接字,以便init进程在收到子进程终止的SIGCHLD信号时调用相应的handler。

epoll_fd = epoll_create1(EPOLL_CLOEXEC);if (epoll_fd == -1) {    ERROR("epoll_create1 failed: %s\n", strerror(errno));    exit(1);}signal_handler_init();

代码 3-14 main()-创建epoll和套接字

      代码3-14中,首先调用epoll_create1()函数创建epoll的句柄,通知内核开始监听文件描述符(file description,fd)。epoll_create1()函数创建成功时也会占用一个文件描述符,EPOLL_CLOEXE参数的作用是通知内核在使用完该文件描述符后需要关闭,以免文件描述符资源耗尽,紧接着调用signal_handler_init()函数创建套接字,安装信号处理器,并将套接字描述符注册到epoll中,如代码3-15所示。

void signal_handler_init() {    // Create a signalling mechanism for SIGCHLD.    int s[2];    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {   (1)        ERROR("socketpair failed: %s\n", strerror(errno));        exit(1);    }    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;       act.sa_flags = SA_NOCLDSTOP;    sigaction(SIGCHLD, &act, 0);                                                         (2)    register_epoll_handler(signal_read_fd, handle_signal);                               (3)}

代码 3-15 创建套接字,监听SIGCHLD事件

(1)调用socketpair()函数创建一对已经连接的非阻塞套接字,套接字描述符存储在二元数组s[2]中。这对套接字可以进行双工通信,每一个描述符既可以读也可以写。init进程使用s[0]作为输入端,s[1]作为输出端。

(2)安装子进程信号处理器。Linux进程通过互相发送/接收消息来实现进程间的通信,这些消息被称为“信号”。每个进程在处理其它进程发送的信号时,都要注册信号处理器。当进程的运行状态改变或终止时,就会产生某种信号,init进程是所有进程的父进程,当其子进程终止产生SIGCHLD信号时,init进程需要调用信号安装函数sigaction()并通过参数传递sigaction结构体配置,完成信号处理器的安装。此处sigaction结构体的sa_flags设置为SA_NOCLDSTOP,表示仅当进程终止时才接收SIGCHLD信号。当发生SIGCHLD信号时,调用此处的handler,即SIGCHLD_handler,实质是对套拼字signal_write_fd(s[0])执行写操作。

(3)将signal_read_fd(s[1])描述符和相应的handler注册到epoll中,当SIGCHLD信号产生时,signal_write_fd会被写入1,由于socketpair的绑定关系,这将触发信号对应的signal_read_fd收到数据,epoll监听到变化,调用此处注册的handler处理子进程的终止。

      上述init进程调用signal_handler_init()函数后的处理过程可以用图3-7总结,即一旦收到子进程终止触发的SIGCHLD信号后,将利用信号处理器SIGCHLD_handler向signal_write_fd写入信息,epoll句柄监听到signal_read_fd接收到信息,将调用handle_signal处理。关于子进程处理的更详细内容,我们将在后面“进程的终止和重启”章节中进得讲解。

图 3-7 main()-signal_handler_init()函数处理子进程终止的过程

图 3-7 main()-signal_handler_init()函数处理子进程终止的过程

      前面我们已经讲解了init进程如何初始化属性,在Android系统中,对属性的修改只能在init进程中进行,其它进程对属性只有读的权限,必须通过向init进程发送修改请求,由init接收请求后进行处理,接下来将进一步讲解这方面的内容。

 

[1] file_contexts的内容参考system/sepolicy/file_contexts。

0 0
原创粉丝点击