> For the complete documentation index, see [llms.txt](https://pshizhsysu.gitbook.io/linux/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://pshizhsysu.gitbook.io/linux/other/jin-cheng.md).

# 进程

## 进程组（process group）

* 每个进程组都有一个leader进程称为进程组组长，进程组组长的pid与进程组pgid一致

## 会话（session）

* 会话是若干进程组的集合
* 一个会话最多拥有一个控制终端（tty或pts）
* 一个会话有且只有一个前台进程组，可以有若干后台进程组
* 会话的控制终端被前台进程组的所有进程共享
* 每个会话有一个会话首进程，该进程的pid与sid一致
* 当会话首进程退出时，会给该会话的所有进程发送sighup信号（kill -1 pid）

当我们使用ssh登录到一台主机时，其实就开启了一个bash进程，可以通过以下命令查看该bash进程的pid

```
$ echo $$
29575
```

创建bash进程的同时也为它创建一个会话以及一个控制终端，我们登录到主机时能够输入命令的那个就是控制终端。通过以下的命令查看会话id与终端名称等信息：

```
$ ps j 29575
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
 29571  29575  29575  29575 pts/2     29603 Ss       0   0:00 -bash
```

解释一下上面的前几列：

* PPID：父进程id
* PID：进程id
* PGID：进程组id
* SID：会话id
* TTY：控制终端名称
* TPGID：该session的前台进程组id

可以看出：pid等于pgid，所以bash进程是进程组29575的组长；pid等于sid，所以bash进程是会话29575的会话首进程。

接下来，我们在当前终端下起一个进程，先编写脚本 `a.sh`，内容如下：

```
#!/bin/bash

echo "a start"
/usr/bin/dockerd --config-file /etc/docker/daemon.json &
sleep 1000
echo "a end"
```

执行命令：

```
$ ./a.sh
```

接下来，我们在另一个终端（另一个ssh登录）下查看该session下的进程：

```
$ ps j -e | grep 29575
 29571  29575  29575  29575 pts/2     29575 Ss+      0   0:00 -bash
 29575  29633  29633  29575 pts/2     29575 S        0   0:00 /bin/bash ./a.sh
 29633  29634  29633  29575 pts/2     29575 Sl       0   0:00 /usr/bin/dockerd --config-file /etc/docker/daemon.json
 29633  29635  29633  29575 pts/2     29575 S        0   0:00 sleep 1000
```

可以看到，这几个进程共用一个控制终端（pts/2）。在这里要注意的是，a.sh在执行命令`sleep 1000`时，是起了一个新的进程，a.sh进程之所以没有退出是要等待`sleep 1000`进程结束返回。这几个进程的父子关系是：

```
bash
└── a.sh
    ├── dockerd
    └── sleep 1000
```

接下来，我们在新的终端下终止会话首进程bash进程：

```
$ kill -1 29575
```

执行该命令后，29575的bash进程的连接就断开了。我们查看会话29575中的进程情况：

```
$ ps j -e | grep 29575
1  29634  29633  29575 ?            -1 Sl       0   0:00 /usr/bin/dockerd --config-file /etc/docker/daemon.json
```

发现，a.sh进程与sleep进程不在了，但dockerd进程还在。我猜测是：会话首进程给进程组中的每个进程发送了sighup信号，`a.sh`进程与`sleep 1000`进程接收到信号后终端运行退出，而dockerd进程却忽略了该信号还继续执行。

这里我们可以手动给dockerd进程发送sighup信号，验证该进程会忽略该信号。执行以下命令，发现dockerd进程还在：

```
$ kill -1 29634
$ ps j -e | grep 29575
1  29634  29633  29575 ?            -1 Sl       0   0:00 /usr/bin/dockerd --config-file /etc/docker/daemon.json
```

同时，dockerd进程的tty变成了`?`，说明该进程已经没有了终端输入与输出能力；dockerd的父进程id变成1，说明该进程已被init进程管理；sid与pgid却还保持没变。

## `nohup` vs `&`

后台命令符号`&`的意思是，子进程进入后台执行模式，父进程不需要等待子进程的返回就可以继续往后面执行。

nohup的意思是，当进程接收到sighup信号时，选择忽略，不会主动结束执行。

## Reference

\[1] <https://www.ibm.com/developerworks/cn/linux/1702_zhangym_demo/index.html> \[2] <https://blog.csdn.net/outofmemo/article/details/54151326>
