On a Linux system, the signal -KILLTERM sends a signal that allows applications to safely shut down. These questions might be a little theoretical, but I want to understand them.
When a system sends a terminate signal, where is it sent?
What allows time for the process or, application, to 'safely' terminate?
Is there a child process, or something similar, that runs in the background of an application that looks for this signal?
These questions stem from the Linux watchdog, in reading the man page I saw that the process of the watchdog is to first send a terminate signal to a given PID, and then a KILL -9 signal to force it. I want to be able to utilize the safety built in to the watchdog.
See this code,
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void cleanUp(){ // Do whatever you want here
printf("Safely terminating \n");
}
void hand(int sig){ // called when you are sent SIGTERM
/*
Here you can safely terminate..
*/
atexit(cleanUp); // call cleanUp at exit.
exit(0);
}
int main(){
signal(SIGTERM, hand); //Assign function to be called on SIGTERM
/*
Your code goes here.
I have put an infinite loop for demonstration.
*/
printf("Started execution..\n");
for(;;);
}
This shows how a function can be assigned to be called when a signal is delivered to your application.
To deliver the signal SIGTERM to this code, do this,
kill -SIGTERM <pid>
Here, <pid> id the process id of your running program.
Related
In my C program, I want to get the PID of the process, send a SIGSTOP to that and wait until a SIGCONT arrives from another terminal. So, the procedure looks like
int pid_t = getpid();
printf("PID %d has been stopped\n", pid_t);
STOP(pid_t);
while (not_received_CONT(pid_t))
;
// continue the program
How can I complete that?
What you're writing doesn't make sense. When you stop the process, there is no more instructions being processed in the program and as such there is no while loop to wait for receiving the continuation.
If you check man 7 signal you can see that only one signal currently (in Linux) causes continuation of a process: SIGCONT, hence its not technically necessary to process a while loop check if you didn't get a continuation -- UNLESS you've got a situation where a number of signals are pending and you're trying to determine that.
You can rewrite your code implicitly to be:
int pid = getpid();
printf("PID %d has been stopped\n", pid);
kill(pid, SIGSTOP);
/* Process implicitly continues here, no while needed */
There are multiple vectors in Linux to manage signal handling which may not necessarily be portable.
There is the traditional signal/sigaction method of catching signals and invoking function calls on receipt of the signal. This can come with some unexpected caveats if you aren't careful.
Another way you can manage signals is the use of sigwait, you block the signal with sigprocmask and can consume signals in a safe fashion using sigwait to consume the signal at some controlled point in your process.
Finally you can do similar to sigwait with a signalfd. The main difference between it and sigwait being you can poll the FD to determine its readiness (a signal is received) then read the FD to consume the signals. This makes it a very nice way to manage signals in event driven programs.
I'm doing a program that utilizes threads. I also have a SIGINT handler that closes everything correctly, for an orderly shutdown. However, since my threads are in a while(1) loop the pthread_join function in my handler gets stuck and i have to press ctrl+c a bunch of times, to close each thread singularly. How can i do this with just 1 click?
Here's my thread worker function:
void *worker(){
struct message msg;
while(1){
if(wr.fnode != NULL){
sem_wait(&tsem);
stats->nptri++;
msg.patient = *(wr.fnode);
wr_deletefnode();
sem_post(&tsem);
sleep((float)(msg.patient.ttime/1000));
msgsnd(mqid,&msg,sizeof(msg)-sizeof(long),0);
}
}
}
It's depend how you are sending signal (SIGINT or any) to a thread. for sending a signal to thread you should use pthread_kill() instead of kill() or raise() because signal handler(signal or sigaction) handles only processes ,not threads.
int pthread_kill(pthread_t thread, int sig);
If you ever try to kill running thread using kill command/function OS will throw warning like Warning: Program '/bin/bash' crashed.
observe running thread using ps -eL | grep pts/0 before and after sending signal.
I hope you got something.
Two things you need to solve:
How to break the endless while loop
How to return from blocking system call (like for example sem_wait())
Referring 1.:
Change
while (1)
to be
while (!exit_flag)
Define exit_flag globally
volatile sig_atomic_t exit_flag = 0;
and set it to 1 on reception of the SIGINT signal.
To catch a signal do not setup a signal handler, but create a separate thread using sigwait() to wait for and receive a SIGINT.
Make sure all other threads are created blocking SIGINT.
Referring 2.:
Keep track of all thread running by memorising their thread-ids.
After the signal-listener thread mentioned under 1. set the exit_flag, then loop over the list of running threads and one by one
enable them to receive SIGINT
signal them using pthread_kill(), in case they were stuck inside a system call, the call would return setting errno to EINTR.
join the thread calling pthread_join().
I am building my own shell in C. I want to implement an builtin called alarm that takes in an integer argument for the number of seconds. The builtin just sends the user a message after i seconds (once) but the shell functionality should continue working in the meantime.
Heres what I have so far:
int seconds;
int main(int argc, char const *argv[], char* envp[]){
...
signal(SIGALRM, alarmHandler);
...
}
void alarmHandler(int sig) {
signal(sig, SIG_IGN);
alarm(seconds);
printf("%s\n", "message");
signal(SIGALRM, alarmHandler);
}
void mainProgram(char* string, char* argument){
... //built ins that don't require forking
pid_t processID = fork();
if(processID==0){ //child
if(strcmp(string, "alarm") == 0){
seconds = atoi(argument);
signal(SIGALRM, alarmHandler);
}else{ // parent
usleep(100000)
}
Clearly that doesn't work. I'm kinda lost. I've been trying this for the past couple of hours and I'm unsure of what to do.
Perhaps you've got the wrong idea of what signal does...
The signal system call can be used for setting up handling of signals. Generally speaking, use the kill system call for sending signals.
OTOH, perhaps it's the difference between BSD signal handling semantics and System V signal handling semantics that's contributing to the confusion. System V signal handling semantics can necessitate calling signal (again) within the signal handler function. Newer linux systems at least stick with the BSD signal handling semantics which are reliable and don't require signal to be called again unless you wanted to change the handler that gets invoked.
As a side note, once you get past using signal correctly, I'd recommend using sigaction instead. You can read the 'Portability' section of the signal manual page that I linked to learn more about why you should use sigaction instead of signal.
But onwards...
There are other system calls that can be used to send signals in addition to the kill system call. Like in your case you may want to use alarm or setitimer.
Alternatively, if you wanted to implement something like what alarm does (without using alarm or setitimer), you could use a separate process (via a call to fork perhaps) that calls a sleep function followed by calling kill like this:
usleep(100000);
kill(pid, SIGALRM);
In this example then, pid would be the process ID of the process that you wanted to send the SIGALRM signal to (which is the signal that alarm sends).
Hope this helps answer your question.
I'm trying to write a signal handler to catch any number of consecutive SIGINT signals and prevent the program from exiting. The program is a simple file server. The handler sets a global flag which causes the while loop accepting new connections to end, a call to pthread_exit() ensures that main lets current connections finish before exiting. It all goes like clockwork when I hit ctrl-C once but a second time exits the program immediately.
I tried first with signal():
signal(SIGINT, catch_sigint);
...
static void catch_sigint(int signo)
{
...
signal(SIGINT, catch_sigint);
}
I also tried it using sigaction:
struct sigaction sigint_handler;
sigint_handler.sa_handler = catch_sigint;
sigemptyset(&sigint_handler.sa_mask);
sigint_handler.sa_flags = 0;
sigaction(SIGINT, &sigint_handler, NULL);
Unsure how to "reinstall" this one I just duplicated this code in the handler similar to the handler using the signal() method.
Neither one of these works as I expected.
Additional info:
The program is a simple file server. It receives a request from the client which is simply a string consisting of the requested file name. It utilizes pthreads so that transfers can occur simultaneously. Upon receiving SIGINT I wish for the server to exit the while loop and wait for all current transfers to complete then close. As is, no matter how I code the signal handler a second SIGINT terminates the program immediately.
int serverStop = 0;
...
int main()
{
/* set up the server -- socket(), bind() etc. */
struct sigaction sigint_hadler;
sigint_handler.sa_handler = catch_sigint;
sigint_handler.sa_flags = 0;
sigemptyset(&sigint_handler.sa_mask);
sigaction(SIGINT, &sigint_handler, NULL);
/* signal(SIGINT, catch_sigint); */
while(serverStop == 0)
{
/* accept new connections and pthread_create() for each */
}
pthread_exit(NULL);
}
...
static void catch_sigint(int signo)
{
serverStop = 1;
/* signal(SIGINT, catch_sigint) */
}
I don't think any other code could be pertinent but feel free to ask for elaboration
On Linux, you should not have to reinstall the signal handler, using either signal (which implements BSD semantics by default) or sigaction.
when I hit ctrl-C once but a second time exits the program immediately.
That's not because your handler got reset, but likely because your signal handler is doing something it shouldn't.
Here is how I would debug this issue: run the program under GDB and
(gdb) catch syscall exit
(gdb) catch syscall exit_group
(gdb) run
Now wait a bit for the program to start working, and hit Control-C. That will give you (gdb) prompt. Now continue the program as if it has received SIGINT: signal SIGINT (this will invoke your handler). Repeat the 'Control-C/signal SIGINT' sequence again. If you get stopped in either exit or exit_group system call, see where that is coming from (using GDB where command).
Update:
Given the new code you posted, it's not clear exactly where you call pthread_exit to "ensures that main lets current connections finish before exiting". As written, your main thread will exit the loop on first Control-C, and proceed to call exit which would not wait for other threads to finish.
Either you didn't show your actual code, or the "second Control-C" is a red herring and your first Control-C takes you out already (without finishing work in other threads).
NOTE: this is largely guesswork.
I'm pretty sure that calling pthread_exit in the main thread is a bad idea. If the main thread has quit, then the OS may try to send subsequent signals to some other thread.
I recommend that instead of using pthread_exit in the main thread, you just pthread_join() all the other threads, then exit normally.
But it's also important to ensure that the other threads do not get the signals. Normally this is done with sigprocmask (or maybe more correctly pthread_sigmask, which is the same under Linux) to mask the signal out in the worker threads. This ensures that the signal is never delivered to them.
Note that to avoid race conditions, you should use pthread_sigmask in the main thread just before creating a child thread, then set the signal mask back again in the main thread afterwards. This ensures that there is no window, however small, during which a child thread can possibly get unwanted signals.
I'm not sure to understand. A signal handler should usually not re-install any signal handler (including itself), because the signal handler stays in function till another is installed. See also SA_NODEFER flag to sigaction to be able to catch the signal during its handling.
A signal handler should be short. See my answer to this question. It usually mostly sets a volatile sig_atomic_t variable.
What is not working? Don't do complex or long-lasting processing inside signal handlers.
Please show your code...
If I have a parent coordinator program and a worker program, but the coordinator program is creating all of the needed worker processes. If I want to implement the alarm() method correctly to kill all of the processes and terminate the program after a certain amount of time.
Is this the correct way to implement it? The current way I have it, it does now print out the message corresponding printf() message for the worker processes being killed.
Note: I implemented the ignoring of interrupt signals so more worker processes could proceed without worry.
int main(int argc, char** argv)
{
signal(SIGINT, sig_ctrl);
signal(SIGALRM, sig_alarm);
alarm(5);
sleep(10);
//rest of program which creates the needed processes
}
void sig_ctrl(int sig)
{
printf("Hold on buddy, you're not terminating this program with CTRL^C.\n");
}
void sig_alarm(int sig)
{
sleep(0);
fflush(stdout);
printf("The alarm is sounding, all worker procceses will be...TERMINATED!");
raise(SIGTERM);
}
There's the easy way, which is arguably the sloppy way, and the hard way.
The easy way involves ensuring that the initial process is the process group leader, that it ignores an appropriate signal (but that its children do not ignore the signal), and then sending the signal to the process group.
The harder way requires a list somewhere of all the child processes, and the parent can then duly send a signal to each of the processes, removing the dead ones from its list.
Your program's problem is that sleep() is usually implemented using the SIGALRM timer, so calling it probably overrides your signal() setting for SIGALRM
$ man 3 sleep
BUGS
sleep() may be implemented using SIGALRM; mixing calls to alarm(2) and
sleep() is a bad idea.
(BTW, The sleep(0) is nonsense too. What are you trying to achieve with that?)
I had the same type of problem in some homework for a unix unit. That is pretty much the solution most people came up with :P