[LINUX] Handle signals in C

Introduction

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.

environment

The project uses Redhat Enterprise Linux 7, but this time we will use Cent OS 7.

About signals

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. ~~

Actually handle 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. ~~

Postscript

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.

Summary

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

Handle signals in C
Access MongoDB in C
Next Python in C
Handle markdown in python
Handle Parquet in Python
C API in Python 3
Extend python in C ++ (Boost.NumPy)
Machine language embedding in C language
Handle constants in Django templates
Heapsort made in C language
Use regular expressions in C
Handle environment variables in Python
Imitated Python's Numpy in C #
Binary search in Python / C ++
Minimum spanning tree in C #
Handle complex numbers in Python
Detect and process signals in Java.
How to handle session in SQLAlchemy
Write a table-driven test in C
Multi-instance module test in C language
Realize interface class in C language
ABC166 in Python A ~ C problem
Handle dates in Linux bash commands
Handle posix message queues in python
Handle NetCDF format data in Python
Handle GDS II format in Python
Handle requests in a separate process
When reading C ++ structs in Cython
Solve ABC036 A ~ C in Python
How to wrap C in Python
Solve ABC037 A ~ C in Python
Segfault with 16 characters in C language
Write C unit tests in Python
How to handle Japanese in Python
Handle multiple python versions in one jupyter
Linked list (list_head / queue) in C language
Solve ABC175 A, B, C in Python
Algorithm in Python (ABC 146 C Binary Search
Implement part of the process in C ++
Implement FIR filters in Python and C
How to use Google Test in C
Sample script to trap signals in Python
Write O_SYNC file in C and Python
I wrote the selection sort in C
How to handle consecutive values in MySQL
Generate C language from S-expressions in Python
Export xlsx file in C ++ using libxlsxwriter.
Run Python in C ++ on Visual Studio 2017
Communicate with I2C devices in Linux C