为什么需要会话(session)和进程组(process group)?怎样安全的在后台运行程序?

What

A process group is a collection of related processes which can all be signalled at once.

A session is a collection of process groups, which are either attached to a single terminal device (known as the controlling terminal) or not attached to any terminal.

Sessions are used for job control: one of the process groups in the session is the foreground process group, and can be sent signals by terminal control characters. You can think of a session with a controlling terminal as corresponding to a “login” on that terminal. (Daemons normally disassociate themselves from any controlling terminal by creating a new session without one.)

e.g. if you run some_app from the shell, the shell creates a new process group for it, and makes that the foreground process group of the session. (some_app might create some child processes; by default they will be part of the same process group.) If you then press ^Z, some_app’s process group is signalled to stop it; and the shell’s process group is switched to be the foreground process group again. Then e.g.bg %1 would start some_app’s process group again, but keep it running in the background.

Why

  • 使用进程组(process group)可以方便我们的管理,比如我可以一次性发信号给同一个进程中的所有进程。

kill -SIGHUP -group_pid

  • 使用会话(Session)可以方便我们管理进程,比如在会话leader退出(收到SIGHUP信号)的时候,session leader process会发SIGHUP信号给该Session中的其他进程。
  • 为什么要有前台进程组(foreground)和后台进程组(background)的概念?所谓的前台进程组,就是我在控制终端(controlling terminal)中发信号(比如说ctrl + z, ctrl+c),信号会被送到的进程组。

Example

  1. 我们在一个终端(终端1)中执行:
jimmy@Jimmy:~$ top | cat
  1. 在另一个终端(终端2)中执行:
jimmy@Jimmy:~$ ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 7902  7903  7903  7903 pts/3     7985 Ss    1000   0:00 -bash
 7903  7985  7985  7903 pts/3     7985 S+    1000   0:00  \_ top
 7903  7986  7985  7903 pts/3     7985 S+    1000   0:00  \_ cat
 7214  7215  7215  7215 pts/4     7990 Ss    1000   0:01 -bash
 7215  7990  7990  7215 pts/4     7990 R+    1000   0:00  \_ ps fj

我们可以看到:

  • top和cat的父进程都是bash(7903)
  • top和cat的都属于7985进程组
  • top和cat以及bash都属于7903 Session
  • bash是Sesiong leader(STAT Ss)
  • top和cat都属于前台进程组(STAT S+)
  1. 在终端1中执行"ctrl+z"
  2. 在终端2中执行:
jimmy@Jimmy:~$ ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 7902  7903  7903  7903 pts/3     7903 Ss+   1000   0:00 -bash
 7903  7985  7985  7903 pts/3     7903 T     1000   0:00  \_ top
 7903  7986  7985  7903 pts/3     7903 T     1000   0:00  \_ cat
 7214  7215  7215  7215 pts/4     7989 Ss    1000   0:01 -bash
 7215  7989  7989  7215 pts/4     7989 R+    1000   0:00  \_ ps fj

我们可以看到到top和cat都处于Stop状态。这是因为,我们在终端1中发送"Ctrl + Z"给到了前台进程组。
5. 接下来,我们执行:

jimmy@Jimmy:~$ kill -SIGHUP 7903
jimmy@Jimmy:~$ ps jf
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 7214  7215  7215  7215 pts/4     7989 Ss    1000   0:01 -bash
 7215  7989  7989  7215 pts/4     7989 R+    1000   0:00  \_ ps fj

我们看到,发送SIGHUP信号给Session leadder,导致所有Session中的进程都退出了。

如何让Linux进程在后台可靠运行(不被KILL掉)

该部分截取自

我们知道,当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:
- 要么让进程忽略HUP信号
- 要么让进程运行在新的会话里从而成为不属于此终端的子进程。


1. nohup

nohup 无疑是我们首先想到的办法。顾名思义,nohup 的用途就是让提交的命令忽略 hangup 信号。让我们先来看一下 nohup 的帮助信息:

NOHUP(1)                        User Commands                        NOHUP(1)
 
NAME
       nohup - run a command immune to hangups, with output to a non-tty
 
SYNOPSIS
       nohup COMMAND [ARG]...
       nohup OPTION
 
DESCRIPTION
       Run COMMAND, ignoring hangup signals.
 
       --help display this help and exit
 
       --version
              output version information and exit
可见,nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上"&"来将命令同时放入后台运行,也可用">filename 2>&1"来更改缺省的重定向文件名。

nohup 示例
[root@pvcent107 ~]# nohup ping www.ibm.com &
[1] 3059
nohup: appending output to `nohup.out'
[root@pvcent107 ~]# ps -ef |grep 3059
root      3059   984  0 21:06 pts/3    00:00:00 ping www.ibm.com
root      3067   984  0 21:06 pts/3    00:00:00 grep 3059
[root@pvcent107 ~]#
2。setsid

nohup 无疑能通过忽略 HUP 信号来使我们的进程避免中途被中断,但如果我们换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能帮助我们做到这一点。让我们先来看一下 setsid 的帮助信息:
SETSID(8)                 Linux Programmer’s Manual                 SETSID(8)
 
NAME
       setsid - run a program in a new session
 
SYNOPSIS
       setsid program [ arg ... ]
 
DESCRIPTION
       setsid runs a program in a new session.
可见 setsid 的使用也是非常方便的,也只需在要处理的命令前加上 setsid 即可。

setsid 示例
[root@pvcent107 ~]# setsid ping www.ibm.com
[root@pvcent107 ~]# ps -ef |grep www.ibm.com
root     31094     1  0 07:28 ?        00:00:00 ping www.ibm.com
root     31102 29217  0 07:29 pts/4    00:00:00 grep www.ibm.com
[root@pvcent107 ~]#
值得注意的是,上例中我们的进程 ID(PID)为31094,而它的父 ID(PPID)为1(即为 init 进程 ID),并不是当前终端的进程 ID。请将此例与nohup 例中的父 ID 做比较。

3。&

这里还有一个关于 subshell 的小技巧。我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。

当我们将"&"也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。让我们来看看为什么这样就能躲过 HUP 信号的影响吧。

subshell 示例
[root@pvcent107 ~]# (ping www.ibm.com &)
[root@pvcent107 ~]# ps -ef |grep www.ibm.com
root     16270     1  0 14:13 pts/4    00:00:00 ping www.ibm.com
root     16278 15362  0 14:13 pts/4    00:00:00 grep www.ibm.com
[root@pvcent107 ~]#
从上例中可以看出,新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。

API related to Session and Process Group

10.6 Sessions and Process Groups
In Linux, as in other Unix systems, users normally interact with groups of related processes. Although they initially log in to a single terminal and use a single process (their shell, which provides a command-line interface), users end up running many processes as a result of actions such as

Running noninteractive tasks in the background
Switching among interactive tasks via job control, which is discussed more fully in Chapter 15
Starting multiple processes that work together through pipes
Running a windowing system, such as the X Window System, which allows multiple terminal windows to be opened
In order to manage all of these processes, the kernel needs to group the processes in ways more complicated than the simple parent-child relationship we have already discussed. These groupings are called sessions and process groups. Figure 10.1 shows the relationship among sessions, process groups, and processes.

10.6.1 Sessions
When a user logs out of a system, the kernel needs to terminate all the processes the user had running (otherwise, users would leave a slew of old processes sitting around waiting for input that can never arrive). To simplify this task, processes are organized into sets of sessions. The session's ID is the same as the pid of the process that created the session through the setsid() system call. That process is known as the session leader for that session group. All of that process's descendants are then members of that session unless they specifically remove themselves from it. The setsid() function does not take any arguments and returns the new session ID.

#include <unistd.h>

pid_t setsid(void);
10.6.2 Controlling Terminal
Every session is tied to a terminal from which processes in the session get their input and to which they send their output. That terminal may be the machine's local console, a terminal connected over a serial line, or a pseudo terminal that maps to an X window or across a network (see Chapter 16 for information on pseudo terminal devices). The terminal to which a session is related is called the controlling terminal (or controlling tty) of the session. A terminal can be the controlling terminal for only one session at a time.

Although the controlling terminal for a session can be changed, this is usually done only by processes that manage a user's initial logging in to a system. Information on how to change a session's controlling tty appears in Chapter 16, on pages 338-339.

10.6.3 Process Groups
One of the original design goals of Unix was to construct a set of simple tools that could be used together in complex ways (through mechanisms like pipes). Most Linux users have done something like the following, which is a practical example of this philosophy:

ls | grep "^[aA].*\.gz" | more
Another popular feature added to Unix fairly early was job control. Job control allows users to suspend the current task (known as the foreground task) while they go and do something else on their terminals. When the suspended task is a sequence of processes working together, the system needs to keep track of which processes should be suspended when the user wants to suspend "the" foreground task. Process groups allow the system to keep track of which processes are working together and hence should be managed together via job control.

Processes are added to a process group through setpgid().

int setpgid(pid_t pid, pid_t pgid);
pid is the process that is being placed in a new process group (0 may be used to indicate the current process). pgid is the process group ID the process pid should belong to, or 0 if the process should be in a new process group whose pgid is the same as that process's pid. Like sessions, a process group leader is the process whose pid is the same as its process group ID (or pgid).

The rules for how setpgid() may be used are a bit complicated.

A process may set the process group of itself or one of its children. It may not change the process group for any other process on the system, even if the process calling setpgid() has root privileges.
A session leader may not change its process group.
A process may not be moved into a process group whose leader is in a different session from itself. In other words, all the processes in a process group must belong to the same session.
setpgid() call places the calling process into its own process group and its own session. This is necessary to ensure that two sessions do not contain processes in the same process group.

A full example of process groups is given when we discuss job control in Chapter 15.

When the connection to a terminal is lost, the kernel sends a signal (SIGHUP; see Chapter 12 for more information on signals) to the leader of the session containing the terminal's foreground process group, which is usually a shell. This allows the shell to terminate the user's processes unconditionally, notify the processes that the user has logged out (usually, through a SIGHUP), or take some other action (or inaction). Although this setup may seem complicated, it lets the session group leader decide how closed terminals should be handled rather than putting that decision in the kernel. This allows system administrators flexibile control over account policies.

Determining the process group is easily done through the getpgid() and getpgrp() functions.

pid_t getpgid(pid_t pid)
Returns the pgid of process pid. If pid is 0, the pgid of the current process is returned. No special permissions are needed to use this call; any process may determine the process group to which any other process belongs.

pid_t getpgrp(void)
Returns the pgid of the current process (equivalent to getpgid(0)).
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值