进程组、session、前台任务、后台任务、守护进程

  • 进程组

进程组是一组进程的集合,每个进程都属于一个进程组,每个进程组有一个进程组leader进程进程组的ID(PGID)等于leader进程的ID。对大部分进程来说,它自己就是进程组的leader,并且进程组里面就只有它自己一个进程。

可以通过将信号发送给一个进程组,使进程组中的所有进程都收到该信号。

查看进程的细节


PID为进程自身的ID,PGID为进程所在的进程组的ID, PPID为进程的父进程ID。从上面的结果,我们可以知道ps和cat都是bash的子进程。

  • session(也称为会话)

一个或多个进程组可以构成一个会话 (session)。

一个会话中有一个领导进程(session leader)。会话领导进程的PID是会话的SID(session ID)。会话中的每个进程组称为一个工作(job)。会话可以有一个进程组成为会话的前台工作(foreground),而其他的进程组是后台工作(background)。每个会话可以连接一个控制终端(也可以不连接)。

会话的意义在于将多个工作囊括在一个终端,并取其中的一个工作作为前台,来直接接收该终端的输入输出以及终端信号。 其他工作在后台运行。当我们打开多个终端窗口时,实际上就创建了多个终端会话。每个会话都会有自己的前台工作和后台工作。工作组和会话机制在Linux的许多地方应用。

eg.

当用xshell连接到主机时,即创建了一个session。shell即是session的leader进程,随后shell里面运行的进程都将属于这个session,当shell退出后,该会话中的进程将退出。

shell里面启动一个进程后,一般都会将该进程放到一个单独的进程组,然后该进程fork的所有进程都会属于该进程组,比如多进程的程序,它的所有进程都会属于同一个进程组,当在shell里面按下CTRL+C时,该程序的所有进程都会收到SIGINT而退出。

  • 前台任务

前台任务是独占命令行窗口的任务,只有运行完了或者手动中止该任务,才能执行其他命令。shell中启动一个进程时,默认情况下,该进程是一个前台进程组的leader,可以收到用户的输入,并且可以将输出打印到终端,只有当该进程组退出后,shell才可以再响应用户的输入。

  • 后台任务

与前台任务相对应,后台任务在运行的时候,并不需要与用户交互,它们通常在不打扰用户其它工作的时候默默地执行。使shell可以继续响应用户的输入
后台任务继承当前会话的标准输出(stdout)和标准错误(stderr)。因此,后台任务的所有输出依然会同步地在命令行下显示。
不再继承当前session的标准输入(stdin),你无法向这个任务输入指令了。如果它试图读取标准输入,就会暂停执行(halt)。

可以看出,”后台任务”与”前台任务”的本质区别只有一个:是否继承标准输入。

前台任务变后台任务

  • 启动时变为后台任务 
    只要在命令的后面加上 & ,启动的进程就会成为“后台进程”。但是后台程序的输出仍然会打印到终端,影响用户输入。可以通过yourcommand>log.out  &,将输出重定向到文件中。
  • 正在运行的“前台任务”变为后台任务 
    先 ctrl+z ,之后执行 bg 命令。相当于让最近一个暂停的“后台任务”继续执行。 

CTRL+Z 和 CTRL+C的对比 
  CTRL+Z 和 CTRL+C 都是中断命令,但是他们的作用却不一样. CTRL+C 是强制中断程序的执行,而 CTRL+Z 的是将任务中断,但是此任务并没有结束,仍然在进程中,只是维持挂起的状态,用户可以使用 fg/bg 操作继续前台或后台的任务。

对于后台运行的进程组,在shell里面体现为job的概念,即一个后台进程组就是一个job,可以通过jobs命令查看后台运行的进程组。也可以通过fg命令将后台进程组切换到前端,这样就可以继续接收用户的输入了。如下


  • 守护进程
守护进程(daemon)是指在UNIX或其他多任务操作系统中在 后台执行 的电脑程序, 并不会接受电脑用户的直接操控 如果一个进程永远都是以后台方式启动,并且不能受到Shell退出影响而退出,一个正统的做法是将其创建为守护进程(daemon)。守护进程值得是系统长期运行的后台进程,类似Windows服务。守护进程信息通过ps –a无法查看到,需要用到–x参数,当使用这条命令的时候,往往还附上-j参数以查看作业控制信息,其中TPGID一栏为-1就是守护进程

守护进程与后台进程

后台进程的文件描述符是继承于父进程,例如shell,所以它也可以在当前终端下显示输出数据。但是daemon进程自己变成了进程组长,其文件描述符号和控制终端没有关联,是控制台无关的。

基本上任何一个程序都可以后台运行,但守护进程是具有特殊要求的程序,比如要脱离自己的父进程,成为自己的会话组长等,这些要在代码中显式地写出来换句话说,守护进程肯定是后台进程,但反之不成立。 

用户退出session之后,后台任务是否会继续执行是判定是否为“守护进程”的依据 
  session退出后,linux系统设计如下:

session就是我们平常所说的终端窗口 

  1. 用户准备退出 session
  2. 系统向该 session 发出SIGHUP信号
  3. session 将SIGHUP信号发给所有子进程
  4. 子进程收到SIGHUP信号后,自动退出

前台任务会随着session的退出而退出是因为它收到了SIGHUP信号。
后台任务是否会受到SIGNUP信号,取决于shell的  huponexit  参数。可以通过  $ shopt | grep huponexit  查看该参数的值。

大多数Linux系统,这个参数默认关闭(off)。因此,session退出的时候(exit??还是直接关闭。待验证),不会把SIGHUP信号发给”后台任务”,即此时的后台任务是守护进程,但这显然不够安全。

更安全地创建守护进程: disown 命令 
  通过”后台任务”启动”守护进程”并不保险,因为有的系统的huponexit参数可能是打开的(on)状态。 更保险的方法是使用disown命令。disown命令可以将指定任务从”后台任务”列表(jobs命令的返回结果)之中移除。一个”后台任务”只要不在这个列表之中,session 就肯定不会向它发出SIGHUP信号。

$ node server.js &
$ disown

  执行上面的命令以后,server.js进程就被移出了”后台任务”列表。你可以执行jobs命令验证,输出结果里面,不会有这个进程。 
  但是,这样还存在问题。因为”后台任务”的标准 I/O 继承自当前 session,disown命令并没有改变这一点。一旦”后台任务”读写标准 I/O,就会发现它已经不存在了,所以就报错终止执行。 
  为了解决这个问题,需要对”后台任务”的标准 I/O 进行重定向。

$ node server.js > stdout.txt 2> stderr.txt < /dev/null &
$ disown

  上面这样执行,基本上就没有问题了。

更简便地创建守护进程: nohup 命令 
还有比disown更方便的命令,就是 nohup

$ nohup node server.js &

nohup命令对server.js进程做了三件事。
1.阻止SIGHUP信号发到这个进程。 
2.将stdin重定向到/dev/null,于是该进程不再能够接收任何输入,即使运行在前台。 
3.重定向标准输出和标准错误到文件nohup.out(有时在文件中看不到输出,有可能是程序没有调用flush)
4.调用exec启动指定的命令(nohup进程将会被新进程取代,但进程ID不变)

从上面nohup干的事可以看出,通过nohup启动的程序有这些特点:

  1. nohup程序不负责将进程放到后台,这也是为什么我们经常在nohup命令后面要加上符号“&”的原因
  2. 由于stdin、stdout和stderr都被重定向了,nohup启动的程序不会读写tty
  3. nohup启动的进程本质上还是属于当前session的一个进程组,所以在当前shell里面可以通过jobs看到nohup启动的程序
  4. 当session leader退出后,该进程会收到SIGHUP信号,但由于nohup帮我们忽略了该信号,所以该进程不会退出
  5. 由于session leader已经退出,而nohup启动的进程属于该session,于是出现了一种情况,那就是通过nohup启动的这个进程组所在的session没有leader,这是一种特殊的情况,内核会帮我们处理这种特殊情况,这里就不再深入介绍

shell中session的退出

当session 中leader进程退出,将导致它所连接终端被hangup,这就意味着该会话结束。但是对于会话的结束,并不会意味着该会话的所以进程都结束。对于daemon进程,在会话中创建,但是不依赖于会话,是常驻在后台的进程。

具体来说当终端hangup时候,内核对会话的leader进程发送SIGHUP信号,它收到SIGHUP信号后并不是马上退出,而是向它的子进程都各自发送一个SIGHUP,将他们都杀死后,自己才退出,但是如果当该leader进程主动退出,而导致的终端hangup那么就不会发送SIGHUP信号给子进程了。

如果又想要某个进程称为常驻后台进程,不随session退出而退出,有下面几个方法:
1. 避免shell发送SIGHUP信号: a. 主动调用exit,而不是直接断开终端;b. 两次fork,因为shell只给子进程发送SIGHUP信号,不给孙进程发送。
2. 忽略SIGHUP信号:进程捕捉到该信号将该信号忽略就行了

3. 通过setsid()系统调用,那么该调用进程将会退出该session而建立一个新的session。

  • stdin、stdout和stderr

/dev/null 表示空设备文件

stdin标准输入,文件标志符0

stdout标准输出,文件标志符1

stderr标准错误输出,文件标志符2

总的来说,stdin,stdout和stderr还是和终端有密切关系,通常在生产环境时,会将这3个流重定向到其它文件。比如编写守护进程的时候,因为守护进程和终端无关,所以往往会将stdin,stdout和stderr重定向到/dev/null去

  • 2>&1解析
  1. 从command>/dev/null说起

其实这条命令是一个缩写版,对于一个重定向命令,肯定是a > b这种形式,command > /dev/null相当于执行了command 1 > /dev/null。执行command产生了标准输出stdout(用1表示),重定向到/dev/null的设备文件中。

     2. command>a 2>a 与 command>a 2>&1的区别

command>a 2>a,等价于command 1>a 2>a;command>a 2>&1,等价于command 1>a 2>&1。

两者都可以理解为执行command产生的标准输入重定向到文件a中,标准错误也重定向到文件a中。但两者的区别在于command>a 2>&1只打开一次文件a,command>a 2>a会打开文件两次,并导致stdout被stderr覆盖。&1的含义就可以理解为用标准输出的引用,引用的就是重定向标准输出产生打开的a。从IO效率上来讲,command 1>a 2>&1比command 1>a 2>a的效率更高。

ctrl+c会发出SIGINT信号,该信号是程序终止(interrupt)信号,用于通知前台进程组终止进程。

终端关闭时,SIGHUP被发送给session首进程,进行响应进程的关闭。

  • ctrl-c, ctrl-z, ctrl-d
ctrl-c: ( kill foreground process ) 发送 SIGINT 信号给前台进程组中的所有进程,强制终止程序的执行;
ctrl-z: ( suspend foreground process ) 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程,而并非结束进程,用户可以使用使用fg/bg操作恢复执行前台或后台的进程。fg命令在前台恢复执行被挂起的进程,此时可以使用ctrl-z再次挂起该进程,bg命令在后台恢复执行被挂起的进程,而此时将无法使用ctrl-z再次挂起该进程;
 一个比较常用的功能:
    正在使用vi编辑一个文件时,需要执行shell命令查询一些需要的信息,可以使用ctrl-z挂起vi,等执行完shell命令后再使用fg恢复vi继续编辑你的文件(当然,也可以在vi中使用!command方式执行shell命令,但是没有该方法方便)。

ctrl-d: ( Terminate input, or exit shell ) 一个特殊的二进制值,表示 EOF,作用相当于在终端中输入exit后回车;

还有以下几个:
ctrl-/    发送 SIGQUIT 信号给前台进程组中的所有进程,终止前台进程并生成 core 文件
ctrl-s   中断控制台输出
ctrl-q   恢复控制台输出
ctrl-l    清屏

转自:

https://segmentfault.com/a/1190000009152815

https://blog.csdn.net/yanglovefeng/article/details/7872251

https://blog.csdn.net/hust_sheng/article/details/50766752

https://blog.csdn.net/mylizh/article/details/38385739

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值