Since I was addicted to the implementation of signal (), I will leave the survey results as a memorandum. The survey environment is
I was refactoring legacy code that was created around the year I was born and modified and extended as the operating platform changed.
Some of them used the signal () system call. Looking at man of signal (), avoid using signal () because its behavior changes depending on the platform. , Sigaction () was used, so I modified it to use sigaction (), but it fits in here.
SIGNAL(2) Linux Programmer's Manual
name
signal -ANSI C signal manipulation
Description
signal()Behavior depends on the UNIX version.
It also historically depends on the version of Linux.
Avoid using this system call and instead sigaction(2)to use.
………
In conclusion, Linux signal () behaves differently depending on the compile options. Even if you call signal (), it is wrapped in glibc, and internally it calls the sigaction () system call. When calling this sigaction () system call, the value of sa_flags, which is an argument, differs depending on the compile option.
First, let's take a look at the internal implementation of the Linux kernel.
/*
* For backwards compatibility. Functionality superseded by sigaction.
*/
SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler)
{
struct k_sigaction new_sa, old_sa;
int ret;
new_sa.sa.sa_handler = handler;
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
sigemptyset(&new_sa.sa.sa_mask);
ret = do_sigaction(sig, &new_sa, &old_sa);
return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}
It should be noted that the flag specified in sa_flags is (SA_ONESHOT | SA_NOMASK). SA_ONESHOT means that sa_handler returns to SIG_DFL when the process receives a signal. SA_NOMASK means that when a process receives a signal and executes a handler, it receives the same signal again (that is, does not mask it). This is the behavior of System V signal (). (Unix has two major systems, System V and BSD, and it seems that the implementation is different in some places. I don't know in detail because I was the first person to touch Linux (OS other than Windows) in 2012. )
Therefore, if you are using Linux kernel ver.5.5.5, you may think that the specification of signal () is an implementation of System V, but as mentioned above, when using glibc, signal () in the kernel is Not called.
When calling a system call from C language, it basically calls the glibc wrapper function. Take a look at signal.h (/usr/include/signal.h), which is included when calling signal ().
/usr/include/signal.h
/* Set the handler for the signal SIG to HANDLER, returning the old
handler, or SIG_ERR on error.
By default `signal' has the BSD semantic. */
#ifdef __USE_MISC
extern __sighandler_t signal (int __sig, __sighandler_t __handler)
__THROW;
#else
/* Make sure the used `signal' implementation is the SVID version. */
# ifdef __REDIRECT_NTH
extern __sighandler_t __REDIRECT_NTH (signal,
(int __sig, __sighandler_t __handler),
__sysv_signal);
# else
# define signal __sysv_signal
# endif
#endif
It differs in the following three ways.
I will explain each.
By default, \ _ \ _USE \ _MISC is defined. You can see this by looking at /usr/include/features.h. The explanation of here was easy to understand.
/usr/include/signal.h
extern __sighandler_t signal (int __sig, __sighandler_t __handler)
__THROW;
signal () is declared extern and the entity is in glibc. The implementation is as follows.
c:glibc-2.28/sysdeps/posix/signal.c
sigset_t _sigintr attribute_hidden; /* Set by siginterrupt. */
__sighandler_t
__bsd_signal (int sig, __sighandler_t handler)
{
struct sigaction act, oact;
<Omitted>
act.sa_handler = handler;
__sigemptyset (&act.sa_mask);
__sigaddset (&act.sa_mask, sig);
act.sa_flags = __sigismember (&_sigintr, sig) ? 0 : SA_RESTART;
if (__sigaction (sig, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
weak_alias (__bsd_signal, signal)
It is weak_alias (\ _ \ _ bsd \ _signal, signal), and when signal () is called, \ _ \ _ bsd \ _signal () is called. This is BSD behavior, as you can see from the name \ _ \ _ bsd \ _signal (). This is consistent with the description "By default` signal'has the BSD semantic. "In the comment on the first line of /usr/include/signal.h.
What we pay attention to this time is the value specified for act.sa_flags. I think \ _ \ _ sigisempty () will return 0 (Is this anything other than 0?), So act.sa_flags is always SA_RESTART. In SA_RESTART, the handler is automatically re-registered after the process receives the signal and completes the handler processing. If a signal is received during system call execution, processing will continue, and if it was in the wait state, it will be in the wait state again, and no error will be returned by EINTR. As an exception, in the system call of socket family and msg family, when a signal is received, an error is returned by EINTR.
Before explaining the implementation of signal (), I will introduce the case where \ _ \ _USE \ _MISC is not defined.
For example, adding the -ansi option makes \ _ \ _USE \ _MISC undefined. You can check it with the following command.
$ echo "#include <signal.h>" | gcc -ansi -dM -E - | grep __USE_
#define __USE_FORTIFY_LEVEL 0
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
By the way, __USE_MISC is not defined even if -D_XOPEN_SOURCE is specified as an option.
$ echo "#include <signal.h>" | gcc -D_XOPEN_SOURCE -dM -E - | grep __USE_
#define __USE_FORTIFY_LEVEL 0
#define __USE_ISOC11 1
#define __USE_ISOC95 1
#define __USE_ISOC99 1
#define __USE_XOPEN 1
#define __USE_POSIX2 1
#define __USE_POSIX 1
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
#define __USE_POSIX_IMPLICITLY 1
On the other hand, if you do not specify -ansi or -D_XOPEN_SOURCE as an option as shown below, that is, by default, you can see that __USE_MISC is defined.
$ echo "#include <features.h>" | gcc -dM -E - | grep __USE_
#define __USE_FORTIFY_LEVEL 0
#define __USE_ISOC11 1
#define __USE_ISOC95 1
#define __USE_ISOC99 1
#define __USE_XOPEN2K 1
#define __USE_POSIX199506 1
#define __USE_POSIX2 1
#define __USE_XOPEN2K8 1
#define __USE_MISC 1
#define __USE_POSIX 1
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
#define __USE_POSIX199309 1
#define __USE_POSIX_IMPLICITLY 1
#define __USE_ATFILE 1
Note that \ _ \ _REDIRECT \ _NTH is defined regardless of the presence or absence of -ansi or -D_XOPEN_SOURCE.
The declaration of signal () when \ _ \ _USE \ _MISC is not defined and \ _ \ _ REDIRECT \ _NTH is defined is as follows.
/usr/include/signal.h
extern __sighandler_t __REDIRECT_NTH (signal,
(int __sig, __sighandler_t __handler),
__sysv_signal);
\ _ \ _REDIRECT \ _NTH is a macro that tells the compiler to use the symbol __sysv_signal instead of the symbol signal It seems to be -nth-do-in-unistd-h), so when you call signal (), glibc's __sysv_signal () is called.
The implementation of __sysv_signal () is as follows.
sysdeps/posix/sysv_signal.c
/* Set the handler for the signal SIG to HANDLER,
returning the old handler, or SIG_ERR on error. */
__sighandler_t
__sysv_signal (int sig, __sighandler_t handler)
{
struct sigaction act, oact;
<Omitted>
act.sa_handler = handler;
__sigemptyset (&act.sa_mask);
act.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT;
act.sa_flags &= ~SA_RESTART;
if (__sigaction (sig, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
weak_alias (__sysv_signal, sysv_signal)
Look at the value of sa_flags every time. SA_ONESHOT and SA_NOMASK are the same as signal () inside the kernel, but are also flagged as SA_INTERRUPT. This SA_INTERRUPT is not explained by man of sigaction, and when I look at glibc, it says / * Historical no-op. * /, So can I ignore it? I've carefully defeated the SA_RESTART bit, but I'm not sure. Is there an environment that has the same bits as SA_INTERRUPT? In the first place, SA_RESTART is a flag unique to the BSD extension, so I wonder if it is carefully defeated because it does not exist in the implementation of system v.
Since it is declared as follows in /usr/include/signal.h, you can see that __sysv_signal () is called when signal () is called regardless of the presence or absence of \ _ \ _REDIRECT \ _NTH. Does the use of \ _ \ _REDIRECT \ _NTH affect anything? I wasn't sure.
/usr/include/signal.h
# define signal __sysv_signal
When I was investigating the source code of glibc, the function with the same name was implemented in multiple places, and I was not sure which one was called. For the time being, I was looking at the following source code in consideration of my own environment.
Since the behavior of signal () changes depending on the compile option, the behavior changed when all the modifications to signal ()-> sigaction () were modified in the same way by brain death.
--Do not use signal () as it is less portable as in man. --When changing signal () to sigaction (), be aware of what the existing behavior of your application is.
We hope this article helps those who maintain and refactor their legacy code (and of course others).
Recommended Posts