Simple Linux Signal Handling

21,386

Solution 1

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

Solution 2

For using sigaction instead, you could use a function such as this:

/*
 * New implementation of signal(2), using sigaction(2).
 * Taken from the book ``Advanced Programming in the UNIX Environment''
 * (first edition) by W. Richard Stevens.
 */
sighandler_t my_signal(int signo, sighandler_t func)
{
    struct sigaction nact, oact;

    nact.sa_handler = func;
    nact.sa_flags = 0;
    # ifdef SA_INTERRUPT
    nact.sa_flags |= SA_INTERRUPT;
    # endif
    sigemptyset(&nact.sa_mask);

    if (sigaction(signo, &nact, &oact) < 0)
        return SIG_ERR;

    return oact.sa_handler;
}

Solution 3

1. Is any signal handling necessary?

  • In the particular case of the link you posted, yes. The runs of softwares that concerns network need particular operations, as warning client of the close of a socket by example, in order to not disturb its run.
  • In your particular case, you don't need to handle any signal for the process to be clear gracefully : your OS will do it for you.

2. Are there any other signals that I need to be concerned with regarding clean termination?

  • First, take a look at his page : The GNU Library Signals The termination signals is what you look after. But take a look at SIGUSR1 and SIGUSR2, even if you 'll never find them in any software, except for debugging purposes.

  • All of this termination signals need to be handled if you don't want your soft to terminate all of a sudden.

3. Does the terminate variable in my example have to be volatile?

  • Absolutely not.

4. I've read that signal() is now deprecated, and to use sigaction()

  • Sigaction() is POSIX while signal is a C standard.

  • Signal() works fine for me, but if you want any example : IBM Example

Solution 4

It's not necessary to use signals for this. A standard terminate doesn't need to be caught. You may have reasons for catching it, but that would be down to your application rather than anything required by the O/S.

In terms of signals generally you should use sigaction not signal these days, it gets around a standardisation issue.

Signal handlers must be written to be reentrant. This does not require your terminate variable to be volatile but it might, depending on how you use it!

The W. Richard Stevens book "Advanced Programming in the UNIX Environment" has a good example of why and how to handle signals.

And no, you don't have to put back the default handler before your application terminates, your handler is only valid for your application, so if you're just killing off the app, it's not needed.

Share:
21,386
It'sPete
Author by

It'sPete

if (pete.had_coffee()) { pete.mood++; } else if (pete.in_staff_meeting()) { pete.mood = SAD_PANDA; } C/C++ code monkey. Interested in everything from embedded programming to high performance stuff on GPUs. Networking is also pretty cool, I guess... "Job satisfaction is the feeling you get right before you die of a heart attack." - Dilbert It's not a real code review until someone gets punched in the face. I think woodchucks are adorable. SOreadytohelp

Updated on May 23, 2020

Comments

  • It'sPete
    It'sPete almost 4 years

    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.

  • Hasturkun
    Hasturkun almost 11 years
    You might want to update that link to point to the current glibc documentation, gnu.org/software/libc/manual/html_node/…
  • Grijesh Chauhan
    Grijesh Chauhan almost 11 years
    @Hasturkun I would like add it also as I read from there I given in an answer. Thanks! updating link with new answer
  • hetepeperfan
    hetepeperfan almost 11 years
    If you give credits to the author, you might want to correct the typo in the book title.
  • Hasturkun
    Hasturkun almost 11 years
    Also, you probably want to always call sigemptyset(&sa.sa_mask), as otherwise its contents are uninitialized. (also, you don't need another variable for the set)
  • Karoly Horvath
    Karoly Horvath almost 11 years
    "The flag terminate should be volatile sigatomic_t" - well, for this simple example not necessarily. If you are happy waiting another 2 seconds to recognise the change then you can live without it (an even this scenario has a really low probability)
  • Grijesh Chauhan
    Grijesh Chauhan almost 11 years
    @KarolyHorvath Yes! is signals comes with time gaps we don't need it. Because its also good practice to write small handler.