多数的服务端程序都需要在服务器上持续运行
比如使用nodejs运行一个http服务
1 | // server.js |
现在运行,直接执行node server.js
即可
但是按照这种方式运行一旦断开ssh的链接,该进程就会停止
以这种方式启动的进程,按
Ctrl+C
可以中断该进程
前台任务与后台任务
上面这种运行的方式,实际是创建了一个前台任务
在执行的命令后面加上&
符号,是将其作为一个后台任务
1 | node server.js & |
如果要让正在执行的前台任务转化为后台任务
可以先按Ctrl+Z
然后输入bg
Ctrl+Z
的作用是将当前的前台任务转换为后台任务,但是处于暂停状态,在输入bg之前是不能访问的
jobs
命令可以查看当前session的后台任务列表
后台任务有如下特点
- 继承当前会话(session)的标准输出(stdout)与错误输出(stderr)
上图中的执行方式并没有对输出进行重定向,所以转换为后台任务后依然会在命令行输出 - 不再继承当前会话(session)的标准输入(stdin)
上图中的执行方式也没有对标准输入进行重定向,所以标准输入仍然来自于命令行,当然这段代码当中并没有从标准输入获取内容的操作
如果有,该进程就会一直暂停执行
SIGNUP 信号
变成后台任务之后,该进程是否会继续执行,Linux的机制是这样的
- 用户准备断开session(比如logout命令或者exit命令)
- 系统向该session发出
SIGNUP
信号 - session将
SIGNUP
信号发给所有子进程 - 子进程收到
SIGNUP
信号后,自动中断
概括来说,就是进程是否会中断退出,就是取决于其是否会收到SIGNUP信号
后台任务是否会收到SIGNUP信号,是由huponexit
参数决定的
1 | shopt | grep huponexit |
大多数的Linux系统,该参数的值都是off,当然也不排除个别情况或者人为修改
所以多数情况下我们可以用后台任务来作为守护进程
disown 命令
该命令是将指定的一个后台任务移出后台任务列表
这样无论上面提到的参数设定是什么,它都不会再收到SIGNUP信号了
常见用法
1 | # 移出最近一个正在执行的后台任务 |
上图中的[1]
就是jobId
当然有些程序执行会有大量的标准输出
习惯上我们会将需要后台运行的进程标准输出、错误输出、标准输入进行重定向
1 | node server.js > stdout.txt 2 > stderr.txt < /dev/null & |
nohup 命令
实现的目标是一样的,这个命令还更加简单一些
1 | nohup node server.js & |
nohup
做了3件事
- 阻止SIGHUP信号发到这个进程。
- 关闭标准输入。该进程不再能够接收任何输入,即使运行在前台。
- 重定向标准输出和标准错误到文件nohup.out。
该命令实际上是把子进程与它所在的session分离了
但是它并不会自动把进程转换为后台任务,所以&
还是要加的
任务、进程与线程
任务(task)
本身是一个逻辑概念,是指由软件完成的一个活动,或者说是一系列共同达到某一目的的操作
它可以是一个或多个进程,也可以是线程
在说进程之前,必须要先说一下程序(program)
,它是一段静态的代码,是保存在非易失性存储器上的的指令和数据的有序集合,没有任何执行的概念
进程(process)
是指一个具有独立功能的程序在某个数据集合上的一次动态执行过程,它是操作系统进行资源分配和调度的基本单元。
它是程序的一次执行过程,包括了动态创建、调度、执行和消亡的整个过程(这一系列都由操作系统的调度机制来完成),它是程序执行和资源管理的最小单位。进程不但包括程序的指令和数据,而且包括程序计数器和处理器的所有寄存器以及存储临时数据的进程堆栈。从操作系统的角度看,进程是程序执行时相关资源的总称。当进程结束时,所有资源被操作系统自动回收。
一次任务的运行可以同时激活多个进程,这些进程相互合作来完成该任务的一个最终目标。
线程
进程是系统中程序执行和资源分配的基本单位。每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程在进行切换时操作系统的开销比较大。
为了提高效率,操作系统又引入了另一个概念——线程(thread)
。
线程是进程上下文中执行的代码序列,又称为轻量级的进程。它是操作系统能够调度的最小单元。线程可以对进程的内存空间和资源进行访问,并与同一进程中的其他线程共享。因此,线程的上下文切换的开销比进程小得多。一个进程可以拥有多个线程,其中每个线程共享该进程所拥有的资源。
要注意的是,由于线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。由此可知,多线程中的同步是非常重要的问题