参考: https://segmentfault.com/a/1190000009082089
https://segmentfault.com/a/1190000009152815
nohup是咋回事呢?nohup干了这么几件事:
将stdin重定向到/dev/null,于是程序读标准输入将会返回EOF
将stdout和stderr重定向到nohup.out或者用户通过参数指定的文件,
程序所有输出到stdout和stderr的内容将会写入该文件(有时在文件中看不到输出,有可能是程序没有调用flush)
屏蔽掉SIGHUP信号
调用exec启动指定的命令(nohup进程将会被新进程取代,但进程ID不变)
从上面nohup干的事可以看出,通过nohup启动的程序有这些特点:
nohup程序不负责将进程放到后台,这也是为什么我们经常在nohup命令后面要加上符号“&”的原因
由于stdin、stdout和stderr都被重定向了,nohup启动的程序不会读写tty
由于stdin重定向到了/dev/null,程序读stdin的时候会收到EOF返回值
nohup启动的进程本质上还是属于当前session的一个进程组,所以在当前shell里面可以通过jobs看到nohup启动的程序
当session leader退出后,该进程会收到SIGHUP信号,但由于nohup帮我们忽略了该信号,所以该进程不会退出
由于session leader已经退出,而nohup启动的进程属于该session,于是出现了一种情况,
那就是通过nohup启动的这个进程组所在的session没有leader,这是一种特殊的情况,内核会帮我们处理这种特殊情况,这里就不再深入介绍
通过nohup,我们最后达到了就算session leader(一般是shell)退出后,进程还可以照常运行的目的。
在我的 Ubuntu 中测试:
1. 在 shell1 中 ping -c 100 baidu.com (注意并没有 & 符号)
2. 在 shell2 中 ps ajx (主要想看 PGID 和 SID)
root@robert-Ubuntu:~# $$
2102: command not found
root@robert-Ubuntu:~#
root@robert-Ubuntu:~# ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 1125 1125 1125 ? -1 Ss 0 0:00 /usr/sbin/sshd -D
....
1125 2264 2264 2264 ? -1 Ss 0 0:00 sshd: root@pts/8
2264 2303 2303 2303 pts/8 2321 Ss 0 0:00 -bash
2303 2321 2321 2303 pts/8 2321 S+ 0 0:00 ping -c 100 baidu.com
2102 2329 2329 2102 pts/9 2329 R+ 0 0:00 ps ajx
4. 把 shell 1 的窗口关闭掉
5. 再次在shell2 中执行 ps ajx (主要想看 PGID 和 SID),发现 ping 进程并没有结束掉,仅仅是 PPID 变为 了1
root@robert-Ubuntu:~# ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 2321 2321 2303 ? -1 S 0 0:00 ping -c 100 baidu.com
2102 2341 2341 2102 pts/9 2341 R+ 0 0:00 ps ajx
root@robert-Ubuntu:~#
daemon
通过nohup,就可以实现让进程在后台一直执行的功能,为什么我们还要写daemon进程呢?
从上面的nohup的介绍中可以看出来,虽然进程是在后台执行,但进程跟当前session还是有着千丝万缕的关系,至少其父进程还是被session管着的,所以我们还是需要一个跟任何session都没有关系的进程来实现deamon的功能。实现deamon进程的大概步骤如下:
调用fork生成一个新进程,然后原来的进程退出,这样新进程就变成了孤儿进程,于是被init进程接收,这样新进程就和调用进程没有父子关系了。
调用setsid,创建新的session,新进程将成为新session的leader,同时该新session不和任何tty关联。
切换当前工作目录到其它地方,一般是切换到根目录,这样就取消了对原工作目录的引用,如果原工作目录是某个挂载点下面的目录,这样就不会影响该挂载点的卸载。
关闭一些从父进程继承过来而自己不需要的fd,避免不小心读写这些fd。
重定向stdin、stdout和stderr,避免读写它们出现错误。