If I want to do something special i.e. delete temp files, can I provide those files as an argument to this handler?

Edit 0: Thanks for the answers. We generally avoid/discourage use of global variables. And in this case, If you have a huge program, things can go wrong at different places and you might need to do a lot of cleanup. Why was the API designed this way?

4 Answers
4

You can't have data of your own passed to the signal handler as parameters. Instead you'll have to store your parameters in global variables. (And be really, really careful if you ever need to change those data after installing the the signal handler).

Response to edit 0: Historical reasons. Signals are a really old and really low-level design. Basically you're just given the kernel a single address to some machine code and asking it to go to this specific address if such and such happens. We're back in the "portable assembler" mindset here, where the kernels provide a no-frills baseline service, and whatever the user process can reasonably be expected to to for itself, it must do itself.

Also, the usual arguments against global variables don't really apply here. The signal handler itself is a global setting, so there is no relevant possibility of having several different sets of user-specified parameters for it around. (Well, actually it is not entirely global but only thread-global. But the threading API will include some mechanism for thread-local storage, which is just what you need in this case).

Signal handlers are not per-thread. Changing the handler for a signal applies to all threads.
–
R..Aug 7 '11 at 2:51

Right. But it still makes sense to want the signal handler to react to which thread it is handling for, which is what TLS can achieve.
–
Henning MakholmAug 7 '11 at 2:56

1

Actually it's unclear to me whether accessing TLS from a signal handler is valid. With glibc/NPTL it's almost surely not valid in general, because a signal could be delivered immediately after the thread is created but before control is passed to the start function, while the thread's copy of TLS is still being initialized. If you're careful to have signals blocked at this time, it may be okay, but it's a dubious practice still...
–
R..Aug 7 '11 at 2:59

By the way, even without TLS, there's one way to identify which thread the signal handler is running from in an async-signal-safe way: &errno is unique for each thread! However, I believe this may not be safe in glibc/NPTL for the same reason I just described. If it's not, I'm pretty sure that's a conformance bug, but it's probably a sufficiently rare race to go undetected...
–
R..Aug 7 '11 at 4:00

A signal handler registration is already a global state equivalent to global variables. So it's no greater offense to use global variables to pass arguments to it. However, it's a huge mistake (almost certainly undefined behavior unless you're an expert!) to do anything from a signal handler anyway. If you instead just block signals and poll for them from your main program loop, you can avoid all these issues.

Thanks for the response. I am not entirely sure what you are suggesting here. Can you please elaborate? My concern is, lets say I have 5 functions in a program and something wrong can happen in any of them, how can I avoid having them declare global variables to take care of clean up?
–
hariAug 7 '11 at 3:11

2

Unless you're an expert with signals, it's very dangerous to do anything from a signal handler. Instead you could just use the signal handler to save a flag that the signal happened, and check for it from various points outside the signal handler, or you could block signals and use sigpending or sigwait to poll for signals.
–
R..Aug 7 '11 at 3:57

1

Wow, this is completely new for me. I've seen code doing clean up like temp files deletion or child process clean up before the main program terminate. Also when you say check for flags, how can that work if you get SIGTERM? Doesn't your program terminate right away if you do not handle that in your handler? Why is it dangerous to do things inside a handler?
–
hariAug 7 '11 at 5:40

1

A signal handler can interrupt the program at any point. This means, unless you've taken care to ensure this can't happen, it could run with various data objects it wants to work with in inconsistent state. For instance the buffer pointers inside a FILE might be only partially updated, or the linked list of open FILEs might have a halfway-inserted or halfway-removed item partly linked to it. Or the internal malloc data structures might have halfway-freed memory referenced... If the signal handler calls functions that depend on that state being consistent, very bad things will happen!
–
R..Aug 7 '11 at 5:49

2

By the way, one classic favorite "safe" way to use signal handlers is to open a pipe to yourself, and store the file descriptors somewhere global. The signal handler can write to the pipe (this is safe) and you can wait for input from the pipe in your main select/poll loop along with other file descriptors you're waiting for input from.
–
R..Aug 7 '11 at 6:03

Store the names of the files in a global variable and then access it from the handler. The signal handler callback will only be passed one argument: the ID for the actual signal that caused the problem (eg SIGINT, SIGTSTP)

Edit 0: "There must be a rock solid reason for not allowing arguments to the handler." <-- There is an interrupt vector (basically, a set of jump addresses to routines for each possible signal). Given the way that the interrupt is triggered, based on the interrupt vector, a particular function is called. Unfortunately, it's not clear where the memory associated with the variable will be called, and depending on the interrupt that memory may actually be corrupted. There is a way to get around it, but then you can't leverage the existing int 0x80 assembly instruction (which some systems still use)

You can use a signal handler which is a method of a class. Then that handler can access member data from that class. I'm not entirely sure what Python does under the covers here around the C signal() call, but it must be re-scoping data?

I was amazed that this works, but it does. Run this and then kill the process from another terminal.

The question was about C ... Though Python most likely remembers the signal somewhere to process it later as soon as possible (it's dangerous to do things inside of signal handlers). Possibly using a global variable.
–
MauganRaMar 2 '14 at 3:05