I have a child process spawned using child_process.fork and would like to terminate it. The problem is that the child process does some lengthy CPU bound calculation and I don't have control over it. That is, the CPU bound code fragment cannot be restructured to make use of process.nextTick or polling.
A very simplified example:
parent.js
var cp = require('child_process');
var child = cp.fork('child.js');
child.js
...
while(true){} // lengthy computation which I cannot modify
...
Is it possible to terminate it? Preferably in a way that allows catching the exit event in the child in order to do some cleanups?
Sending SIGTERM/SIGKILL/etc using child.kill() doesn't
seem to work on Windows. I assume even if it works on other OSes it wouldn't kill the process anyway due to child not being able to process events while doing the computation.
I've done this the messy way by using the PID of the process and killing it at the OS level.
Not sure how to do it in windows, but in Linux/mac I've done:
var cp = require('child_process'),
badJob = cp.fork('badFile.js');
cp.execSync('kill -9 ' + badJob.pid);
The signal 9 is caught at the Kernel level, so the condition of the process is irrelevant.
Edit: In Windows you can use taskkill instead of kill. ex:
cp.execSync('taskkill /f ' + badJob.pid);
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
Improve this question
EDIT: If managing child processes for a shell script is really purely a matter of "opinion"......no wonder there are so many terrible shell scripts. Thanks for continuing that.
I'm having trouble understanding how SIGTERM is conventionally handled with relation to child processes in Linux.
I am writing a command line utility in Bash.
It looks like
command1
command2
command3
Very simple, right?
However, if my program is sent SIGTERM signal, the Bash script will end but the current child process (e.g. command2) will continue.
But with some more code, I can write my program like this
trap 'jobs -p | xargs -r kill' TERM
command1 &
wait
command2 &
wait
command3 &
wait
That will propogate SIGTERM to the currently running child process. I haven't often seen Bash scripts written like that, but that's what it would take.
Should I:
Write my program in the second style each time I create a child process?
Or expect users to launch my program in a process group if they want to send SIGTERM?
What's the best practice/conventions for process management responsibilities with respect to SIGTERM for children?
tl;dr
The first way.
If a process starts a child process and waits for it to finish (the example), nothing special is necessary.
If a process starts a child process and may prematurely terminate it, it should start that child in a new process group and send signals to the group.
Details
Oddly for how often this applies (like, every shell script), I can't find a good answer about convention/best practice.
Some deduction:
Creating and signaling process groups are very common. In particular, interactive shells do this. So (unless it takes extra steps to prevent it) a processes' children can receive SIGINT signals at any time, in very normal circumstances.
In the interest of supporting as few paradigms as possible, it seems to make sense to rely on that always.
That means the first style is okay, and the burden of process management is placed on processes that deliberately terminate their children during regular operation (which is relatively less common).
See also "Case study: timeout" below for further evidence.
How to do it
While the perspective of the question was from the requirements of a vanilla callee program, this answer prompts the question: how does one start a process in a new process group (in the non-vanilla case that one wishes to prematurely interrupt the process)?
This is easy in some languages and difficult in others. I've created a utility run-pgrp to assist in the latter case.
#!/usr/bin/env python3
# Run the command in a new process group, and forward signals.
import os
import signal
import sys
pid = os.fork()
if not pid:
os.setpgid(0, 0)
os.execvp(sys.argv[1], sys.argv[1:])
def receiveSignal(sig, frame):
os.killpg(pid, sig)
signal.signal(signal.SIGINT, receiveSignal)
signal.signal(signal.SIGTERM, receiveSignal)
_, status = os.waitpid(-1, 0)
sys.exit(status)
The caller can use that to wrap the process that it prematurely terminate.
Node.js example:
const childProcess = require("child_process");
(async () => {
const process = childProcess.spawn(
"run-pgrp",
["bash", "-c", "echo start; sleep 600; echo done"],
{ stdio: "inherit" }
);
/* leaves orphaned process
const process = childProcess.spawn(
"bash",
["-c", "echo start; sleep 600; echo done"],
{ stdio: "inherit" }
);
*/
await new Promise(res => setTimeout(res, /* 1s */ 1000));
process.kill();
if (process.exitCode == null) {
await new Promise(res => process.on("exit", res));
}
})();
At the end of this program, the sleep process is terminated. If the command invoked directly without run-pgrp, the sleep process continues to run.
Case study: timeout
The GNU timeout utility is a program that may terminate its child process.
Notably, it runs the child in a new process group. This supports the conclusion that potential interruptions should be preceded by creating a new process group.
Interestingly, however, timeout puts itself in the process group as well, to avoid complexities around forwarding signals, but causing some strange behavior. https://unix.stackexchange.com/a/57692/56781
For example, in an interactive shell, run
bash -c "echo start; timeout 600 sleep 600; echo done"
Try to interrupt this (Ctrl+C). It doesn't respond, because timeout never gets the signal!
In contrast, my run-pgrp utility keeps itself in the original process group and forwards SIGINT/SIGTERM to the child group.
Is it necessary to null the spawn after pause and kill?
let child = spawn(cmd_str);
child.on('exit', code => {
child.stdin.pause();
child.kill();
child = null;
});
I don't want my module to have a chance to take the extra resources from the system after doing it's job.
No, it is not necessary.
JavaScript has a garbage collector fonctionnality that takes care of cleaning memory for variable that are not used any more.
One thing you could do instead to be sure the subprocess has been killed with success is to listen to the close or error event, as shown in the example from the NodeJS doc.
You could also track the process by its PID to make sure it is not alive anymore.
Note that I don't fully understand why you need to kill your process on exit, you should maybe focus on a clean exit of the subprocess program.
Im creating NodeJS application, that creates quite a few child processes. They are started by both spawn and exec (based on lib implementation). Some examples may be GraphicsMagick (gm) for image manipulation or Tesseract (node-tesseract) for OCR. Now I would like to gracefully end my application so I created shutdown hook:
function exitHandler() {
killer.waitForShutdown().then(function(){
logger.logInfo("Exited successfully.");
process.exit();
}).catch(function(err) {
logger.logError(err, "Error during server shutdown.");
process.exit();
});
}
process.on('exit', exitHandler);
process.on('SIGINT', exitHandler);
process.on('SIGTERM', exitHandler);
Exit handling itself works fine, it is waiting well and so on, but there is a catch. All "native" (gm, tesseract, ...) processes that run at that time are also killed. Exception messages only consists of "Command failed" and then content of command which failed e.g.
"Command failed: /bin/sh -c tesseract tempfiles/W1KwFSdz7MKdJQQnUifQFKdfTRDvBF4VkdJgEvxZGITng7JZWcyPYw6imrw8JFVv/ocr_0.png /tmp/node-tesseract-49073e55-0ef6-482d-8e73-1d70161ce91a -l eng -psm 3\nTesseract Open Source OCR Engine v3.03 with Leptonica"
So at least for me, they do not tell anything useful. I'm also queuing process execution, so PC don't get overloaded by 50 processes at one time. When running processes are killed by SIGINT, new processes that were queued are started just fine and finishes successfully. I have problem only with those few running at the time of receiving SIGINT. This behavior is same on Linux (Debian 8) and Windows (W10). From what I read here, people usually have opposite problem (to kill child processes). I tried to search if stdin gets somehow piped into child processes but I can't find it. So is this how its supposed to work? Is there any trick to prevent this behavior?
The reason this happens is because, by default, the detached option is set to false. If detached is false, the signals will also be sent to the child processes, regardless of whether you setup an event listener.
To stop this happening, you need to change your spawn calls to use the third argument in order to specify detached; for example:
spawn('ls', ['-l'], { detached: true })
From the Node documentation:
On Windows, setting options.detached to true makes it possible for the
child process to continue running after the parent exits. The child
will have its own console window. Once enabled for a child process, it
cannot be disabled.
On non-Windows platforms, if options.detached is set to true, the
child process will be made the leader of a new process group and
session. Note that child processes may continue running after the
parent exits regardless of whether they are detached or not. See
setsid(2) for more information.
Is there a way to kill make a child process "suicide"? I tried with process.exit(1) but apparently it kills the whole application I'm running. I just want to kill the child process (like when we call process.kill() from the "father" of the child process). Also calling process.kill() within the child process kills the whole application.
Any idea?
process is always a reference to the main process. But you can simply use this:
var spawn = require( "child_process" ).spawn;
// example child process
var grep = spawn( "grep", [ "ssh"] );
grep.on( "exit", function (code, signal) {
console.log( "child process terminated due to receipt of signal "+signal);
});
grep.kill( "SIGHUP" );
I guess it depends on how you use child processes. But if you don't have a reference to the child process, then there is not much you can do.
WARNING: Killing child processes is almost never a good idea. You should send a message to the child processes and handle it in that process.
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(){
pid_t pid = fork();
if(pid==0){
system("watch ls");
}
else{
sleep(5);
killpg(getpid(),SIGTERM); //to kill the complete process tree.
}
return 0;
}
Terminal:
anirudh#anirudh-Aspire-5920:~/Desktop/testing$ gcc test.c
anirudh#anirudh-Aspire-5920:~/Desktop/testing$ ./a.out
Terminated
for the first 5 secs the output of the "watch ls" is shown and then it terminates because I send a SIGTERM.
Question: How can a process kills itself ? I have done kill(getpid(),SIGTERM);
My hypothesis:
so during the kill() call the process switches to kernel mode. The kill call sends the SIGTERM to the process and copies it in the process's process table. when the process comes back to user mode it sees the signal in its table and it terminates itself (HOW ? I REALLY DO NOT KNOW )
(I think I am going wrong (may be a blunder) somewhere in my hypothesis ... so Please enlighten me)
This code is actually a stub which I am using to test my other modules of the Project.
Its doing the job for me and I am happy with it but there lies a question in my mind how actually a process kills itself. I want to know the step by step hypothesis.
Thanks in advance
Anirudh Tomer
Your process dies because you are using killpg(), that sends a signal to a process group, not to a process.
When you fork(), the children inherits from the father, among the other things, the process group. From man fork:
* The child's parent process ID is the same as the parent's process ID.
So you kill the parent along with the child.
If you do a simple kill(getpid(), SIGTERM) then the father will kill the child (that is watching ls) and then will peacefully exit.
so during the kill() call the process switches to kernel mode. The kill call sends the SIGTERM to the process and copies it in the process's process table. when the process comes back to user mode it sees the signal in its table and it terminates itself (HOW ? I REALLY DO NOT KNOW )
In Linux, when returning from the kernel mode to the user-space mode the kernel checks if there are any pending signals that can be delivered. If there are some it delivers the signals just before returning to the user-space mode. It can also deliver signals at other times, for example, if a process was blocked on select() and then killed, or when a thread accesses an unmapped memory location.
I think it when it sees the SIGTERM signal in its process tables it first kills its child processes( complete tree since I have called killpg() ) and then it calls exit().
I am still looking for a better answer to this question.
kill(getpid(), SIGKILL); // itself I think
I tested it after a fork with case 0: and it quit regular from separate parent process.
I don't know if this is a standard certification method ....
(I can see from my psensor tool that CPU usage return in 34% like a normal program code with
a counter stopped ) .
This is super-easy in Perl:
{
local $SIG{TERM} = "IGNORE";
kill TERM => -$$;
}
Conversion into C is left as an exercise for the reader.