About SIGINT in child processes - c

I am writing a shell, now it comes to control the child process.
When I use signal (SIGTERM, SIG_DFL); in the child process,
the signal SIGINT is generated by Ctrl + C, and that signal terminates whole the OS shell.
how can I just terminate the process e.g “cat” only, but not whole shell??
Should I use somethings like:
void sig_handler(int sig) {
if(sig ==SIGINT)
{
kill(pid);
}
}
Really thanks a slot.

Your question is rather vague. Can you be more clear on what you want to achieve?
I think you should be using signal(SIGTERM, sig_handler) instead of SIG_DFL which is the default action taken. Since, you have a signal handler, you call it instead of predefined functions like SIG_INT or SIG_DFL. The code inside your function looks fine. As long as you know the pid, you can do a kill(pid).

In the exec'd child, the SIGINT (and SIGQUIT) handlers will be SIG_DFL if they were set to a handler in the parent shell, and that's most likely correct. (You can't inherit a non-default signal handler across an exec, of course, because the function usually doesn't even exist in the exec'd process.)
Setting a handler for SIGTERM won't affect the response to SIGINT, or vice versa.
Your shell shouldn't need to deliver signals to its children.

Related

process catch SIGINT signal using sigint_handler

I encountered this problem when doing shell lab from the book CSAPP, the lab ask you to implement your own version of shell with some specification,one of them is
Typing ctrl-c (ctrl-z) should cause a SIGINT (SIGTSTP) signal to be
sent to the current foreground job, as well as any descendents of that
job (e.g., any child processes that it forked). If there is no
foreground job, then the signal should have no effect.
so you should complete one of the given functions called sigint_handler which supposed to catch SIGINT signal and send it along to the foreground job. below is a piece of code I find online(the code passed the correctness check)
void sigint_handler(int sig)
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if (pid != 0)
kill(-pid, sig);
errno = olderrno;
return;
}
what I don't understand is if SIGINT is sent using kill,then the descendents of foreground job will also use this handler to catch SIGINT signal right? so it's kind of a recursive call to me.so how does this actually work? thanks for helping me.
handler is installed in the main function
signal(SIGINT, sigint_handler); /* ctrl-c */
and fgpid return PID of current foreground job, 0 if no such job
Once a child process calls execve(), the child's (usually short-lived) initial address space is freed/released and replaced with space for the specified executable image; now the child no longer has a copy or access to the parent's data or text, like signal handlers.
Now consider a process-group associated with the control-terminal (tty). When a user types a CTRL-C (or CTRL-\ or CTRL-Z), the tty driver posts a signal to 1+ processes as members of the associated process-group. The result of delivering a signal would be the system default action unless a process established a different signal disposition (signal(), sigaction(), or related).
The posted code excerpt indicates a relayed event: user types a CTRL-C, tty driver posts a SIGINT to the shell, shell's handler looks for a foreground job, calls kill() with a negative pid to post a signal to members of that process-group.
For related info see these man pages:
man setpgrp
man tty_ioctl (symbols: TIOCSCTTY, TIOCGPGRP, TIOCSPGRP)
alternates:
man tcgetpgrp tcsetpgrp

Stoping a process then using it again without killing it

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.

How can I resume a paused child process from its parent?

I'm a little overwhelmed by how many ways you can control processes, like wait() pause() signal handling etc. All I want is to resume a paused process, and execute the line after the pause() statement afterward, like so:
/* Child code */
pause();
execvp(args[index], args);
The topology of my processes is linear children. One parent, n children, no grandchildren. So after the parent finishes forking, I have it running this loop to try to wake them up in order:
// Parent iterates through n child processes
for (i = 0; i < n; i++) {
// Need to unpause here, do i need signals?
signal(SIGCONT, sighandler);
// I don't know what im doing
}
wait(&status);
I can get their process IDs if that helps, but I dont know what to do with them.
From the pause(2) man page (emphasis mine):
pause() causes the calling process (or thread) to sleep until a signal is delivered that either terminates the process or causes the invocation of a signal-catching function.
And more specifically:
pause() only returns when a signal was caught and the signal-catching function returned.
This means that for your child to unpause, you need to send it a signal (and probably a custom signal handler).
This is a simple signal handling function - Usually these are put at the top of your page (under the imports) or in the header file.
void handleContinueSignal(int sig) {
myGlobalStaticContinueVariable = 1; // Or some other handling code
}
And this is how you announce that your signal handling function should be associated with the SIGCONT signal, should it ever be received. You'll probably only want your child process to run this line. Make sure you put it in before the pause though - getting signal handlers running is one of the first things that a new process should do.
signal(SIGCONT, handleContinueSignal); // Name of the signal handling function
Finally, you can make your parent send a SIGCONT signal to the child by giving its PID like this:
kill(yourChildPID, SIGCONT);
For your code, you'll have to make the parent loop though and call this once for each child's PID, which will wake each of them up in turn.

Custom Signal Handler in C

Consider the following chunk of C code:
void TERMHandler(int sig){
signal (sig, SIG_DFL);
}
main() {
pid_t pid;
pid = fork()
if (pid == 0) {
signal(SIGTERM,TERMHandler);
while(1);
}
else
{
sleep(3);
kill(pid,SIGTERM);
sleep(3);
kill(pid,SIGTERM);
}
}
We create a new process and distinguish between child (pid = 0) and parent.
Can a custom handler be used for every type of signals? If so, assuming we create a custom handler, is it right that there wouldn't be any difference between all signals if I only use the signal once (or never reset the signal handler), since it would just execute my handler without considering the signal in the function?
What I'm trying to say is, is it right that:
signal(SIGTERM,CustomHandler);
signal(SIGTSTP,CustomHandler);
signal(SIGHUP,CustomHandler);
...
will execute the same code when the parent runs kill(pid, SomeSignal)?
Can a custom handler be used for every type of signals?
Yes, the same custom signal-handler function can be registered to handle different types of signals, up to and including all the signals that can be caught on the system in question. Note, however, that there may be defined signals that cannot be caught. On POSIX-conforming systems, for example, SIGKILL and SIGSTOP have this property.
If so,
assuming we create a custom handler, is it right that there wouldn't
be any difference between all signals if I only use the signal once
(or never reset the signal handler), since it would just execute my
handler without considering the signal in the function?
The signal handler function is not obligated to consider the signal number in determining what to do. It can perform the same action no matter what, or, as in your example function, it can simply pass the signal number on to some other function. You may or may not consider the latter to be a special case of the former.
Do note, however, that on a POSIX system, the sigaction() function is preferable to signal() for modifying signal dispositions. Its behavior is both more flexible and more consistent than signal()'s over various operating systems.
Can a custom handler be used for every type of signals?
Yes. You can install a custom "signal-catching" function for all signals which can be caught. (For example, SIGKILL and SIGSTOP may not be caught.)
[I]s it right that there wouldn't be any difference between all signals if I only use the signal once (or never reset the signal handler), since it would just execute my handler without considering the signal in the function?
That depends on how you code your signal catching function. The system will pass the caught signal to the function, so the same function could do something different upon catching a SIGTERM rather than a SIGHUP, for instance. If your handler ignores its sig argument and ignores the signal environment generally (masks, stacks, dispositions), then, yes, each invocation would be like any other.

behaviour of signal handler when multiple signals are sent

I am dealing with some issue, I have a function that's handling a signal and like that:
void sigChld(int noSig)
{
//some action here
}
void F1 () // some child process
{
struct sigaction action;
.... // initialisation of the structure with sigChld as the function handler
sigaction(SIGCHLD, &action, 0);
while(1)
callToFunction();
}
In my child process F1, I am attaching the handler to SIGCHLD to sigChld() and then I do callToFunction() that creates another process and do some treatment. At the end of its execution, It sends me a SIGCHLD which I need to treat in my handler.
Now my question is : I need the return value of callToFunction() so I thought about using a waitpid in the handler of sigchld before doing some actions. But what if during waitpid() or the actions, callToFunction() send another signal ? will the current handler stop ? or will continue its execution and then treat the next signal ?
In my problem, I need to treat completely the signals one after another one like if I was executing the handler in parallel.
I'm not sure it's actually possible for a waitpid() in a SIGCHLD handler to get interrupted by a signal, since it should return instantly if there's a child available, but in general signals can occur during a signal handler just like anywhere else.

Resources