Use eventpoll+signalfd to process signals
signal processing flow in eventpoll+signalfd mode
1. Set SIGCHLD to block
Call sigprocmask, and the system call will SIGCHLD signal block, that is, the task of init process_ struct. The corresponding bit of blocked SIGCHLD is set to 1. Set to blocked to prevent the native signal processing flow from processing this signal,
2. Create signalfd file
Signalfd file is an anon file, file f_ OP is signalfd_fops,file.private_data points to a signalfd_ctx, the sigmask member in this structure holds the signal concerned by signalfd. Before assigning a value to this member, there is a signotset(mask), which reverses the mask, so bit 0 represents the signal concerned, while bit 1 represents not the signal concerned (the meaning of this representation is determined by the way next_signal() takes out the pending signal)
3. Register signalfd# file to eventpoll
Call epoll_ctl(EPOLL_CTL_ADD) system calls to register signalfd # file to eventpoll, epoll_event.events is EPOLLIN
4. Call epoll_ The wait system call returns from this system call when waiting for the target signal to be generated. At this time, the epoll of the signal concerned by signalfd is obtained_ Event, and then call epoll_event. The callback function pointed to by data, that is, HandleSignalFd()
5. Call signalfd file read system call to get signalfd_siginfo
HandleSignalFd() called the read function of signalfd file to get signalfd_siginfo, such as the signalfd read at this time_ SSI of siginfo_ If the Signo member is SIGCHLD, call realanyoutstandingchildren(), and this function calls waitid system call to get which child process exit s
Through the above process, the sigcld signal sent to itself by the init process when processing the child process exit is realized. This is different from the original sigaction method. Sigcld signal SA needs to be registered in user space_ Handler, which calls back the Sa of user space when SIGCHLD} signal is generated_ handler.
As for why we use the method of eventpoll+signalfd to process signals, this is because the epoll of multiple files can be monitored through one eventpoll_ Event occurs through an epoll_wait system call can monitor epoll of multiple monitored files_ For the generation of event, please refer to another article:
https://www.cnblogs.com/aspirs/p/15861763.html
*If a signal is set to blocked, it can still be sent to the process, but it will not be processed on the native signal processing flow. For details, please refer to another article:
https://www.cnblogs.com/aspirs/p/15504105.html
Note:
In InstallSignalFdHandler(), you can call sigaction for the init process on SIGCHLD # signal, and the Sa of this signal_ Set handler to SIG_DFL, simultaneous sa_flags is SA_NOCLDSTOP.
What is the purpose of this?
This is because for the parent process, sigcld , signal will be sent to the parent process when the child process stops (such as receiving SIGSTOP , signal) or exit s. SA is set here_ Handler is SIG_DFL,sa_flags with SA_NOCLDSTOP will prohibit the child process from sending sigcld # signal to the parent process when stopping. See do for code logic_ notify_ parent_ cldstop()
Therefore, when the child process of init stop s, sigcld signal will not be sent to the init process, so signalfd will not receive such sigcld signal, but only sigcld signal of the child process exit class
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 }
Note:
Because the init process blocks the SIGCHLD signal, when the init fork subprocess, the task of the subprocess_ struct. Blocked will copy directly from the parent process, so calling UnblockSignals() in the child process will unblock sigcld # signal. Therefore, the child process still follows the native processing process for sigcld # 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() registers the callback function. When the parent process forks the child process, it will call back these callbacks:
Parent: execute this callback in the parent process;
Child: execute this callback in the child process.