Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Linux Signal Handling

Tags:

c++

c

linux

signals

I have a program that creates many threads and runs until either power is shutdown to the embedded computer, or the user uses kill or ctrlc to terminate the process.

Here's some code and how the main() looks.

static int terminate = 0;  // does this need to be volatile?  static void sighandler(int signum) { terminate = 1; }  int main() {   signal(SIGINT, sighandler);   // ...   // create objects, spawn threads + allocate dynamic memory   // ...   while (!terminate) sleep(2);   // ...   // clean up memory, close threads, etc.   // ...   signal(SIGINT, SIG_DFL);  // is this necessary? } 

I'm wondering a few things:

  1. Is any signal handling necessary?
    I read in this thread "Linux C catching kill signal for graceful termination", that apparently the OS will handle cleanup for me. Therefore, can I just replace the signal handler with just an infinite loop and let the OS gracefully exit the threads, de-allocate the memory, etc?

  2. Are there any other signals that I need to be concerned with regarding clean termination? This thread "How does SIGINT relate to the other termination signals?", was useful to list all the signals I may be concerned with, but how many actually required handling?

  3. Does the terminate variable in my example have to be volatile? I've seen many examples where this variable is volatile, and others where it is not.

  4. I've read that signal() is now deprecated, and to use sigaction(). Are there any really good examples to show how to convert from the previous signal() call? I'm having trouble with the new structure that I have to create/pass and how it all fits together.

  5. Is the second call to signal() necessary?
    Is there something similar that I need to be concerned with for sigaction()?

To be clear, all I'm trying to accomplish to to have my: main loop run until either ctrlc or power is disconnected or something really bad happens.

like image 401
It'sPete Avatar asked Jul 30 '13 08:07

It'sPete


People also ask

How signals are handled in Linux?

There are several methods of delivering signals to a program or script. One of the most common is for a user to type CONTROL-C or the INTERRUPT key while a script is executing. When you press the Ctrl+C key, a SIGINT is sent to the script and as per defined default action script terminates.

How do I create a signal handler in UNIX?

*/ . . /* set a signal handler for ALRM signals */ signal(SIGALRM, catch_alarm); /* prompt the user for input */ printf("Username: "); fflush(stdout); /* start a 30 seconds alarm */ alarm(30); /* wait for user input */ gets(user); /* remove the timer, now that we've got the user's input */ alarm(0); . . /* do something ...

How do you send signals in Linux?

Sending signals to foreground processesPressing Ctrl+C sends an Interrupt signal (SIGINT) to the process and the process terminates. Restart it. Pressing Ctrl+Z sends a STOP signal (SIGTSTP) to the process which kind of freezes/pauses the process and the shell prompt returns.


1 Answers

[Q-3] Does the terminate variable in my example have to be volatile? I've seen many examples where this variable is volatile, and others where it is not.

The flag terminate should be volatile sig_atomic_t:

Because handler functions can be called asynchronously. That is, a handler might be called at any point in the program, unpredictably. If two signals arrive during a very short interval, one handler can run within another. And it is considered better practice to declare volatile sig_atomic_t, this type are always accessed atomically, avoid uncertainty about interrupting access to a variable. volatile tells the compiler not to optimize and put it into register. (read: Atomic Data Access and Signal Handling for detail expiation).
One more reference: 24.4.7 Atomic Data Access and Signal Handling. Furthermore, the C11 standard in 7.14.1.1-5 indicates that only objects of volatile sig_atomic_t can be accessed from a signal handler (accessing others has undefined behavior).

[Q-4] I've read that signal() is now deprecated, and to use sigaction(). Are there any really good examples to show how to convert from the previous signal() call? I'm having trouble with the new structure that I have to create/pass and how it all fits together.

The example below (and the link in the comments) can be helpful:

// 1. Prepare struct  struct sigaction sa; sa.sa_handler =  sighandler;  // 2. To restart functions if interrupted by handler (as handlers called asynchronously) sa.sa_flags = SA_RESTART;   // 3. Set zero  sigemptyset(&sa.sa_mask);  /* 3b.   // uncomment if you wants to block   // some signals while one is executing.  sigaddset( &sa.sa_mask, SIGINT ); */   // 4. Register signals  sigaction( SIGINT, &sa, NULL ); 

references:

  1. Beginning Linux Programming, 4th Edition: in this book, exactly your code is explained with sigaction() nicely in "Chapter 11: Processes and Signals".
  2. The sigaction documentation, including an example (quick learning).
  3. The GNU C Library: Signal Handling
    *I started from 1, Presently I am reading 3 GNU-library

[Q-5] Is the second call to signal() necessary? Is there something similar that I need to be concerned with for sigaction()?

Why you set it to default-action before program termination is unclear to me. I think the following paragraph will give you an answer:

Handling Signals

The call to signal establishes signal handling for only one occurrence of a signal. Before the signal-handling function is called, the library resets the signal so that the default action is performed if the same signal occurs again. Resetting signal handling helps to prevent an infinite loop if, for example, an action performed in the signal handler raises the same signal again. If you want your handler to be used for a signal each time it occurs, you must call signal within the handler to reinstate it. You should be cautious in reinstating signal handling. For example, if you continually reinstate SIGINT handling, you may lose the ability to interrupt and terminate your program.

The signal() function defines the handler of the next received signal only, after which the default handler is reinstated. So it is necessary for the signal handler to call signal() if the program needs to continue handling signals using a non-default handler.

Read a discussion for further reference: When to re-enable signal handlers.

[Q-1a] Is any signal handling necessary?

Yes, Linux will do cleanup for you. For example if you don't close a file or a socket, Linux will do the cleanup after the process terminates. But Linux may not necessary perform the clean up immediately and it may take some time (may be to keep system performance high or some other issues). For example if you don't close a tcp-socket and the program terminates the kernel will not close the socket immediately to ensure all data has been transmitted, TCP guarantees delivery if possible.

[Q-1b] Therefore, can I just replace the signal handler with just an infinite loop and let the OS gracefully exit the threads, de-allocate the memory, etc?

No, operating system performs do clean-up only after program terminates. While a process executes, resources that are allocated to that process don't get claimed by the OS. (The OS can't know whether your process is in an infinite loop or not - this is an unsolvable problem). If you want that after process termination the OS performs the clean-up operations for you, then you don't need to handle signals (even in case your process abnormally terminated by a signal).

[Q] All I'm trying to accomplish to to have my: main loop run until either ctrlc or power is disconnected or something really bad happens.

No, there is a limitation! You can't catch all signals. Some signals are not catchable e.g. SIGKILL and SIGSTOP and both are termination signals. Quoting one:

— Macro: int SIGKILL

The SIGKILL signal is used to cause immediate program termination. It cannot be handled or ignored, and is therefore always fatal. It is also not possible to block this signal.

So you can't make a program that cannot be interrupted (an uninterrupted program)!

I am not sure but may be you can do something like this in Windows systems: by writing TSRs (some sort of kernel-mode hooking). I remember from my thesis time that some viruses couldn't be terminated even from task manager but I also believe that they trick user by admin permissions.

I hope this answer will help you.

like image 60
Grijesh Chauhan Avatar answered Sep 18 '22 21:09

Grijesh Chauhan