I will describe the history until I handle the abort signal as a C language beginner (newcomer training level).
I would be grateful if you could point out any points in the description or code.
The project uses Redhat Enterprise Linux 7, but this time we will use Cent OS 7.
Signals are one of the interprocess communications used in UNIX and Linux. It's hard to imagine when you hear it at a glance, but it is used when you want to stop a program or command on the terminal or when you want to generate a specific event. This also applies to trying to stop a running command or program with ** Ctrl + C **.
There are two types of signals handled by Linux: ** standard signals ** and ** real-time signals **. In addition, there are 32 signals for each, which means that there are a total of 64 signals. At this time, the standard signals are assigned numbers 1 to 32, and the real-time signals are assigned numbers 33 to 64. I think that the signals you often hear about Linux are the standard signals ** SIGINT ** and ** SIGKILL **.
For details on signals, refer to Man page of SIGNAL.
This time, I would like to describe based on ~~ SIGABRT ~~ SIGINT.
For more information about ~~ SIGABRT, please refer to Man page of ABORT. ~~
There are two ways to catch a signal in C, the signal
function and the sigaction
function. First of all, I would like to confirm with signal
.
The code actually written is as follows.
test_signal.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
volatile sig_atomic_t e_flag = 0;
void abrt_handler(int sig);
int main() {
printf("start %s\n", __func__);
if ( signal(SIGINT, abrt_handler) == SIG_ERR ) {
exit(1);
}
while (!e_flag) {}
printf("end %s\n", __func__);
return 0;
}
void abrt_handler(int sig) {
e_flag = 1;
}
Execution result
[root@ca035c198d1f work]# ./signal_test.o
start main
^Chandle signal : 2
end main
The following is the NG code before receiving your indication. I will leave the description as an NG sample.
test_signal.c(NG)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf buf;
void abrt_handler(int sig);
int main() {
printf("start %s\n", __func__);
// SIG_Set the handle of ABRT.
//If the handle fails, the process ends.
if ( signal(SIGABRT, abrt_handler) == SIG_ERR ) {
exit(1);
}
//Set the return point after the signal handle.
//Prevent abort from being called again after returning.
if ( setjmp(buf) == 0 ) {
printf("publish abort\n");
abort();
}
printf("end %s\n", __func__);
return 0;
}
//Signal handle function
void abrt_handler(int sig) {
printf("handle signal : %d\n", sig);
//After displaying the message, it returns to setjmp.
longjmp(buf, 1);
}
The execution result is as follows.
Execution result
[root@ca035c198d1f work]# ./signal_test.o
start main
publish abort
handle signal : 6
end main
As you can see from the code, it's not that difficult.
Define the signal you want to handle in the signal
function and specify the function in the second argument. That way, ʻabrt_handler will be executed when SIGABRT occurs. In addition, the generated signal number is entered in the first argument when ʻabrt_handler
is called. This time, "6" is entered.
~~ Since abort is issued this time, if you return to the same place after handling the signal, the process will end as it is. Therefore, we use longjmp
to prevent the process from terminating by not passing through the same place. ~~
Since you pointed out the longjmp
running in the signal handler, we have corrected it.
Regarding longjmp
in the signal handler, JPCERT CC has the following description.
Calling the longjmp () function from within a signal handler can result in undefined behavior and compromise program integrity. Therefore, neither longjmp () nor POSIX siglongjmp () should be called from within the signal handler.
I found that the above code can handle the signal, but the signal handle using the singal
function is not recommended.
As mentioned in the Man page of SIGNAL, it says that you should avoid the less portable signal
and use sigaction
. Details can be found in the Man page of SIGNAL.
Now, let's try the implementation with sigaction
.
The code actually written is as follows.
sigaction_test.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
void abrt_handler(int sig, siginfo_t *info, void *ctx);
volatile sig_atomic_t eflag = 0;
int main() {
printf("start %s\n", __func__);
struct sigaction sa_sigabrt;
memset(&sa_sigabrt, 0, sizeof(sa_sigabrt));
sa_sigabrt.sa_sigaction = abrt_handler;
sa_sigabrt.sa_flags = SA_SIGINFO;
if ( sigaction(SIGINT, &sa_sigabrt, NULL) < 0 ) {
exit(1);
}
while ( !eflag ) {}
printf("end %s\n", __func__);
return 0;
}
void abrt_handler(int sig, siginfo_t *info, void *ctx) {
// siginfo_It is displayed by printf to check if the value of t has been acquired.
//Originally, printf is not asynchronously safe and should not be used here.
printf("si_signo:%d\nsi_code:%d\n", info->si_signo, info->si_code);
printf("si_pid:%d\nsi_uid:%d\n", (int)info->si_pid, (int)info->si_uid);
eflag = 1;
}
The execution result is as follows.
Execution result
[root@ca035c198d1f work]# ./sigaction_test.o
start main
^Csi_signo:2
si_code:128
si_pid:0
si_uid:0
end main
The following is the NG code before receiving your indication. I will leave the description as an NG sample.
sigaction_test.c(NG)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <string.h>
jmp_buf buf;
void abrt_handler(int sig, siginfo_t *info, void *ctx);
int main() {
printf("start %s\n", __func__);
//Settings for sigaction
struct sigaction sa_sigabrt;
memset(&sa_sigabrt, 0, sizeof(sa_sigabrt));
sa_sigabrt.sa_sigaction = abrt_handler;
sa_sigabrt.sa_flags = SA_SIGINFO;
// SIG_Set the handle of ABRT.
//If the handle fails, the process ends.
if ( sigaction(SIGABRT, &sa_sigabrt, NULL) < 0 ) {
exit(1);
}
//Set the return point after the signal handle.
//Prevent abort from being called again after returning.
if ( setjmp(buf) == 0 ) {
printf("publish abort\n");
abort();
}
printf("end %s\n", __func__);
return 0;
}
void abrt_handler(int sig, siginfo_t *info, void *ctx) {
printf("si_signo:%d\nsi_code:%d\n", info->si_signo, info->si_code);
printf("si_pid:%d\nsi_uid:%d\n", (int)info->si_pid, (int)info->si_uid);
//After displaying the message, it returns to setjmp.
longjmp(buf, 1);
}
The execution result is as follows.
Execution result
[root@ca035c198d1f work]# start main
publish abort
si_signo:6
si_code:-6
si_pid:79
si_uid:0
end main
^C
[1]+ Done ./sigaction_test.o
As you can see in the code, it is set more often than the signal
function. For sigaction
, the function to be executed at the time of handle and sa_flags
are set. The function settings are roughly the same as signal
, but sa_flags
can be set to various values. This time, I set SA_SIGINFO
so that I can get the information when the signal was sent.
I think that the argument of the function to be executed at the time of signal handle is different from that at the time of signal
. This time, we set SA_SIGINFO
to sa_flags
, so siginfo_t
, which stores the information, will be the argument. At the time of handle, you can see that the process ID at runtime etc. can be obtained from * info
of siginfo_t
.
There is also ctx
, but this time I have not investigated it so far.
It was a rough sketch, but now I can handle the signal. This time, we are only doing it for abort, but we can do the same for other signals.
Regarding the contents described this time, I am honestly not confident about the manners for signal, so if you have any suggestions or advice such as I am writing this, please let me know.
Recommended Posts