fg, bg, jobs, ctrl+z

operating systems unix

Context

Process: A kernel level abstraction representing an executing program. It has a PID, PPID, GID, address space (with code, static data, stack, heap), a table of file descriptors etc.

Job: A shell level abstraction representing one or more processes grouped together into a single process group.

In Unix, processes can be paused by sending the SIGTSTP. The process is suspended and sent into the background

  sleep 100 && cat "Hello world" | echo
^Z # ctrl + z sends SIGTSTP to the process hence suspending it
[1]  + 78159 suspended  sleep 100

fg can be used to resume the paused process. fg -> foreground

  fg
[1]  + 78159 continued  sleep 100

In case there are multiple suspended jobs. fg brings the most recently suspended job to the foreground.

  jobs
[1]    suspended  sleep 100
[2]  - suspended  sleep 100 # - means previous suspended job
[3]  + suspended  sleep 100 # + means current suspended job. 

# invoking fg without arg will resume the current suspended job [3] and bring its process to the foreground
  fg
[3]    26648 continued  sleep 100

#invoking fg with job ID resumes that job and brings its process to the foreground.
  jobs
[1]  - suspended  sleep 100
[2]  + suspended  sleep 100
  fg %1
[1]  - 94189 continued  sleep 100

jobs shows all background jobs. These are processes suspended or sent to the background. Adding & after a command executes the command in a background process.

# executes the process in the background
  (sleep 100 && echo 'Will be sent to background') &
[3] 94611 # 3 is the job id, 94611 is the process PID
  jobs 
[1]  - suspended  sleep 100
[2]  + suspended  sleep 100 # the most recently suspended job.
[3]    running    ( sleep 100 && echo 'Will be sent to background'; ) # the running background process

More details on job vs process

Let’s walk through how the shell (zsh for example) creates a job and use it to manage processes that should be managed as one unit of execution.

This command below requires the shell to create two processes One to run wc |. Another to run cat

  wc | cat

The shell will create two processes and place them in a single process group. The ID of the process group is the ID of the first process in the group (The leader process).

  ps -o pid,ppid,pgid,tpgid,comm | grep -iE 'PPID|50626'
  PID  PPID  PGID TPGID COMM
50626 23401 50626 51212 wc
50627 23401 50626 51212 cat

Hitting ctrl + z suspends all processes in the process group. The shell then persists a job record of the process group. Creating a job records helps the shell track process groups that have been suspended or are running in the background.

# Both processes in the group are suspended.
  wc | cat
^Z
[1]  + 50626 suspended  wc |
       50627 suspended  cat

# Job record created after suspension
  jobs
[1]  + suspended  wc | cat

Using fg or bg, the job can be resumed to run in the foreground or background. These will resume the processes (by sending SIGCONT to the process group) in the process group and bring one of them to the foreground in the case of fg.

  ~ fg
[1]  + 50626 continued  wc |
       50627 continued  cat
wc: stdin: read: Interrupted system call # wc is not robost enough to resume when stdin read is interruped by a syscall. It should have retry the read :(

fg vs bg

# start and suspend a foreground process
  ~ sleep 100
^Z
[1]  + 59312 suspended  sleep 100
  ~ jobs
[1]  + suspended  sleep 100

# Resume the suspended process and run in background
  ~ bg
[1]  + 59312 continued  sleep 100
  ~ jobs
[1]  + running    sleep 100

# Bring background process to foreground then suspend again
  ~ fg
[1]  + 59312 running    sleep 100
^Z
[1]  + 59312 suspended  sleep 100

# Resume suspended process and bring to foreground then suspend
  ~ fg
[1]  + 59312 continued  sleep 100
^Z
[1]  + 59312 suspended  sleep 100
  ~ jobs
[1]  + suspended  sleep 100

# Kill suspended job. Running background jobs can also be terminated by providing the job ID.
  ~ kill %1
[1]  + 59312 terminated  sleep 100
  ~ jobs
  ~

← Back to all posts