I read that killing the parent process will get the child process attached to init (PPID=1).

To test this I created three shells and sent the second one the kill signal:

# first process "a"
# second process "b"
sh-5.0$ PS1=b$
# thrid process "c"
sh-5.0$ PS1=c$
# killing process "b"
c$kill -9 $PPID

(Killed and exit are printed to the screen, not entered)

But the third shell is killed as well instead of being attached to init. Why and by whom?

  • 1
    Are you shure that the third shell was killed? – schily Oct 5 '20 at 14:00
  • @DopeGhoti SIGTERM will not kill an interactive shell – laktak Oct 5 '20 at 14:37

I'll recreate your experiment:

# First shell
$ echo $$

# Start the second shell
$ bash
$ echo $$ $PPID
41934 41903

# Start the third shell
$ bash
$ echo $$ $PPID
41938 41934

Now, in a second terminal, I'll use strace to monitor the system calls executed and signals handled by the third shell. Note that the shell is blocked waiting for input:

$ strace -p 41938
strace: Process 41938 attached
pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])

In a third terminal, I'll kill the second shell (the parent of the one I'm tracing):

$ kill -9 41934

At this time, I observe nothing in the strace output -- the third shell is still running.

Now, if I tried to type something in first terminal, I see the following in the strace output:

read(0, 0x7ffd6879ebbf, 1)              = -1 EIO (Input/output error)
write(2, "exit\n", 5)                   = 5
setpgid(0, 41938)                       = 0
exit_group(0)                           = ?

The third shell failed to read from standard input (file descriptor 0 -- the first parameter to read in this case) and terminated itself. Killing the parent process seems to have changed the state of the terminal device in some way that caused the read() in the child to fail.

Let's repeat the experiment, but this time trace the first shell -- the parent of the process that I'll kill.

$ echo $$

$ bash
$ echo $$ $PPID
48163 48134

$ bash
$ echo $$ $PPID
48169 48163

In a separate terminal, trace the first shell:

$ strace -p 48134
strace: Process 48134 attached

In another terminal, kill the second shell:

$ kill -9 48163

What did the original shell do in response to its child's death?

rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0
ioctl(255, TIOCSPGRP, [48134])          = 0

Notice the call to ioctl() with the TIOCSPGRP parameter. When the first shell noticed that its child terminated, it changed the foreground process group ID of the terminal back to itself (note that fd 255 is also an open file descriptor to the terminal device). That's what caused the read() in the third shell to fail -- it tried to read from the device, but was not in the foreground process group.

  • Thank you for the detailed explanation! What threw me off was that it read a$ after the kill but that was just because b exited and c was still waiting ... – laktak Oct 5 '20 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.