11

Out of curiosity, I tried to kill Vim from inside vim --clean. I tried:

  • :!kill $PPID
  • :call system('kill ' . getpid())

However, nothing happens after running the commands above. The commands give no output, and v:shell_error is 0 in both cases. I noticed that kill works perfectly when run from outside Vim; Vim terminates when it receives the TERM signal: Vim: Caught deadly signal TERM Vim: Finished..

Why is it not possible to kill Vim from inside Vim using the commands above?

1
  • 1
    Note that !kill -9 $PPID works for me (but !kill $PPID) doesn't, so it's probably about how vim handles signals... But I'm not enough of a linux guru to give more insights :) Also kill without -9 works from a term buffer.
    – statox
    Mar 16, 2021 at 11:25

1 Answer 1

20

A process can block, ignore, or catch signals. You can see how a given process handles signals with this shell command:

$ cat /proc/PID/status | grep -E '^Sig(Blk|Ign|Cgt):'
                                       │   │   │
                                       │   │   └ caught signals
                                       │   └ ignored signals
                                       └ blocked signals

Replace PID with Vim's pid (process ID).

You'll get some output which could look like this:

SigBlk: 0000000000000000
SigIgn: 0000000000003000
SigCgt: 00000001ef824eff

The numbers on the right are bitmasks written in hexadecimal. To understand their meaning, you must convert them in binary; for example with bc(1):

$ echo 'ibase=16;obase=2;ABC123' | bc
101010111100000100100011

The index of each non-zero bit stands for the number of a signal.

So, for example, if you have this bitmask:

SigCgt: 00000000280b2603

It can be converted in binary, and interpreted like so:

SigCgt: 101010111100000100100011
                       │  │   ││
                       │  │   │└ the signal 1 is caught (SIGHUP)
                       │  │   └ the signal 2 is caught (SIGINT)
                       │  └ the signal 6 is caught (SIGUSR1)
                       └ ...

Here is a short shell script which I sometimes use while debugging a process:

#!/bin/bash

if [[ $# -eq 0 ]]; then
  cat <<EOF >&2
usage:
  $0 <pid>
EOF

  exit 64
fi

# https://unix.stackexchange.com/a/85365/289772
bitmask2signals() {
  i=0
  bits="$(printf 'ibase=16; obase=2; %X\n' "0x$1" | bc)"
  while [[ -n "${bits}" ]]; do
    i=$((i + 1))
    if [[ "${bits}" == *1 ]]; then
      printf ' %s(%s)' "$(kill -l "$i")" "$i"
    fi
    bits="${bits%?}"
  done
}

grep -E '^Sig(Blk|Ign|Cgt):' "/proc/$1/status" |
  while read -r a b; do
    printf '%s%s\n' "$a" "$(bitmask2signals "$b")"
  done

It's taken and adapted from this unix.stackexchange.com answer. If I pass the pid of a Vim process as an argument to the script, here is its output:

SigBlk:
SigIgn: PIPE(13) ALRM(14)
SigCgt: HUP(1) INT(2) QUIT(3) ILL(4) TRAP(5) ABRT(6) BUS(7) FPE(8) USR1(10) SEGV(11) USR2(12) TERM(15) CONT(18) XCPU(24) XFSZ(25) VTALRM(26) PROF(27) WINCH(28) PWR(30) SYS(31) (32) (33)

Notice that TERM(15) is on the third line SigCgt. SIGTERM is the default signal sent to a process when using kill(1) on linux. As you can see, Vim catches it.

For a process, catching a signal means that it has registered a handler which will be automatically run whenever the signal is received. This handler will determine the behavior of the process.

For Vim, I think the handler is defined here. Notice that the comment says that the caught signals are blocked when Vim is busy:

/*
 * Handling of SIGHUP, SIGQUIT and SIGTERM:
 * "when" == a signal:       when busy, postpone and return FALSE, otherwise
 *                           return TRUE
 * "when" == SIGNAL_BLOCK:   Going to be busy, block signals
 * "when" == SIGNAL_UNBLOCK: Going to wait, unblock signals, use postponed
 *                           signal
 * Returns TRUE when Vim should exit.
 */

My guess is that when you invoke system(), Vim is busy waiting for the shell command to finish; so it blocks the signal. This is confirmed by the fact that the issue disappears if you use job_start() instead:

:call job_start('kill ' . getpid())

Because in that case, Vim doesn't need to wait for the command to finish.

1
  • 1
    And for the signals table, man 7 ascii or kill -L.
    – Quasímodo
    Mar 16, 2021 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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