c++信号处理

signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受 到了一定的限制。而 POSIX 标准定义的信号处理接口是 sigaction 函数。

signal函数每次设置具体的信号处理函数(非SIG_IGN)只能生效一次,每次在进程响应处理信号时,随即将信号处理函数恢复为默认处理方式.所以如果想多次相同方式处理某个信号,通常的做法是,在响应函数开始,再次调用signal设置。

1 int sig_int()
2 {
3     signal(SIGINT, sig_int);
4     ....
5 }

这种代码段的一个问题是:在信号发生之后到信号处理程序中调用s i g n a l函数之间有一个 时间窗口。在此段时间中,可能发生另一次中断信号。第二个信号会造成执行默认动作,而对 中断信号则是终止该进程。这种类型的程序段在大多数情况下会正常工作,使得我们认为它们 正确,而实际上却并不是如此。 另一个问题是:在进程不希望某种信号发生时,它不能关闭该信号。

sigaction:

1.在信号处理程序被调用时,系统建立的新信号屏蔽字会自动包括正被递送的信号。因此保证了在处理一个 给定的信号时,如果这种信号再次发生,那么它会被阻塞到对前一个信号的处理结束为止

2.响应函数设置后就一直有效,不会重置

http://man7.org/linux/man-pages/man2/sigaction.2.html

1 struct sigaction {
2    void     (*sa_handler)(int);
3    void     (*sa_sigaction)(int, siginfo_t *, void *);
4    sigset_t   sa_mask;
5    int        sa_flags;
6    void     (*sa_restorer)(void);
7 };

When the SA_SIGINFO flag is specified in act.sa_flags, the signal handler address is passed via the act.sa_sigaction field. This handler takes three arguments, as follows:

1 void handler(int sig, siginfo_t *info, void *ucontext)
2 {
3    ...
4 }

And the signal action (thru sa_sigaction field should do something (machine specific) with its siginfo_t to skip the offending machine instruction, or to mmap the offending address, or call siglongjmp, otherwise when returning from the signal handler you'll get the SIGSEGV again since the offending machine instruction is restarted.

When your signal handler returns (assuming it doesn't call exit or longjmp or something that prevents it from actually returning), the code will continue at the point the signal occurred, reexecuting the same instruction. Since at this point, the memory protection has not been changed, it will just throw the signal again, and you'll be back in your signal handler in an infinite loop.

https://stackoverflow.com/questions/2663456/how-to-write-a-signal-handler-to-catch-sigsegv

这是第一个问题,需要处理完之后,要么退出程序,要么就是进行错误处理之后需要跳过当前的代码。

At the time of generation, a determination shall be made whether the signal has been generated for the process or for a specific thread within the process. Signals which are generated by some action attributable to a particular thread, such as a hardware fault, shall be generated for the thread that caused the signal to be generated. Signals that are generated in association with a process ID or process group ID or an asynchronous event, such as terminal activity, shall be generated for the process.

The problematic aspect of trying to handle SIGSEGV in a multi-threaded program is that, while delivery and signal mask are thread-local, the signal disposition (i.e. what handler to call) is process-global. In other words, sigaction sets a signal handler for the whole process, not just the calling thread. This means that multiple threads each trying to setup their own SIGSEGV handlers will clobber each other's settings.

https://stackoverflow.com/questions/16204271/about-catching-the-sigsegv-in-multithreaded-environment

另一个问题,信号 的mask和delivery是thread local的,但是signal disposition不是。

SIGSEGV is catchable but the behavior after doing so is undefined unless the signal was generated by raise() or kill(). It sounds like you are trying to cover up bugs in your program -- the better solution is to find and squash these bugs so that you're not getting SIGSEGV in the first place. If the thread is executing code which is out of your direct control, you really should be running it in a separate process anyway.

The fundamental problem is that signals behave strangely under pthreads. Signals are not delivered to the thread which triggered them. Instead, they are delivered to whichever thread has the signal unblocked. If all threads have the signal unblocked, then any thread at all could receive the signal, not just the one which triggered it. In other words, pthread_self() may not be referring to the thread which caused the SIGSEGV.

There is no solution to this. In order to ensure that only the faulting thread receives the signal, it must be the only thread with this signal unblocked. But there is no way to accomplish that, because you do not know ahead of time which thread is about to segfault.

The best you can do is to block SIGSEGV in ALL threads but one, and when this thread receives the signal, simply shut everything down gracefully.

https://cboard.cprogramming.com/linux-programming/108364-question-about-pthreads-sigsegv.html

最好的方式还是直接退出。比如如果是写错内存,那么继续执行的话,可能会导致其他线程的结果也是错的。尽管此时程序并没有退出。

 http://www.apuebook.com/threadsig.html

apue上的例子,只是打印了哪个线程捕获了信号,并不能保证,捕获了信号的线程就是引起信号的线程。

原文地址:https://www.cnblogs.com/linyx/p/9674711.html