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:
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?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?
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.
I've read that
signal()
is now deprecated, and to usesigaction()
. Are there any really good examples to show how to convert from the previoussignal()
call? I'm having trouble with the new structure that I have to create/pass and how it all fits together.Is the second call to
signal()
necessary?
Is there something similar that I need to be concerned with forsigaction()
?
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.
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.
Sigaction()
is POSIX while signal is a C standard.Signal()
works fine for me, but if you want any example : IBM ExampleFirst of all - if you don't know, whether you should handle any signals, then you probably don't. Signals require handling in some specific cases, like closing sockets, sending some message to other connected processes before exiting, or handling SIGPIPE signal from write() (and probably many more).
Secondly - this
while (!terminate) sleep(2);
is not very good - in worst case it might make user (or even system) impatient with having to wait 2s and send to your program a SIGKILL, which you can't handle.IMHO the best solution here would be using signalfd and select, so you can terminate your program without having to wait 2s.
For using
sigaction
instead, you could use a function such as this: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.
The flag
terminate
should bevolatile 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).The example below (and the link in the comments) can be helpful:
references:
sigaction()
nicely in "Chapter 11: Processes and Signals".*I started from 1, Presently I am reading 3 GNU-library
Why you set it to default-action before program termination is unclear to me. I think the following paragraph will give you an answer:
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 callsignal()
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.
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.
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).
No, there is a limitation! You can't catch all signals. Some signals are not catchable e.g.
SIGKILL
andSIGSTOP
and both are termination signals. Quoting one: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.