首页 > 代码库 > Linux终端关闭后台进程也结束原因分析和nohup的使用
Linux终端关闭后台进程也结束原因分析和nohup的使用
Windows和Linux的远程连接(都叫做远程连接吧)不同:
1)Windows远程连接后,假设在远程机器上执行某个程序,全然能够退出连接后过一段时间再连接到那台机器上看有没有结束。即Windows的远程连接在不经过不论什么处理的情况下。在远程机器上执行的程序不会随着中止连接而结束。
2)Ubuntu在终端登陆远程主机并执行程序后。假设此时直接中止连接退出,那么这个连接所开的会话(session)下执行的全部进程都会被杀死,即在远程执行的程序会随着中止连接而结束。
Windows就此功能来说,方便使用,Ubuntu假设在使用上可以达到Windows的响应功能特性将会大慷慨便工作。
Ubuntu中之所以会这样,主要由于:
1)进程的相关概念:
在Linux中。每一个进程都属于一个进程组(group),进程组有一个组长。多个进程组构成一个会话,会话是由当中的进程建立的,该进程叫做会话的领导进程(session leader)。
会话领导进程的PID成为识别会话的SID(session ID)。会话中的每一个进程组称为一个工作(job)。会话能够有一个进程组成为会话的前台工作(foreground job),而其它的进程组是后台工作(background job)。并非进程组中的每一个进程都是job中的内容。job是由session进程直接的“儿子”组成的。可是当job中的进程又产生子进程的时候,子进程便不是job中的内容。
每一个会话能够连接一个控制终端(control terminal)。当控制终端有输入输出时,都传递给该会话的前台进程组。当前台进程组或者说job中的最后一个进程结束后,后台的session控制进程自己主动切换至前端,由终端产生的信号。比方CTRL+Z, CTRL+\,会传递到前台进程组。
会话主要是针对一个终端建立,当我们打开多个终端窗体时。实际上就创建了多个终端会话。每一个会话都会有自己的前台工作和后台工作。这样。我们就为进程添加了管理和执行的层次。
2)终端的相关概念:
在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal)。控制终端是保存在PCB中的信息,而我们知道fork会复制PCB中的信息。因此由Shell进程启动的其他进程的控制终端也是这个终端。默认情况下(没有重定向),每一个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。在控制终端输入一些特殊的控制键能够给前台进程发信号,比如Ctrl-C表示SIGINT,Ctrl-表示SIGQUIT。
每一个进程都能够通过一个特殊的设备文件/dev/tty訪问它的控制终端。
其实每一个终端设备都相应一个不同的设备文件,/dev/tty提供了一个通用的接口。一个进程要訪问它的控制终端既能够通过/dev/tty也能够通过该终端设备所相应的设备文件来訪问。ttyname函数能够由文件描写叙述符查出相应的文件名称,该文件描写叙述符必须指向一个终端设备而不能是随意文件。在linux上的命令tty 也能够查看到当前的终端。
比方我们在图形界面下打开一个终端可能是/dev/pts/0, 第二个可能是/dev/pts/1 .. (网络终端)。而切换到字符界面下可能是/dev/tty1 ...(虚拟终端)
3)进程和终端:
用户通过终端登录系统后得到一个Shell进程,Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group)。一个前台作业能够由多个进程组成,一个后台作业也能够由多个进程组成,Shell能够同一时候执行一个前台作业和随意多个后台作业,这称为作业控制(JobControl)。
比如用下面命令启动5个进程(这个样例出自APUE):
$ proc1 | proc2 &
$ proc3 | proc4 | proc5
当中proc1和proc2属于同一个后台进程组,proc3、proc4、proc5属于同一个前台进程组,Shell进程本身属于一个单独的进程组。这些进程组的控制终端同样,它们属于同一个Session。一个Session与一个控制终端相关。当用户在控制终端输入特殊的控制键(比如Ctrl-C)时,内核会发送对应的信号(比如SIGINT)给前台进程组的全部进程。各进程、进程组、Session的关系例如以下图所看到的。
在上面的样例中,proc3、proc4、proc5被Shell放到同一个前台进程组,当中有一个进程是该进程组的Leader,Shell调用wait等待它们执行结束。一旦它们所有执行结束,Shell就调用tcsetpgrp函数将自己提到前台继续接受命令。可是注意。假设proc3、proc4、proc5中的某个进程又fork出子进程,子进程也属于同一进程组,可是Shell并不知道子进程的存在,也不会调用wait等待它结束。
换句话说,proc3 | proc4 | proc5是Shell的作业,而这个子进程不是,这是作业和进程组在概念上的差别。
一旦作业执行结束,Shell就把自己提到前台。假设原来的前台进程组还存在(假设这个子进程还没终止),则它自己主动变成后台进程,被init进程接管。
Linux的普通进程(守护进程除外)是终端的子进程,进程的存在要依赖终端为其提供空间包含标准输入、标准输出、标准出错。
当打开终端的最初的进程(也就是SID进程)退出后,其子进程也会结束。
4)终端登录和运行命令的过程:
如今我们从Session和进程组的角度又一次来看登录和运行命令的过程。
getty或telnetd进程在打开终端设备之前调用setsid函 数创建一个新的Session,该进程称为SessionLeader,该进程的id也能够看作Session的id。然后该进程打开终端设备作为这个Session中全部进程的控制终端。
在创建新Session的同一时候也创建了一个新的进程组,该进程是这个进程组的Process Group Leader,该进程的id也是进程组的id。
在登录过程中,getty或telnetd进程变成login,然后变成Shell。但仍然是同一个进程,仍然是Session Leader。
由 Shell进程fork出的子进程(比方说上例中的p3,p4。p5)本来具有和Shell同样的Session、进程组和控制 终端。可是Shell调用setpgid函数将作业中的某个子进程指定为一个新进程组的 Leader(比方说p3)。然后调用setpgid将该作业中的其他子进程也转移到这个进程组中。假设这个进程组须要在前台执行,就调用tcsetpgrp函数将它设置为前台进程组。因为一个 Session仅仅能有一个前台进程组,所以Shell所在的进程组就自己主动变成后台进程组。
5)终端退出进程继续运行的方法
Unix/Linux下一般比方想让某个程序在后台执行,非常多都是使用 & 在程序结尾来让程序自己主动执行。可能我们的程序仅仅是普通程序而已(并非守护进程),一般这样的程序使用 & 结尾。可是假设终端关闭。那么程序也会被关闭。
使用nohup命令能够在后台执行程序而且终端退出仍然执行:
nohup make &
此时屏幕提示:
[~]$ appending output to nohup.out
证明执行成功,同一时候把程序执行的输出信息放到当前文件夹的 nohup.out 文件里去。
记得在退出终端的时候应该採用正常的退出方法,而不是直接关掉putty或者其它终端登陆程序,应该在会话中exit。此点待考。
附:nohup命令參考
nohup 命令
用途:不挂断地执行命令。
语法:nohup Command [ Arg ... ] [ & ]
描写叙述:nohup 命令执行由 Command 參数和不论什么相关的 Arg 參数指定的命令。忽略全部挂断(SIGHUP)信号。
在注销后使用 nohup 命令执行后台中的程序。要执行后台中的 nohup 命令。加入 & (表示“and”的符号)到命令的尾部。
不管是否将 nohup 命令的输出重定向到终端,输出都将附加到当前文件夹的 nohup.out 文件里。假设当前文件夹的 nohup.out 文件不可写。输出重定向到 $HOME/nohup.out 文件里。假设没有文件能创建或打开以用于追加,那么 Command 參数指定的命令不可调用。假设标准错误是一个终端。那么把指定的命令写给标准错误的全部输出作为标准输出重定向到同样的文件描写叙述符。
其它方法參照让Linux的进程不受终端影响
參考:
http://www.educity.cn/linux/1241877.html
http://www.linuxidc.com/Linux/2010-09/28366.htm
http://blog.sina.com.cn/s/blog_70e94af80100lmep.html
Linux终端关闭后台进程也结束原因分析和nohup的使用