Skip to content

Persistent terminal in Linux

Page last updated on: 2019-10-22

Jump to Use tmux if you are in a rush.

Problem

When you work on a remote server via ssh, you work under a terminal window. The problem is, if you logout or are disconnected when the remote job is running, you lose the job. The terminal and job both quit in such events.

This is because the ssh terminal is a process created by the ssh daemon for this connection, and the commands started in this terminal window are child processes of the terminal process.

           PID  PPID
root     20283  2365  0 16:23 ?        00:00:00 sshd: jmao [priv]
jmao     20363 20283  0 16:24 ?        00:00:00 sshd: jmao@pts/2
jmao     20364 20363  0 16:24 pts/2    00:00:00 -bash
jmao     20415 20364  0 16:31 pts/2    00:00:00 grep --color=auto ssh

ps -ef | grep 2365
root      2365     1  0 Oct02 ?        00:00:08 /usr/sbin/sshd -D

In the above example, I run a command to demonstrate such a relationship:

PID is process ID and PPID is parent process ID.

SSH daemon with process (pid=2365) was created by the system (1) and it listens to any ssh connections. When I connect to the server, the ssh daemon created two ssh processed with privilege separation (pid=20283, 20363) to take care of my connections. The ssh process 20283 runs on my behalf to start a terminal (pid=20364), and the grep command issued in this terminal has pid=20415 and knows its parent process is id=20364.

If this ssh session is disconnected, or logged out, process 20283 and 20363 will die, and so do its child processes 20364 and 20415.

In order for terminal child process stay alive after disconnection, the child processes must:

  • detach from its parent process;
  • be linked to another process that is not a child process of ssh connection.

Solutions

Detach an existing job

This method detaches and attaches the child process.

Summary:

  • In a terminal with running job, press CTRL-Z to stop job and regain the control of terminal.
  • Type bg to resume the stopped job in background.
  • Background job is still tied to current terminal process. Run disown %JOBID to detach the job.
  • Now you can exit terminal or disconnect, the job will keep running.

Explanation:

Suppose you run a job that takes some time to finish:

sleep 120

In another terminal, check this job's process id and its parent process id:

ps -ef | grep sleep
jmao     238727  91227  0 22:16 pts/3    00:00:00 sleep 120
ps -ef | grep 91227
jmao      91227  22442  0 Oct04 pts/3    00:00:00 bash

We find "sleep 120" is a child process of bash (terminal) with pid=22442.

Now go back to the terminal window where we start sleep 120, press CTRL-Z key combination.

(base) jmao@pc:~/projects/levich-computing$ sleep 120
^Z
[1]+  Stopped                 sleep 120
This stops sleep 120 and gives terminal control back as shown below:

(base) jmao@pc:~/projects/levich-computing$ jobs
[1]+  Stopped                 sleep 120

Now, types bg to resume job in the background:

(base) jmao@pc:~/projects/levich-computing$ bg
[1]+ sleep 120 &
(base) jmao@pc:~/projects/levich-computing$ jobs
[1]+  Running                 sleep 120 &
(base) jmao@pc:~/projects/levich-computing$ 

As the result, the sleep 120 is running in backgroud. Although we have the terminal control, the job is still a child process of bash. This means killing or quiting bash process 22442 will stop sleep 120 process.

ps -ef | grep sleep
jmao     238727  91227  0 22:16 pts/3    00:00:00 sleep 120

Since the job has id [1], run

disown -h %1

After the job is disowned, the job doesn't receive interruption signal from the shell. This means you can close the window or disconnect ssh without causing the job to quit.

(base) jmao@pc:~/projects/levich-computing$ ps -ef | grep sleep
jmao     239877  21989  0 23:57 ?        00:00:00 sleep 120
jmao     239884 153505  0 23:57 pts/4    00:00:00 grep --color=auto sleep

(base) jmao@pc:~/projects/levich-computing$ ps -ef | grep 21989
jmao      21989      1  0 Sep26 ?        00:00:00 /lib/systemd/systemd --user

The job becomes child of the system.

Run as a nohup background job

This method makes independent child process.

Summary:

A much simpler way comparing with the previous method is to submit a job with nohup and directly to the background. Essentially the following 4 steps

  • In a terminal with running job, press CTRL-Z to stop job and regain the control of terminal.
  • Type bg to resume the stopped job in background.
  • Run disown %JOBID to detach the job.
  • Now you can exit terminal or disconnect, the job will keep running.

are now:

nohup command &

Example:

nohup sleep 120 &
  • nohup means the command is immune to hangup signal.
  • & means the command will run in the background.

When a command is issued this way, the command will keep running even if the terminal window is closed or ssh is disconnected.

Use tmux

This method sends child process to 3rd process.

The above two methods have a drawback. That is the screen printout and command history are lost once the terminal/ssh window is closed.

tmux is a terminal multiplexer. A tmux session manages one or more sub-terminals and keeps them alive in detached mode even when ssh is disconnected:

Simple tmux usage

To start:

tmux

Run other commands in this tmux window as usual.

To detach tmux window:

  • Use CTRL-b, then "d"

To re-attach tmux window back:

tmux a

To list tmux sessions

tmux ls

To attach a specific tmux session

tmux a -t ID
ID is the session number identified by tmux ls.

To close a tmux session:

  • Use CTRL-D in an active tmux window.

Advanced tmux usage

The advanced usage involves splitting the pane. CTRL-b is key prefix, it is followed by another command key. We learned CTRL-b, d (CTRL-b followed by d) is to detach the pane. To split the pane:

  • CTRL-b, % : split vertically
  • CTRL-b, " : split horizontally

To navigate through panes:

  • CTRL-b, arrow key:

tmux also supports multiple windows. The window IDs are numbers shown at the bottom of tmux session screen: tmux

To create a new window:

  • CTRL-b, c

To navigate through windows: * CTRL-b, p * CTRL-b, Window ID

Command summary:

Goal command or key Explanation
Start tmux session tmux Start a new tmux session
List tmux sessions tmux ls List all tmux sessions
Re-attach a tmux session tmux a Re-attach the last tmux session
Re-attach a tmux session tmux a -t ID Re-attach a specific tmux session as listed in tmux ls
Detach tmux CTRL-b, d Detach current tmux session
Exit tmux CTRL-d or exit Exit from a tmux pane or window
Split tmux pane CTRL-b, % Split current tmux pane vertically
Split tmux pane CTRL-b, " Split current tmux pane horizontally
Navigate through tmux panes CTRL-b, ↑ Move up to another pane
Navigate through tmux panes CTRL-b, ↓ Move down to another pane
Navigate through tmux panes CTRL-b, → Move right to another pane
Navigate through tmux panes CTRL-b, ← Move left to another pane
Create a tmux window CTRL-b, c Create a new tmux window
Navigate through tmux windows CTRL-b, p move to previous tmux window
Navigate through tmux windows CTRL-b, n move to nex tmux window
Navigate through tmux windows CTRL-b, ID move to a tmux window with that ID

Last update: 2019-10-22