I wrote a signal handler for a process, and fork() after that, the signal handler will be applied to both parent and child processes. If I replace the child process with "exec", the signal handler is no more.
I know this happens because "exec" call will overwrite the child process address space with it's own.
I just want to know if there is a way to make signal handler work even after "exec" call ?
No. From the man pages:
execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. The program invoked inherits the calling process's PID, and any open file descriptors that are not set to close on exec. Signals pending on the calling process are cleared. Any signals set to be caught by the calling process are reset to their default behaviour. The SIGCHLD signal (when set to SIG_IGN) may or may not be reset to SIG_DFL.
In fact, if the signal handler were still active after the code had been replaced with some very different code, you could expect all sorts of mayhem when the signal occurred. The signal handler is, after all, just an address to call when something happens (discounting SIG_IGN and SIG_DFL for now). Who knows what piece of code would be at that address when you replace the entire text segment?
Related
I have a program in C. I wish for it to always exit cleanly with exit code of 0 when it gets a SIGTERM. What is the earliest place I can register the signal handler? I added it at the top of my main(), but I worry it might get a sigterm just before the signal registers.
Is it possible to register a signal handler even earlier?
Yes you can. Using platform specific initializers such as gcc's __attribute((constructor)). But that's hardly a robust solution.
If you wish to "to always exit cleanly with exit code of 0 when it gets a SIGTERM", then instruct the process-spawning code to start with SIGTERM blocked.
Your main can then register a signal handler and unblock SIGTERM (with sigprocmask or pthread_sigmask, at which point the signal handler will run immediately if it had been received at any point in between process creation up to the signal-unblocking call.
Essentially, it will defer the delivery of the signal up to a point where you're ready too handle it.
(Note that if you start the process with the signal ignored rather than blocked, then any instance of the signal received up to unignoring the signal will have been lost, as if they never happened. That would seem to go against your stated requirement.)
If you can switch to C++: between start of the program and main global variables are initialized. So in theory you could have code like the following that would be run before main is called.
int f() {
signal(...);
return 0;
}
int x = f();
But you don't have a guarantee in which order global objects are initialized, so x might not be initialized first, but last.
But coming back to your original request: the time between starting the program and main is so short, why do you want to prepare against someone sending a SIGTERM in that short time? Isn't that too unlikely to happen?
If it is possible you could change the parent to ignore SIGTERM and then fork and execve. signal man page says
A child created via fork(2) inherits a copy of its parent's
signal dispositions. During an execve(2), the dispositions of
handled signals are reset to the default; the dispositions of
ignored signals are left unchanged.
So you could start your process ignoring SIGTERM until it sets a handler for SIGTERM.
Is there any way in C programming language , to stop a child process , and then call it again to start from the beginning? I have realised that if I use SIGKILL and then call the child process again nothing happens.
void handler {
printf(“entered handler”);
kill(getpid(),SIGKILL);
}
int main () {
pid_t child;
child=fork();
if (child<0) printf(“error”);
else if (child==0) {
signal(SIGINT,handler);
pause();
}
else {
kill(child,SIGINT);
kill(child,SIGINT);
}
This should print two times “Entered Handler” but it does not. Probably because it cannot call child again . Could I correct this in some way?
This should print two times “Entered Handler” but it does not.
Probably because it cannot call child again .
There are several problems here, but a general inability to deliver SIGINT twice to the same process is not one of them. The problems include:
The signal handler delivers a SIGKILL to the process in which it is running, effecting that process's immediate termination. Once terminated, the process will not respond to further signals, so there is no reason to expect that the child would ever print "entered handler" twice.
There is a race condition between the child installing a handler for SIGINT and the parent sending it that signal. If the child receives the signal before installing a handler for it, then the child will terminate without producing any output.
There is a race condition between the the first signal being accepted by the child and the second being delivered to it. Normal signals do not queue, so the second will be lost if delivered while the first is still pending.
There is a race condition between the child blocking in pause() and the parent signaling. If the signal handler were not killing the child, then it would be possible for the child to receive both signals before reaching the pause() call, and therefore fail to terminate at all.
In the event that the child made it to blocking in pause() before the parent first signaled it, and if it did not commit suicide by delivering itself a SIGKILL, then the signal should cause it to unblock and return from pause(), on a path to terminating normally. Thus, there would then also be a race condition between delivery of the second signal and normal termination of the child.
The printf() function is not async-signal safe. Calling it from a signal handler produces undefined behavior.
You should always use sigaction() to install signal handlers, not signal(), because the behavior of signal() is underspecified and varies in practice. The only safe use for signal() is to reset the disposition of a signal to its default.
Could I correct this in
some way?
Remove the kill() call from the signal handler.
Replace the printf() call in the signal handler with a corresponding write() call.
Use sigaction() instead of signal() to install the handler. The default flags should be appropriate for your use.
Solve the various race conditions by
Having the parent block SIGINT (via sigprocmask()) before forking, so that it will initially be blocked in the child.
Have the child use sigsuspend(), with an appropriate signal mask, instead of pause().
Have the child send some kind of response to the parent after returning from sigsuspend() (a signal of its own, perhaps, or a write to a pipe that the parent can read), and have parent await that response before sending the second signal.
Have the child call sigsuspend() a second time to receive the second signal.
Is it possible using signals in C to have a parent and child process running, and when randomly control C (SIGINT) is pressed, a integer is given to the parent to pass to the child via a pipe.
pseudo code:
main:
-call firstfunction
handler:
-handler for SIGINT
-call second function if SIGINT is live
firstfunction:
-create pipe, fork for child
-let OS know to handle SIGINT
secondfunction:
-handler for control C signal
-return a INT value to firstfunction parent process
I'm struggling trying to see how would I go about the secondfunction actual code.
In Standard C all you are allowed to do is to set an atomic flag in the signal handler, via a global variable of type volatile sig_atomic_t.
So your signal handler sets the flag and returns; and your main loop will poll this flag to see if any signals have been received. Your main loop then sends the message to child when it detects the flag.
Specific operating systems may or may not permit you to do more in the signal handler (e.g. open the pipe and send something).
How I would go about capturing/intercepting signals sent to another process from my own process? Willing to use C, Ruby, or any Linux package.
You can write a library wrapper that will replace system signal/sigaction calls to intercept setting of the signal handler and set your own handlers. On received signal, you can do your job and call user handler later. Use LD_PRELOAD to replace system signal/sigaction routines by your own.
I think that the ptrace(2) system call is what you want. From the manual: "While being traced, the child will stop each time a signal is delivered, even if the signal is being ignored. (The exception is SIGKILL, which has its usual effect.) The parent will be notified at its next wait(2) and may inspect and modify the child process while it is stopped. The parent then causes the child to continue, optionally ignoring the delivered signal (or even delivering a different signal instead)."
I am writing a C program in which the parent forks n child processes. A child process once created invokes a SIGSTOP to allow other child processes to be created. The parent after creating all the n child processes sends a SIGCONT signal to all the child.
All the child processes execute an infinite loop and share a common resource using semaphores. Now I want that whenever the user presses ctrl-c, the parent and all the child processes terminate together. However before terminating the child processes should update in a file how many times each has used the resource.
eg:
Process 1 - 5 times
Process 2 - 3 times
and so on.
Need help in this implementation please...
The formal signal handler function should do as little as feasible. The C standard says it can write to a volatile sig_atomic_t variable, or call abort() or _Exit() (or, with restrictions, signal()). POSIX allows more to happen, and you're probably working on Linux (though you didn't say so). So, your signal handler will change the value of a sig_atomic_t variable from 0 to 1 to indicate that the signal occurred.
So, your child processes will be looping. As part of the loop condition, you should check a sig_atomic_t variable to see whether the child should terminate. When it detects that a signal occurred, it will stop looping, open the log file for append, write its information to that file, and exit. You could check the sig_atomic_t variable at other points in the processing than just the main loop condition; that's your decision.
Note that you should use sigaction() rather than signal() to control the signal handling, and you should block interrupts while processing a signal.
So, to summarize:
Your signal handler does as little as possible.
Your code detects in the main loop when the signal handler has been called and arranges to exit.
Your code can also detect when the signal handler has been called at other convenient points.
You can call a function to do the logging and exit.
In order to write the file, you need to add the code to write to said file in your signal handler for SIGINT. If you want each process to write to that file, you're going to need to make sure that the SIGINT gets sent to the entire process group.
You can probably get by without sending the SIGQUIT to each, as you could have each process simply exit itself after processing SIGINT. If you want to optimize a little, you could keep a shared data structure of which processes have already received the SIGINT so that you don't send each process several SIGINTs.