使用eventpoll+signalfd方式来处理signal
使用eventpoll+signalfd方式来处理signal
android init进程signal处理采用了eventpoll+signal的方式,本文解析一下这种方式是如何实现的
eventpoll+signalfd方式signal处理流程
1. 将SIGCHLD block
调用sigprocmask系统调用将SIGCHLD signal block,即将init进程的task_struct.blocked SIGCHLD对应bit设置为1。设置为blocked是为了防止原生的signal处理flow去处理此signal,
2. 创建signalfd file
signalfd file是anon file,file.f_op为signalfd_fops,file.private_data指向一个signalfd_ctx,这个structure里的sigmask成员保存着signalfd所关注的signal,在给这个成员赋值前有一个signotset(mask),将mask取反了,所以bit为0代表是所关注的signal,而为1表示不是所关注的signal(这种表示含义由next_signal()取出pending signal的方式所决定)
3. 注册signalfd file到eventpoll
调用epoll_ctl(EPOLL_CTL_ADD)系统调用注册signalfd file到eventpoll,epoll_event.events为EPOLLIN
4. 调用epoll_wait系统调用等待目标signal产生时从此系统调用返回,此时获取了signalfd所关注signal的epoll_event,再调用epoll_event.data所指向的callback函数,即HandleSignalFd()
5. 调用signalfd file read系统调用获取signalfd_siginfo
HandleSignalFd()调用了signalfd file的read函数,以获取signalfd_siginfo,比如此时读取到的signalfd_siginfo的ssi_signo成员是SIGCHLD,则调用ReapAnyOutstandingChildren(),而这个函数是调用了waitid系统调用获取是哪个子进程exit了
通过上述流程就实现了init进程处理子进程exit时发给自己的SIGCHLD signal,这个不同于原生的sigaction方式需要在user space注册SIGCHLD signal sa_handler,在SIGCHLD signal产生时再回调user space的sa_handler。
至于为什么要采用eventpoll+signalfd的方式来处理signal,这个是因为通过一个eventpoll就能监视多个file的epoll_event的发生,即通过一个epoll_wait系统调用即可监视多个被监视file的epoll_event的产生,具体参考我的另外一篇文章:
https://www.cnblogs.com/aspirs/p/15861763.html
* 某个signal被设置为blocked,该signal还是可以被发送给进程,只是在原生signal处理flow上就不会再去处理它了,这块具体参考我的另外一篇文章:
https://www.cnblogs.com/aspirs/p/15504105.html
注:
在InstallSignalFdHandler()里有为init进程对SIGCHLD signal调用sigaction,将此signal的sa_handler设置为了SIG_DFL,同时sa_flags为SA_NOCLDSTOP。
这个的目的是什么呢?
这个是因为对父进程来说,SIGCHLD signal在子进程stop(比如收到SIGSTOP signal)或者exit时均会给父进程发送,这里设置sa_handler为SIG_DFL、sa_flags带有SA_NOCLDSTOP,将禁止子进程在stop时给父进程发送SIGCHLD signal,code逻辑见do_notify_parent_cldstop()
所以对于init的子进程stop时,将不会给init进程发送SIGCHLD signal,所以signalfd将接收不到此类SIGCHLD signal,只会接收到子进程exit类的SIGCHLD signal
system/core/init/init.cpp
540 static void InstallSignalFdHandler(Epoll* epoll) { 541 // Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving 542 // SIGCHLD when a child process stops or continues (b/77867680#comment9). 543 const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP }; 544 sigaction(SIGCHLD, &act, nullptr); 545 546 sigset_t mask; 547 sigemptyset(&mask); 548 sigaddset(&mask, SIGCHLD); 549 550 if (!IsRebootCapable()) { 551 // If init does not have the CAP_SYS_BOOT capability, it is running in a container. 552 // In that case, receiving SIGTERM will cause the system to shut down. 553 sigaddset(&mask, SIGTERM); 554 } 555 556 if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) { 557 PLOG(FATAL) << "failed to block signals"; 558 } 559 560 // Register a handler to unblock signals in the child processes. 561 const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals); 562 if (result != 0) { 563 LOG(FATAL) << "Failed to register a fork handler: " << strerror(result); 564 } 565 566 signal_fd = signalfd(-1, &mask, SFD_CLOEXEC); 567 if (signal_fd == -1) { 568 PLOG(FATAL) << "failed to create signalfd"; 569 } 570 571 if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) { 572 LOG(FATAL) << result.error(); 573 } 574 }
注:
因为init进程将SIGCHLD信号block了,init fork子进程时,子进程的task_struct.blocked将直接从父进程copy过来,所以在子进程内调用UnblockSignals()将SIGCHLD signal unblock,所以子进程对于SIGCHLD signal的处理方式还是走原生处理流程:
526 static void UnblockSignals() { 527 const struct sigaction act { .sa_handler = SIG_DFL }; 528 sigaction(SIGCHLD, &act, nullptr); 529 530 sigset_t mask; 531 sigemptyset(&mask); 532 sigaddset(&mask, SIGCHLD); 533 sigaddset(&mask, SIGTERM); 534 535 if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) { 536 PLOG(FATAL) << "failed to unblock signals for PID " << getpid(); 537 } 538 }
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
* pthread_atfork()注册了回调函数,父进程fork子进程时将会回调这些callback:
parent:在父进程里执行此callback;
child:在子进程里执行此callback。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)