I've recently had a problem with signals. I'd like to write a program in C which would print anything after a signal is sent to the process. For example: If I send SIGTERM to my process (which is simply running program), I want the program to print out for example, "killing the process denied" instead of killing the process. So how to do that? How to force process to catch and change the meaning of such signal. Also I have a question if there is any possibility to kill the init process (I know it's kind of a stupid question, but I was wondering how linux deals with such a signal, and how would it technically look if I type: sudo kill -9 1.
Don't use the signal handler to print. You can set a variable of type volatile sig_atomic_t instead, and have your main thread check this (see this example).
When your main thread has nothing else to do (which should be most of the time), let it block on a blocking function call (e.g. sleep()) that will wake up immediately when the signal is received (and set errno to EINTR).
C++ gotcha: Unlike the C sleep() function, std::this_thread::sleep_for() (in recent versions of glibc) does not wake up when a signal is received.
Regarding if it's possible to kill pid 1, see this question. The answer seems to be no, but I remember that Linux got very grumpy once I booted with init=/bin/bash and later exited this shell – had to hard reboot.
If you're looking for trouble, better kill pid -1.
Related
I've been chasing a problem: on exit application hangs, but when you debug it all is good. It boiled down to a wrong assumption made by someone 15 years ago, specifically he assumed that if one thread is waiting in accept() -- closing that handle in another thread will cause accept() to fail. Some of process unwinding code hinged on this assumption (and I know this assumption is not correct).
Question: why this assumption holds when program is being debugged? Precisely what changes in execution environment?
Edit: observed in CentOS 7
Edit 2: I know it is a UB and I need to fix it. My question is not "what to do?" but "why it happens?". I am curious because ability to sense debugger via side effects like this one is pretty cool and one day may came handy.
Edit 3:
I've discovered that if your process have a signal handler installed and (after closing fd) you send that signal (via pthread_kill()) to thread currently sleeping in accept() -- that call always immediately returns (with EBADF error). Doesn't matter what your handler is doing (as long as it returns). It looks like signal delivery causes thread to wake up, interrupt accept() and restart it (at which point it checks if related file "handle" is good and exit with error).
I am not encouraging to rely on this behavior, but propose a possible explanation to original question -- maybe gdb periodically wakes up every thread with some signal? Or being ptraced means kernel (for some reason) will periodically wake up every thread "as if" it was interrupted by a signal?
From ptrace man:
Signal injection and suppression
After signal-delivery-stop is observed by the tracer, the tracer
should restart the tracee with the call
ptrace(PTRACE_restart, pid, 0, sig)
where PTRACE_restart is one of the restarting ptrace requests. If
is 0, then a signal is not delivered. Otherwise, the signal sig
is delivered. This operation is called signal injection in this man‐
ual page, to distinguish it from signal-delivery-stop.
The sig value may be different from the WSTOPSIG(status) value: the
tracer can cause a different signal to be injected.
Note that a suppressed signal still causes system calls to return
prematurely. In this case, system calls will be restarted: the
tracer will observe the tracee to reexecute the interrupted system
call (or restart_syscall(2) system call for a few system calls which
use a different mechanism for restarting) if the tracer uses
PTRACE_SYSCALL. Even system calls (such as poll(2)) which are not
restartable after signal are restarted after signal is suppressed;
however, kernel bugs exist which cause some system calls to fail with
EINTR even though no observable signal is injected to the tracee.
In my case termination logic starts with a delivery of a signal (that gets intercepted by gdb) and then passed injected into process being traced. strace-ing gdb produces:
ptrace(PTRACE_PEEKTEXT, 2274, 0x7f0c9d0ee2e0, [0x7f0ca013ab80]) = 0 <-- gdb woke up to SIGTERM directed at tracee
ptrace(PTRACE_PEEKUSER, 2274, 8*SS + 8, [0x7f0ca013a8c0]) = 0
ptrace(PTRACE_GETREGS, 2274, 0, 0x7ffdb4f2c3a0) = 0
...
ptrace(PTRACE_CONT, 2338, 0x1, SIG_0) = 0
ptrace(PTRACE_CONT, 2274, 0x1, SIGTERM) = 0 <-- SIGTERM is delivered to a thread chosen by kernel
...
ptrace(PTRACE_CONT, 2276, 0x1, SIG_0) = 0 <-- all other threads are restarted
...
note that this is not enough to explain behavior fully because thread sleeping in accept() can restart syscall before other thread closes file descriptor.
But strace log is choke-full of similar sequences of commands (PTRACE_PEEKTEXT followed by ever-reducing number of PTRACE_CONT). What happens here is gdb wakes up on every thread termination, pulls some data out of tracee and restarts (remaining) threads, causing syscalls to be restarted. I.e. as threads exit one after another, each remaining thread gets stopped and restarted multiple times eventually causing accept() to be restarted after file descriptor is closed by another thread. In fact it is guaranteed to happen because said thread exits after closing it.
I developed a C application in linux that contains an infinite loop while(1).
There are some pointers that are dynamically allocated and are useful under the infinite loop, so the only time to deallocate memory is after interrupting the while(1) by ctrl-z, ctrl-c, kill -9 apppid, killall appname.
So the idea is that I associate new handler that deallocates memory to the interruption events signals.
void deallocatehandler(int signal){ printf("Memory Deallocation\n"); exit(0);}
int main(){
signal(SIGINT, &deallocatehandler);
signal(SIGTSTP, &deallocatehandler);
signal(SIGKILL, &deallocatehandler);
while(1){
/**
Some code here
**/
}
}
If I press ctrl-c or ctrl-z the handler is called but the problem is with SIGKILL. The commands kill -9 and killall doesn't launch the handler.
Has someone an idea why? and is there suggestions to correct it?
The whole point of SIGKILL is to kill the process no matter what. That's why you're not allowed to handle it.
In most cases, you start with a SIGTERM to give the process a chance to exit nicely. SIGKILL is usually used as a last resort when SIGTERM does not work. After all, the expected behavior from SIGTERM is that the process exits. If it doesn't you know that something is wrong.
From the manual
The SIGTERM signal is a generic signal used to cause program termination. Unlike SIGKILL, this signal can be blocked, handled, and ignored. It is the normal way to politely ask a program to terminate.
...
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.
In the same document, you can also read this interesting thing
In fact, if SIGKILL fails to terminate a process, that by itself constitutes an operating system bug which you should report.
You can't catch SIGKILL and SIGSTOP signals. So your signal handler wouldn't do anything.
There's nothing you can do when your process receives SIGKILL, let alone any memory cleanup.
On Linux, memory will be cleaned up on program exit, so this is probably not an issue.
Usually such cleanup-on-exit is done for SIGTERM.
The correct answer is don't send SIGKILL (kill -9 should only be used if kill itself doesn't work).
That's not the way to request to a process to terminate itself. Send SIGTERM first and if it doesn't work, then send SIGKILL.
man 7 signal
When you SIGKILL a process, you don't ask it to terminate nicely. You ask the kernel to stop any further execution for that process.
Thus, the process can't be aware it has received a SIGKILL.
But it shouldn't matter for you, since a SIGKILL shall only be issued when SIGTERM has shown no success.
So the idea is that I associate new handler that deallocates memory to the interruption events signals.
Not needed! After the process terminates, whatever the reason was, all memory is deallocated from the kernel. So you have no need to do that manually.
With IPC resources and semaphores you will have this problem which can't be handled properly at all.
Your question is ill-posed for several reasons.
First, malloced memory is freed when your process terminates. Most machines would be completely unusable if modern architectures wouldn't do this automatically.
Second, some signals and some ways of process termination aren't catchable. So for these there is no hope of doing repairwork anyhow. Among the methods that terminate an execution without much cleanup are some signals, abort, quick_exit, _Exit.
Third, using signalhandlers for cleanup jobs is complete overkill. The C library has atexit and at_quick_exit (since C11) handlers that are designed for that purpose. So if you have to do something special when an execution terminates (such as writing some final message to a socket, cleaning up files or shared memory) use the tools that were invented for this.
You can't catch SIGKILL (kill -9) by definition. It is meant as a "last resort" way to kill a process, so for this reason the process must not be able to catch it. For a friendly termination request, check for SIGTERM (kill -15 or kill without specific value).
But you should in general not want to catch such events, unless you need to do very specific clean-up actions. The memory will be deallocated but the Operating System; no need for your program to catch the signals only to free the memory.
First of all, I do know that there was a similar question here in the past.
But that question wasn't answered properly. Instead, it diverted into suggestion what to do to catch signals.
So just to clarify: I've done whatever needs to be done to handle signals.
I have an application that forks a daemon that monitors the main process through pipe.
If a main process crashes (e.g. segmentation fault), it has a signal handler that writes all the required info to pipe and aborts.
The goal is to have as much info as possible when something bad happens to the application, w/o messing with "normal" operation, such as SIGHUP, SIGUSR1, etc.
So my question is: which signals should I catch?
I mean signals that w/o me catching them would cause application to abort anyway.
So far I've come up with the following list:
SIGINT (^C, user-initiated, but still good to know)
SIGTERM (kill <pid> from shell or, AFAIK, can be result of OutOfMemory)
SIGSEGV
SIGILL
SIGFPE
SIGBUS
SIGQUIT
Does anybody know if I miss something? kill -l has lots of them... :)
I'm looking at my copy of advanced programming the unix environment (Stevens). According to the table in the section on signals, the following POSIX signals will by default terminate the process, if you don't catch them. It wouldn't be unreasonable to monitor all of these that you don't use:
SIGABRT
SIGALRM
SIGFPE
SIGHUP
SIGILL
SIGINT
SIGKILL
SIGPIPE
SIGQUIT
SIGSEGV
SIGTERM
SIGUSR1
SIGUSR2
You can catch all of these except SIGKILL, but hopefully SIGKILL won't come up very often for you.
Note that your signal man page (man 7 signal) should give you the proper list for your system - this is the POSIX list, and may differ depending on your architecture.
You should not catch the signal and write the code to a pipe. This is both unnecessary and not failsafe.
Let me quote an answer from the question you linked to, to point out why it's not failsafe: "What makes you think that a SEGV hasn't already corrupted your program memory"
Now you may be wondering how to do it better, and why I've said it is "unnecessary".
The return code of the waitpid syscall can be checked using WIFSIGNALED() to determine whether the process was terminated normally or via a signal, and WTERMSIG() will return the signal number.
This is failsafe and it does not require a handler or a pipe. Plus, you don't need to worry what to catch, because it will report every signal that terminates your process.
It depends: whatever signal you like if that's useful to inform the user something bad just happened. However SIGKILL and SIGSTOP cannot be caught.
Not long ago, I wondered about the question: why are all processes killed when you close a terminal on Linux, and not passed to the "init" process (with pid 1)?
Because, all child processes are adopted by "init" process after termination of the parent.
Please, help me understand difference and the errors in my reasoning.
And also:
If it's possible, then can we use a system call to stop this happening? I think, that for this the programs need use setsid(), but in practice it's not correct.
As explained by cnicutar, it's due to the SIGHUP sent to all processes in the process group associated with the controlling terminal. You may either install a handler for this signal or ignore it completely. For arbitrary programs, you can start them with the nohup utility designed for this purpose.
You can also place the process in a new process group without a controlling terminal.
why on close terminal on linux all his processes will terminated, but
not passed to "init" process (with pid 1)
The processes are losing their controlling terminal so the kernel sends them a SIGHUP. The default action of SIGHUP is to terminate the process.
i think this will help you to understand
http://www.digipedia.pl/usenet/thread/18802/10189/
While running a thread program and repeatedly killing the main program using Ctrl + C, i see unexpected results in the program in second run. However, if i let the program run and voluntarily exit, there are no issues.
So, my doubt is, does Ctrl + C, kill threads also along with the main process?
Thanks in advance.
In multithreaded programming, signals are delivered to a single thread (usually chosen unpredictably among the threads that don't have that particular signal blocked). However, this does not mean that a signal whose default action is to kill the process only terminates one thread. In fact, there is no way to kill a single thread without killing the whole process.
As long as you leave SIGINT with its default action of terminating the process, it will do so as long as at least one thread leaves SIGINT unblocked. It doesn't matter which thread has it unblocked as long as at least one does, so library code creating threads behind the application's back should always block all signals before calling pthread_create and restore the signal mask in the calling thread afterwards.
Well, the only thing that Ctrl + C does is sending SIGINT to one thread in the process that is not masking the signal. Signals can be handled or ignored.
If the program does handle Ctrl+C, the usual behavior is self-termination, but once again, it could be used for anything else.
In your case, SIGINT is being received by one thread, which probably does kill itself, but does not kill the others.
Under Linux 2.6 using NPTL threads: I am assuming that the process uses the default signal handler, or calls exit() in it: Yes it does. The C library exit() call maps to the exit_group system call which exits all the threads immediately; the default signal handler calls this or something similar.
Under Linux 2.4 using Linuxthreads (or using 2.6 if your app still uses Linuxthreads for some weird reason): Not necessarily.
The Linuxthreads library implements threads using clone(), creating a new process which happens to share its address-space with the parent. This does not necessarily die when the parent dies. To fix this, there is a "master thread" which pthreads creates. This master thread does various things, one of them is to try to ensure that all the threads get killed when the process exits (for whatever reason).
It does not necessarily succeed
If it does succeed, it is not necessarily immediate, particularly if there are a large number of threads.
So if you're using Linuxthreads, possibly not.
The other threads might not exit immediately, or indeed at all.
However, no matter what thread library you use, forked child processes will continue (they might receive the signal if they are still in the same process-group, but can freely ignore it)