Pregunta

Estaba a punto de agregar un controlador de señal adicional a una aplicación que tenemos aquí y noté que el autor había usado sigaction () para configurar los otros controladores de señal. Iba a usar la señal () . Para seguir la convención, debería usar sigaction () , pero si estuviera escribiendo desde el principio, ¿cuál debería elegir?

¿Fue útil?

Solución

Use sigaction() unless you've got very compelling reasons not to do so.

The signal() interface has antiquity (and hence availability) in its favour, and it is defined in the C standard. Nevertheless, it has a number of undesirable characteristics that sigaction() avoids - unless you use the flags explicitly added to sigaction() to allow it to faithfully simulate the old signal() behaviour.

  1. The signal() function does not (necessarily) block other signals from arriving while the current handler is executing; sigaction() can block other signals until the current handler returns.
  2. The signal() function (usually) resets the signal action back to SIG_DFL (default) for almost all signals. This means that the signal() handler must reinstall itself as its first action. It also opens up a window of vulnerability between the time when the signal is detected and the handler is reinstalled during which if a second instance of the signal arrives, the default behaviour (usually terminate, sometimes with prejudice - aka core dump) occurs.
  3. The exact behaviour of signal() varies between systems — and the standards permit those variations.

These are generally good reasons for using sigaction() instead of signal(). However, the interface of sigaction() is undeniably more fiddly.

Whichever of the two you use, do not be tempted by the alternative signal interfaces such as sighold(), sigignore(), sigpause() and sigrelse(). They are nominally alternatives to sigaction(), but they are only barely standardized and are present in POSIX for backwards compatibility rather than for serious use. Note that the POSIX standard says their behaviour in multi-threaded programs is undefined.

Multi-threaded programs and signals is a whole other complicated story. AFAIK, both signal() and sigaction() are OK in multi-threaded applications.

Cornstalks observes:

The Linux man page for signal() says:

  The effects of signal() in a multi-threaded process are unspecified.

Thus, I think sigaction() is the only that can be used safely in a multi-threaded process.

That's interesting. The Linux manual page is more restrictive than POSIX in this case. POSIX specifies for signal():

If the process is multi-threaded, or if the process is single-threaded and a signal handler is executed other than as the result of:

  • The process calling abort(), raise(), kill(), pthread_kill(), or sigqueue() to generate a signal that is not blocked
  • A pending signal being unblocked and being delivered before the call that unblocked it returns

the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function defined in this standard other than one of the functions listed in Signal Concepts.

So POSIX clearly specifies the behaviour of signal() in a multi-threaded application.

Nevertheless, sigaction() is to be preferred in essentially all circumstances — and portable multi-threaded code should use sigaction() unless there's an overwhelming reason why it can't (such as "only use functions defined by Standard C" — and yes, C11 code can be multi-threaded). Which is basically what the opening paragraph of this answer also says.

Otros consejos

They're different interfaces for OS's signal facilities. One should prefer using sigaction to signal if possible as the signal() has implementation-defined (often race prone) behavior and behaves differently on Windows, OS X, Linux and other UNIX systems.

See this security note for details.

To me, this below line was enough to decide:

The sigaction() function provides a more comprehensive and reliable mechanism for controlling signals; new applications should use sigaction() rather than signal()

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

Whether you're starting from scratch or modifying an old program, sigaction should be the right option.

signal() is standard C, sigaction() is not.

If you're able to use either (that is, you're on a POSIX system), then use sigaction(); it's unspecified whether signal() resets the handler, meaning that to be portable you have to call signal() again inside the handler. What's worse is that there's a race: if you get two signals in quick succession, and the second is delivered before you reinstall the handler, you'll have the default action, which is probably going to be to kill your process. sigaction(), on the other hand, is guaranteed to use “reliable” signal semantics. You need not reinstall the handler, because it will never be reset. With SA_RESTART, you can also get some system calls to automatically restart (so you don't have to manually check for EINTR). sigaction() has more options and is reliable, so its use is encouraged.

Psst... don't tell anyone I told you this, but POSIX currently has a function bsd_signal() which acts like signal() but gives BSD semantics, which means it's reliable. Its main use is for porting old applications that assumed reliable signals, and POSIX does not recommend using it.

From the signal(3) man page:

DESCRIPTION

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

Both invoke the same underlying facility. You should presumably not manipulate the response the a single signal with both, but mixing them shouldn't cause anything to break...

I'd use signal() since it's more portable, in theory at least. I'll vote up any commenter who can come up with a modern system that doesn't have a POSIX compatibility layer and supports signal().

Quoting from the GLIBC documentation:

It's possible to use both the signal and sigaction functions within a single program, but you have to be careful because they can interact in slightly strange ways.

The sigaction function specifies more information than the signal function, so the return value from signal cannot express the full range of sigaction possibilities. Therefore, if you use signal to save and later reestablish an action, it may not be able to reestablish properly a handler that was established with sigaction.

To avoid having problems as a result, always use sigaction to save and restore a handler if your program uses sigaction at all. Since sigaction is more general, it can properly save and reestablish any action, regardless of whether it was established originally with signal or sigaction.

On some systems if you establish an action with signal and then examine it with sigaction, the handler address that you get may not be the same as what you specified with signal. It may not even be suitable for use as an action argument with signal. But you can rely on using it as an argument to sigaction. This problem never happens on the GNU system.

So, you're better off using one or the other of the mechanisms consistently within a single program.

Portability Note: The basic signal function is a feature of ISO C, while sigaction is part of the POSIX.1 standard. If you are concerned about portability to non-POSIX systems, then you should use the signal function instead.

Copyright (C) 1996-2008 Free Software Foundation, Inc.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

I would also suggest using sigaction() over signal() and would like to add one more point. sigaction() gives you more options such as pid of the process that died (possible using the siginfo_t struct).

From man page signal(7)

A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

And I would say this "issue" exists for signal(2) and sigaction(2). So be careful with signals and pthreads.

... and signal(2) seems to call sigaction(2) underneath in Linux with glibc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top