help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: is kill 0 to be used to terminate the current script


From: Greg Wooledge
Subject: Re: is kill 0 to be used to terminate the current script
Date: Tue, 21 Mar 2023 09:28:45 -0400

On Tue, Mar 21, 2023 at 01:27:08PM +0100, alex xmb ratchev wrote:
> On Tue, Mar 21, 2023, 12:58 Greg Wooledge <greg@wooledge.org> wrote:
> 
> DESCRIPTION
> >        The  kill()  system  call can be used to send any signal to any
> > process
> >        group or process.
> >
> >        If pid is positive, then signal sig is sent to the process with
> > the  ID
> >        specified by pid.
> >
> >        If pid equals 0, then sig is sent to every process in the process
> > group
> >        of the calling process.

> that one , to send signal also to $( jobs -p ) ?

You're basically asking how process groups work.  See below.

> never seen , must be .c manual

I pasted from "man 2 kill" on Linux.  I thought it was pretty obvious
that it was a man page, from the formatting, and that it was in section 2,
from the words "system call".

OK, next, let's quote something from "man bash" regarding process groups:

       To facilitate the implementation of the user interface to job  control,
       the operating system maintains the notion of a current terminal process
       group ID.  Members of this process group (processes whose process group
       ID is equal to the current terminal process group ID) receive keyboard-
       generated signals such as SIGINT.  These processes are said  to  be  in
       the  foreground.

After a bit more digging, I found "man 7 credentials" on Debian, which
also talks about process groups:

   Process group ID and session ID
       Each process has a session ID and a process group ID, both  represented
       using  the  type pid_t.  A process can obtain its session ID using get‐
       sid(2), and its process group ID using getpgrp(2).

       A child created by fork(2) inherits its parent's session ID and process
       group  ID.   A  process's session ID and process group ID are preserved
       across an execve(2).

       Sessions and process groups are abstractions devised to  support  shell
       job  control.   A process group (sometimes called a "job") is a collec‐
       tion of processes that share the same process group ID; the shell  cre‐
       ates  a  new  process  group for the process(es) used to execute single
       command or pipeline (e.g., the two processes  created  to  execute  the
       command  "ls | wc"  are placed in the same process group).  A process's
       group membership can  be  set  using  setpgid(2).   The  process  whose
       process  ID  is  the  same as its process group ID is the process group
       leader for that group.

So.  We have a few concepts to explore here.  The first is this thing
called a "process group ID".  Each process has one, but you don't normally
see it in "ps" output, because it's not one of the default fields in either
the "-f" or the "u" output format.  So we need to construct a special
command to see them.

unicorn:~$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
greg        1010     999  0 Jan24 pts/2    00:00:00 bash
greg      339327    1010  0 08:58 pts/2    00:00:00 ps -f
unicorn:~$ ps -o uid,pid,ppid,pgid,tty,time,cmd
  UID     PID    PPID    PGID TT           TIME CMD
 1000    1010     999    1010 pts/2    00:00:00 bash
 1000  339375    1010  339375 pts/2    00:00:00 ps -o uid,pid,ppid,pgid,tty,time

Now that we know how to see the PGID, we can verify what credentials(7)
is saying about pipelines.

unicorn:~$ PS1='pts/2:\w\$ '
pts/2:~$ sleep 100 | sleep 200 | grep foo

unicorn:~$ PS1='pts/3:\w\$ '
pts/3:~$ ps -t pts/2 -o pid,ppid,pgid,cmd
    PID    PPID    PGID CMD
   1010     999    1010 bash
 339818    1010  339818 sleep 100
 339819    1010  339818 sleep 200
 339820    1010  339818 grep foo

Using two terminal windows, I created a pipeline in one, and ran ps in
the other, so that I could see the PIDs and PGIDs of the processes in
the pipeline.  As promised, all the processes in the pipeline share
the same PGID (339818).

Therefore, if we wanted to kill the entire pipeline programatically (i.e.
not by simply pressing Ctrl-C in pts/2), we could send a signal to the
entire process group.  Remember this part from kill(2):

       If pid is less than -1, then sig  is  sent  to  every  process  in  the
       process group whose ID is -pid.

So, from pts/3 we could use this command:

    kill -TERM -339818

That would send a SIGTERM to each of the three processes in the process
group (a.k.a. the pipeline).

This leads to the next obvious question: how does a script get the process
group ID of a pipeline?

pts/2:~$ sleep 100 | sleep 200 | grep foo &
[1] 340241
pts/2:~$ echo "$!"
340241

pts/3:~$ ps -t pts/2 -o pid,ppid,pgid,cmd
    PID    PPID    PGID CMD
   1010     999    1010 bash
 340239    1010  340239 sleep 100
 340240    1010  340239 sleep 200
 340241    1010  340239 grep foo

That $! value isn't the process group ID.  It's the PID of the last command
in the pipeline.  To get the process group ID, we need to do more work.

pts/3:~$ ps -p 340241 -o pgid=
 340239

So, that's one way we can get it.  Maybe there's something cleaner?
No idea.

Now, you asked:

> that one , to send signal also to $( jobs -p ) ?

pts/2:~$ sleep 500 | sleep 600 &
[1] 340484
pts/2:~$ sleep 700 | sleep 800 | sleep 900 &
[2] 340490
pts/2:~$ jobs -p
340483
340488

It appears that "jobs -p" gives the process group IDs.  Interesting.  I
didn't know that (it's not in "help jobs" either).  But that's really
handy -- we can skip the PGID lookup.

We can also take advantage of the fact that (jobs -p) in a subshell has
special voodoo that lets it report the parent's jobs.

pts/2:~$ mapfile -t jobs < <(jobs -p)
pts/2:~$ declare -p jobs
declare -a jobs=([0]="340483" [1]="340488")

And then with a little shell magic:

pts/2:~$ echo kill -TERM "${jobs[@]/#/-}"
kill -TERM -340483 -340488
pts/2:~$ kill -TERM "${jobs[@]/#/-}"
pts/2:~$ 
[1]-  Terminated              sleep 500 | sleep 600
[2]+  Terminated              sleep 700 | sleep 800 | sleep 900



reply via email to

[Prev in Thread] Current Thread [Next in Thread]