[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: CTL-z bug?
Eduardo A . Bustamante López
Re: CTL-z bug?
Tue, 13 Jan 2015 22:59:50 -0600
I think that the following text from the zshell's code is an interesting read
on the subject:
TL;DR: Another strategy is to "migrate" the while-loop to a child process the
moment that you hit ^Z, but, this is really hard (maybe impossible?) to do
* In most shells, if you do something like:
* cat foo | while read a; do grep $a bar; done
* the shell forks and executes the loop in the sub-shell thus created.
* In zsh this traditionally executes the loop in the current shell, which
* is nice to have if the loop does something to change the shell, like
* setting parameters or calling builtins.
* Putting the loop in a sub-shell makes life easy, because the shell only
* has to put it into the job-structure and then treats it as a normal
* process. Suspending and interrupting is no problem then.
* Some years ago, zsh either couldn't suspend such things at all, or
* it got really messed up when users tried to do it. As a solution, we
* implemented the list_pipe-stuff, which has since then become a reason
* for many nightmares.
* Pipelines like the one above are executed by the functions in this file
* which call each other (and sometimes recursively). The one above, for
* example would lead to a function call stack roughly like:
* (when waiting for the grep, ignoring execpline2 for now). At this time,
* zsh has built two job-table entries for it: one for the cat and one for
* the grep. If the user hits ^Z at this point (and jobbing is used), the
* shell is notified that the grep was suspended. The list_pipe flag is
* used to tell the execpline where it was waiting that it was in a pipeline
* with a shell construct at the end (which may also be a shell function or
* several other things). When zsh sees the suspended grep, it forks to let
* the sub-shell execute the rest of the while loop. The parent shell walks
* up in the function call stack to the first execpline. There it has to find
* out that it has just forked and then has to add information about the sub-
* shell (its pid and the text for it) in the job entry of the cat. The pid
* is passed down in the list_pipe_pid variable.
* But there is a problem: the suspended grep is a child of the parent shell
* and can't be adopted by the sub-shell. So the parent shell also has to
* keep the information about this process (more precisely: this pipeline)
* by keeping the job table entry it created for it. The fact that there
* are two jobs which have to be treated together is remembered by setting
* the STAT_SUPERJOB flag in the entry for the cat-job (which now also
* contains a process-entry for the whole loop -- the sub-shell) and by
* setting STAT_SUBJOB in the job of the grep-job. With that we can keep
* sub-jobs from being displayed and we can handle an fg/bg on the super-
* job correctly. When the super-job is continued, the shell also wakes up
* the sub-job. But then, the grep will exit sometime. Now the parent shell
* has to remember not to try to wake it up again (in case of another ^Z).
* It also has to wake up the sub-shell (which suspended itself immediately
* after creation), so that the rest of the loop is executed by it.
* But there is more: when the sub-shell is created, the cat may already
* have exited, so we can't put the sub-shell in the process group of it.
* In this case, we put the sub-shell in the process group of the parent
* shell and in any case, the sub-shell has to put all commands executed
* by it into its own process group, because only this way the parent
* shell can control them since it only knows the process group of the sub-
* shell. Of course, this information is also important when putting a job
* in the foreground, where we have to attach its process group to the
* controlling tty.
* All this is made more difficult because we have to handle return values
* correctly. If the grep is signaled, its exit status has to be propagated
* back to the parent shell which needs it to set the exit status of the
* super-job. And of course, when the grep is signaled (including ^C), the
* loop has to be stopped, etc.
* The code for all this is distributed over three files (exec.c, jobs.c,
* and signals.c) and none of them is a simple one. So, all in all, there
* may still be bugs, but considering the complexity (with race conditions,
* signal handling, and all that), this should probably be expected.
I hope this explains why bash does what it does.
- CTL-z bug?, Guillaume MULLER, 2015/01/13
- Re: CTL-z bug?, Chet Ramey, 2015/01/13
- Re: CTL-z bug?,
Eduardo A . Bustamante López <=