前文我们提到进程、会话、进程组、终端的概念,在开始本文前,需要先补充一点我们每次通过终端登录Linux后,操作系统会为该次登录分配一个会话id,同时创建bash进程作为该会话的管理者此后在bash进程的管理下,用户执行的任何命令或启动的任何程序,都会通过fork()成为bash的子进程这些由bash创建的进程以及它们后续创建的子进程,会按照一定的规则被组织到不同的进程组中,共同构成当前会话的成员而当我要结束本次登录退出时,本质上就是要退出当前会话,也就意味着会话内的所有进程都要被终止,此时会话内的所有进程都会收到信号SIGHUP,收到这个信号后的进程都会默认终止程序所以如果我写了一个程序,希望他能一直运行不受任何影响,最好的办法就是退出该会话,自立为组的条件下自己创建一个不依赖终端、没有会话管理进程的会话为了更好地理解上面这段话,我们从零开始一点一点来打造这个守护进程根据上面的描述,一个很自然的思路是,竟然你是因为SIGHUP信号造成的会话结束,进程终止,那我直接忽略SIGHUP信号不就行了?介绍一个指令nohup,用它运行的程序都会忽略SIGHUP信号执行完上面的步骤,我们要稍微等一会,不然不一定能看到成果我们会发现,相比于最初,多了两个文件,一个是nohup.out,一个是a.out事实上,a.out文件是gcc生成的可执行程序,这个挺正常的回到最初我们编写的代码,printf函数可以一直在打印的呀!但我们并没有在终端上看到任何打印的痕迹而运行代码的时候,整个终端一直处于阻塞阶段,同时也会看到一段英文nohup: ignoring input and appending output to ‘nohup.out’
根据以上的现象,仔细分析,我们可以得出如下结论:
1、运行nohup指令时,默认程序的标准输入被忽略了(或者也可以认为是被重定向到了/dev/null)
2、运行nohup指令时,默认程序的标准输出被重定向到了./nohup.out
同时前面所说要等一下才能看得出结果,其核心原因在于输出到文件是全缓冲而非行缓存,只有缓冲区满了才会刷新(语言层面的缓冲区)
现在可以试着在退出运行程序的Linux终端前后,在另一个终端输入指令
ps -axj | grep a.out
此时你会发现虽然会忽略了SIGHUP信号,但是并不能保证退出当前会话,程序仍能运行
这是怎么回事呢?
这就回到前文说的事情上了,a.out说白了就是一个前台进程!
事实上,前台进程的输入缓冲区和输出缓冲区默认都是终端的输入缓冲区和输出缓冲区,但是这里由于nohup指令导致输出缓冲区被重定向为了文件nohup.out,而输入缓冲区仍是终端的输入缓冲区
只有一个前台进程有读终端的输入缓冲区的能力!现在虽然不会因为收到SIGHUP信号而退出了,但它仍在运行,就仍会保持前台进程的特性
而当退出会话时,往往会伴随着终端退出,即使当前进程没有与终端打交道,bash进程也有可能会出于安全考虑以其他信号把你杀掉
所以,我们要使得当前程序在后台运行而非前台
可以使用&指令将进程提到后台运行
这个时候运行nohup ./a.out &即可完成退出会话,进程仍能运行
虽然上述方法可以,但是从长期运行的角度上来讲,还是不够,具体为什么,我们放到下篇文章予以阐述大家如果有什么感兴趣的内容,欢迎私信或者评论区留言