1、守护进程定义
守护进程(daemon)是指在后台运行的,没有控制终端与之相连的进程。它独立于控制终端,周期性地执行某种任务。
Linux的大多数服务器就是用守护进程的方式实现的。如web服务器进程http等。守护进程在后台运行,类似于Windows中的系统服务。
2、守护进程的特点
Linux系统启动时会启动很多系统服务进程,这些系统服 务进程没有控制终端,不能直接和用户交互。
其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,
它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)。
在Linux中,每个系统与用户进行交流的界面成为终端,每一个从此终端开始运行的进程都会依附于这个终端,
这个终端被称为这些进程的控制终端,当控制终端被关闭的时候,相应的进程都会自动关闭。
但是守护进程却能突破这种限制,它脱离于终端并且在后台运行,
并且它脱离终端的目的是为了避免进程在运行的过程中的信息在任何终端中显示并且进程也不会被任何终端所产生的终端信息所打断。
它从被执行的时候开始运转,知道整个系统关闭才退出(当然可以认为的杀死相应的守护进程)。
如果想让某个进程不因为用户或中断或其他变化而影响,那么就必须把这个进程变成一个守护进程。
3、查看系统中的进程
ps axj
参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,
参数x表示不仅列有控制终端的进程,也列出所无控制终端的进程,
参数j表示列出与作业控制相关的信息。
凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,
因此没有程序文件名和命令行, 通常采用以k开头的名字,表示Kernel。守护进程通 常采用以d结尾的名字,表示Daemon。
4、daemon()守护进程函数
原型 int daemon(int nochdir,int noclose)
其中,nochdir参数用于指定是否改变工作目录,如果给它传递0,则工作目录将被设置为“/”(根目录),否则继续使用当前工作目录。
noclose参数为0时,标准输入、标准输出和标准错误输出都被重定向到/dev/null文件,否则依然使用原来的设备。
该函数成功时返回0,失败返回-1,并设置errno。
例如:
daemon(0,0):nochdir为0 改为根目录,noclose为0 关闭所有文件描述符;
将当前工作目录更改为根目录的作用:
防止当前目录有一个目录被删除,导致守护进程无效。
使用fork()创建的子进程是继承了父进程的当前工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。
因此通常的做法是让“/”作为守护进程的当前目录,当然也可以指定其他的别的目录来作为守护进程的工作目录。
#include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <linux/fs.h> int main(){ daemon(0,0); //下面可以写自己的操作... while(1){ sleep(1); } return 0; }
5、前台任务与后台任务
参考阮一峰的网络日志,写的非常好。
"前台任务"(foreground job)。它会独占命令行窗口,只有运行完了或者手动中止,才能执行其他命令。
变成守护进程的第一步,就是把它改成"后台任务"(background job)。
sh watchdog.sh &
只要在命令的尾部加上符号&
,启动的进程就会成为"后台任务"。
如果要让正在运行的"前台任务"变为"后台任务",可以先按ctrl + z
,然后执行bg
命令(让最近一个暂停的"后台任务"继续执行)。
"后台任务"有两个特点:
1、继承当前 session (对话)的标准输出(stdout)和标准错误(stderr)。因此,后台任务的所有输出依然会同步地在命令行下显示。
2、不再继承当前 session 的标准输入(stdin)。你无法向这个任务输入指令了。如果它试图读取标准输入,就会暂停执行(halt)。
可以看到,"后台任务"与"前台任务"的本质区别只有一个:是否继承标准输入。所以,执行后台任务的同时,用户还可以输入其他命令。
6、SIGHUP信号
用户退出 session 以后,Linux系统是这样设计的。
1、用户准备退出 session 2、系统向该 session 发出SIGHUP信号 3、session 将SIGHUP信号发给所有子进程 4、子进程收到SIGHUP信号后,自动退出
上面的流程解释了,为什么"前台任务"会随着 session 的退出而退出:因为它收到了SIGHUP
信号。
那么,"后台任务"是否也会收到SIGHUP
信号?
这由 Shell 的huponexit
参数决定的。
shopt | grep huponexit
执行上面的命令,就会看到huponexit
参数的值。
大多数Linux系统,这个参数默认关闭(off
)。因此,session 退出的时候,不会把SIGHUP
信号发给"后台任务"。
所以,一般来说,"后台任务"不会随着 session 一起退出。
7、disown 命令
通过"后台任务"启动"守护进程"并不保险,因为有的系统的huponexit
参数可能是打开的(on
)。
更保险的方法是使用disown
命令。它可以将指定任务从"后台任务"列表(jobs
命令的返回结果)之中移除。一个"后台任务"只要不在这个列表之中,session 就肯定不会向它发出SIGHUP
信号。
$ sh watchdog.sh &
$ disown
执行上面的命令以后,sh watchdog.sh
进程就被移出了"后台任务"列表。你可以执行jobs
命令验证,输出结果里面,不会有这个进程。
disown
的用法如下:
# 移出最近一个正在执行的后台任务 $ disown # 移出所有正在执行的后台任务 $ disown -r # 移出所有后台任务 $ disown -a # 不移出后台任务,但是让它们不会收到SIGHUP信号 $ disown -h # 根据jobId,移出指定的后台任务 $ disown %2 $ disown -h %2
disown缺陷:
使用disown
命令之后,还有一个问题。那就是,退出 session 以后,如果后台进程与标准I/O有交互,它还是会挂掉。
这是因为"后台任务"的标准 I/O 继承自当前 session,disown
命令并没有改变这一点。
一旦"后台任务"读写标准 I/O,就会发现它已经不存在了,所以就报错终止执行。
为了解决这个问题,需要对"后台任务"的标准 I/O 进行重定向。
$ sh watchdog.sh > stdout.txt 2> stderr.txt < /dev/null & $ disown
8、nohup 命令
还有比disown
更方便的命令,就是nohup
。
$ nohup sh watchdog.sh &
nohup
命令对sh watchdog.sh进程做了三件事。
1、阻止SIGHUP信号发到这个进程。 2、关闭标准输入。该进程不再能够接收任何输入,即使运行在前台。 3、重定向标准输出和标准错误到文件nohup.out。
也就是说,nohup
命令实际上将子进程与它所在的 session 分离了。
注意,nohup
命令不会自动把进程变为"后台任务",所以必须加上&
符号。
参考:
https://blog.csdn.net/str999_cn/article/details/78686923
https://blog.csdn.net/snow_5288/article/details/73321516
https://www.ruanyifeng.com/blog/2016/02/linux-daemon.html