android -- init进程对信号的处理流程

上传人:第*** 文档编号:31311287 上传时间:2018-02-06 格式:DOC 页数:8 大小:59.50KB
返回 下载 相关 举报
android -- init进程对信号的处理流程_第1页
第1页 / 共8页
android -- init进程对信号的处理流程_第2页
第2页 / 共8页
android -- init进程对信号的处理流程_第3页
第3页 / 共8页
android -- init进程对信号的处理流程_第4页
第4页 / 共8页
android -- init进程对信号的处理流程_第5页
第5页 / 共8页
点击查看更多>>
资源描述

《android -- init进程对信号的处理流程》由会员分享,可在线阅读,更多相关《android -- init进程对信号的处理流程(8页珍藏版)》请在金锄头文库上搜索。

1、Android - Init 进程对信号的处理流程在 Android 中,当一个进程退出( exit())时,会向它的父进程发送一个 SIGCHLD 信号。父进程收到该信号后,会释放分配给该子进程的系统资源;并且父进程需要调用 wait()或waitpid()等待子进程结束。如果父进程没有做这种处理,且父进程初始化时也没有调用signal(SIGCHLD, SIG_IGN)来显示忽略对 SIGCHLD 的处理,这时子进程将一直保持当前的退出状态,不会完全退出。这样的子进程不能被调度,所做的只是在进程列表中占据一个位置,保存了该进程的 PID、终止状态、CPU 使用时间等信息;我们将这种进程称为

2、“Zombie”进程,即僵尸进程。在 Linux 中,设置僵尸进程的目的是维护子进程的一些信息,以供父进程后续查询获取。特殊的,如果一个父进程终止,那么它的所有僵尸子进程的父进程将被设置为 Init 进程(PID 为 1) ,并由 Init 进程负责回收这些僵尸进程(Init 进程将 wait()/waitpid()它们,并清除它们在进程列表中的信息) 。由于僵尸进程仍会在进程列表中占据一个位置,而 Linux 所支持的最大进程数量是有限的;超过这个界限值后,我们就无法创建进程。所以,我们有必要清理那些僵尸进程,以保证系统的正常运作。接下来,我们分析下 Init 进程是如何处理 SIGCHLD

3、 信号的。在 Init.cpp 中,我们是通过 signal_handler_init()来初始化 SIGCHLD 信号处理的:cpp view plain copy 在 CODE 上查看代码片派生到我的代码片void signal_handler_init() / Create a signalling mechanism for SIGCHLD. int s2; /socketpair()创造一对未命名的、相互连接的 UNIX 域套接字 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) = -

4、1) ERROR(socketpair failed: %sn, strerror(errno); exit(1); signal_write_fd = s0; signal_read_fd = s1; / Write to signal_write_fd if we catch SIGCHLD. struct sigaction act; memset( act.sa_handler = SIGCHLD_handler;/设置信号处理函数句柄,当有信号产生时,会向上面创建的 socket 写入数据 ,epoll 监控到该 socket 对中的 fd 可读时,就会调用注册的函数去处理该事件 a

5、ct.sa_flags = SA_NOCLDSTOP;/设置标志,表示只有当子进程终止时才接受 SIGCHID信号 sigaction(SIGCHLD, /初始化 SIGCHLD 信号处理方式 reap_any_outstanding_children();/处理这之前退出的子进程 register_epoll_handler(signal_read_fd, handle_signal); 我们通过 sigaction()函数来初始化信号。在 act 参数中,指定了信号处理函数:SIGCHLD_handler();如果有信号到来,就会调用该函数处理;同时,在参数 act 中,我们还设置了 SA

6、_NOCLDSTOP 标志,表示只有当子进程终止时才接受 SIGCHLD 信号。Linux 中,信号是一种软中断,所以信号的到来会终止当前进程正在处理的操作。所以,我们在注册的信号处理函数中不要调一些不可重入的函数。并且,Linux 不会对信号做排队处理,在一个信号的处理期间不管再收到多少个信号,当前信号处理完毕后,内核也只会再发送一个信号给进程;所以这里就存在信号丢失的可能。为了避免丢失信号,我们注册的信号处理函数操作应该越高效、越快越好。而我们处理 SIGCHLD 信号时,父进程会做等待操作,这个时间是比较长的。为了解决这个问题,上面的信号初始化代码中创建了一对未命名且相关联的本地 soc

7、ket 用于线程间通信。注册的信号处理函数是 SIGCHLD_handler():cpp view plain copy 在 CODE 上查看代码片派生到我的代码片static void SIGCHLD_handler(int) if (TEMP_FAILURE_RETRY(write(signal_write_fd, 1, 1) = -1) ERROR(write(signal_write_fd) failed: %sn, strerror(errno); cpp view plain copy 在 CODE 上查看代码片派生到我的代码片#define TEMP_FAILURE_RETRY(

8、exp) ( decltype(exp) _rc; do _rc = (exp); while (_rc = -1 _rc; ) 当有信号到来时,只要向 socket 中写入数据,这个过程是很快的,此时信号的处理就转移到 socket 的响应中去进行了;这样就不会影响下一个信号的处理。同时,write()函数外围嵌套了一个 do.while 循环,循环条件是 write()发生错误且当前的错误号为EINTR(EINTR :此调用被信号所中断) ,即当前 write()是由于有中断到来而发生错误时,操作将再次执行;其他情况下,write()函数只会执行一次。再初始化完信号处理后,就会调用 rea

9、p_any_outstanding_children() 处理这之前的进程退出情况:cpp view plain copy 在 CODE 上查看代码片派生到我的代码片static void reap_any_outstanding_children() while (wait_for_one_process() wait_for_one_process()主要调用 waitpid()等待子进程结束,当该进程代表的服务需要重启时,会对它做一些设置、清理工作。最后,通过 epoll_ctl()向 epoll_fd 注册本地 socket,监听其是否可读;并注册了 epoll 事件的处理函数:cpp

10、 view plain copy 在 CODE 上查看代码片派生到我的代码片register_epoll_handler(signal_read_fd, handle_signal); cpp view plain copy 在 CODE 上查看代码片派生到我的代码片void register_epoll_handler(int fd, void (*fn)() epoll_event ev; ev.events = EPOLLIN;/对文件描述符可读 ev.data.ptr = reinterpret_cast(fn);/保存指定的函数指针 ,用于后续的事件处理 if (epoll_ctl(

11、epoll_fd, EPOLL_CTL_ADD, fd, &ev) = -1) /向 epoll_fd 添加要监听的 fd,比如 property、keychord 和 signal 事件监听 ERROR(epoll_ctl failed: %sn, strerror(errno); 我们以 Zygote 进程退出为例,来看下 SIGCHLD 信号处理的具体流程。 Zygote 进程在init.rc 中被声明为 Service 并由 Init 进程创建。当 Zygote 进程退出时,将向 Init 进程发送SIGCHLD 信号。前面的代码已经完成了信号的初始化操作,所以当信号到来时会调用SIG

12、CHLD_handler()函数处理,它的处理就是直接通过 socket 写入一个数据就立刻返回;这时,SIGCHLD 的处理就转移到 socket 事件的响应上。我们通过 epoll_ctl 注册了本地socket,并监听它是否可读;这时由于之前的 write()调用,此时 socket 有数据可读,此刻会调用注册的 handle_signal()函数进行处理:cpp view plain copy 在 CODE 上查看代码片派生到我的代码片static void handle_signal() / Clear outstanding requests. char buf32; read(s

13、ignal_read_fd, buf, sizeof(buf); reap_any_outstanding_children(); 它会将 socket 的数据的独到 buf 中,并调用 reap_any_outstanding_children()函数处理子进程的退出及服务的重启操作:cpp view plain copy 在 CODE 上查看代码片派生到我的代码片static void reap_any_outstanding_children() while (wait_for_one_process() cpp view plain copy 在 CODE 上查看代码片派生到我的代码片

14、static bool wait_for_one_process() int status; pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, /等待子进程结束,并获取到它的 pid 进程号,WNOHANG 表明若没有进程结束,则立即返回. if (pid = 0) return false; else if (pid = -1) ERROR(waitpid failed: %sn, strerror(errno); return false; service* svc = service_find_by_pid(pid);/根据 pid,在链表中找到这个服

15、务信息 std:string name; if (svc) name = android:base:StringPrintf(Service %s (pid %d), svc-name, pid); else name = android:base:StringPrintf(Untracked pid %d, pid); NOTICE(%s %sn, name.c_str(), DescribeStatus(status).c_str(); if (!svc) return true; / TODO: all the code from here down should be a member function on service. /如果该服务进程没有设定 SVC_ONESHOT 标志,或者设置了 SVC_RESTART

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 中学教育 > 其它中学文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号