Search code examples
bashshellsignalszshjobs

How does Bash (and other shells) handle detaching STDIN when `bg`ing a stopped process?


I'm in the process of writing my own shell and I have support for job control[1] (akin to jobs). I can stop processes, resume them in the foreground and also background[2] them. That all largely works as expected - at least from the user's perspective.

However the issue I have is when resuming a job in the background any applications that read from STDIN will compete with readline (I've actually written my own readline API, for reasons out of scope of this question) and this breaks usability of the shell.

These instances are quite rare I'll admit, but my understanding is what should normally happen is any backgrounded processes that requires reading from STDIN[3] are send a SIGTTIN[4] signal.

My issue is how do I monitor that applications read from STDIN - so that I can send SIGTTIN when required?

This is where my research has come to a dead end. So I'm interested in how other shells handle this kind of problem.

Below are some references to help explain what I'm trying to do in case my description above wasn't very clear:

[1] https://en.wikipedia.org/wiki/Job_control_(Unix)#Overview

[2] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bg.html

[3] https://en.wikipedia.org/wiki/Job_control_(Unix)#Implementation

[4] https://en.wikipedia.org/wiki/Signal_(IPC)#SIGTTIN


Solution

  • My understanding:

    • The shell is usually the session leader which allocates the controlling terminal.
    • Each job (may have multiple processes, like ls | wc -l) is a process group.
    • Only the foreground pgrp can read from the controlling terminal. (The fg pgrp may have multiple processes and these processes CAN read from the controlling termianl at the same time.)
    • The shell calls tcsetpgrp() to set the foreground pgrp (e.g. when we start a new job, or put a bg job back to fg with fg).
    • It's the kernel (tty driver) who sends SIGTTIN to a background process which tries to read from the controlling terminal.
    • The shell does not know when a process would read from the controlling terminal. Instead, the shell monitors the job's status change. When a process is stopped by SIGTTIN, the shell would receive SIGCHLD and then it can call waitpid() to get more info.