Nohup源码分析

   在我们日常工作中, 总是不可避免的需要将进程放置后台运行, 于是我们就会使用& 或者nohup ... &, 我们有时会疑虑, 其实为什么多余添加一个nohup, 于是就是谷歌/百度, 然后就会得出一个答案: nohup 能够避免在终端断开时, 后台进程被杀掉. 但为什么nohup能够实现这个? 我们先来看下bash 对& 和nohup的解析吧:

1& 2If a command is terminated by the control operator &, the shell executes the command in the background 3in a subshell. The shell does not wait for the command to finish, and the return status is 0. 4Commands separated by a ; are executed sequentially; the shell waits for each command to terminate 5in turn. The return status is the exit status of the last command executed. 6 7nohup 8 Run COMMAND, ignoring hangup signals

从上面的描述可以看得很清楚, & 只是将当前执行的命令, 在subshell中执行, 父shell不再等到command结束,而且返回0, 而nohup只是说, 以忽略SIGHUP信号的形式, 运行command.

于是我们就能得出结论, 放置后台的指令, 是& 而不是 nohup, nohup只是让命令/进程 忽略SIGHUP

为什么忽略SIGHUP就能避免终端断开, 进程退出的问题呢? 原因就是, 终端断开, 就是发送的SIGHUP

既然终端断开发送的是SIGHUP, 忽略SIGHUP信号的指令是nohup, 那有没有一种感觉就是, 似乎nohup .. 不需要后面的&呢? 上面早已经提到, & 是将命令置于subshell去运行, 所以, 如果我们在终端只运行nohup, 没有&, 那么我们的终端就无法使用, 必须等到命令结束才能恢复, 例如: 

当我们将这个终端关闭, 从另外的终端是, 是可以看到这个sleep的进程的.

----------------------------------------------- 分割线 ------------------------------------------------------

上面是 我们从表面的现象去得出的结论, 接下来, 我们来看下源码是怎样实现的吧:

1nohup.c (抽取关键位置) 2 main (int argc, char **argv) 3 { 4 bool redirecting_stdout; //是否重定向输出 5 ... 6 ... 7 redirecting_stdout = isatty (STDOUT_FILENO); //判断标准输出是不是 tty 8 ... 9 10 // 如果重定向的文件描述符, 是tty 而不是文件, 则写入nohup.out 11 if (redirecting_stdout || (redirecting_stderr && stdout_is_closed)) 12 { 13 char *in_home = NULL; 14 char const *file = "nohup.out"; 15 int flags = O_CREAT | O_WRONLY | O_APPEND; 16 mode_t mode = S_IRUSR | S_IWUSR; 17 mode_t umask_value = umask (~mode); 18 out_fd = (redirecting_stdout 19 ? fd_reopen (STDOUT_FILENO, file, flags, mode) 20 : open (file, flags, mode)); 21 22 if (out_fd < 0) 23 { 24 int saved_errno = errno; 25 char const *home = getenv ("HOME"); 26 if (home) 27 { 28 in_home = file_name_concat (home, file, NULL); 29 out_fd = (redirecting_stdout 30 ? fd_reopen (STDOUT_FILENO, in_home, flags, mode) 31 : open (in_home, flags, mode)); 32 } 33 if (out_fd < 0) 34 { 35 int saved_errno2 = errno; 36 error (0, saved_errno, _("failed to open %s"), quote (file)); 37 if (in_home) 38 error (0, saved_errno2, _("failed to open %s"), 39 quote (in_home)); 40 exit (exit_internal_failure); 41 } 42 file = in_home; 43 } 44 45 umask (umask_value); 46 error (0, 0, 47 _(ignoring_input 48 ? N_("ignoring input and appending output to %s") 49 : N_("appending output to %s")), 50 quote (file)); 51 free (in_home); 52 } 53 ... 54 signal (SIGHUP, SIG_IGN); // 注册SIGHUP信号处理函数 SIG_IGN, SIG_IGN 宏定义在下一行 55 // typedef void (*__sighandler_t)(int); 类型转换宏定义 56 // #define SIG_IGN ((__force __sighandler_t)1) /* ignore signal */ 57 58 59{ 60 int exit_status; 61 int saved_errno; 62 char **cmd = argv + optind; 63 64 execvp (*cmd, cmd); //执行真正的命令 65 exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); 66 saved_errno = errno; 67 68 /* The execve failed. Output a diagnostic to stderr only if: 69 - stderr was initially redirected to a non-tty, or 70 - stderr was initially directed to a tty, and we 71 can dup2 it to point back to that same tty. 72 In other words, output the diagnostic if possible, but only if 73 it will go to the original stderr. */ 74 if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO) 75 error (0, saved_errno, _("failed to run command %s"), quote (*cmd)); 76 77 exit (exit_status); 78 } 79}

从代码中的:

signal (SIGHUP, SIG_IGN);                              

可以看到nohup主要是注册了一个SIGHUP和函数SIG_IGN,咱们来看下SIG_IGN是一个什么东西吧:

1取自: Signal-defs.h 2 3typedef void (*__sighandler_t)(int); //类型转换宏定义 4 5#define SIG_DFL ((__force __sighandler_t)0) /* default signal handling */ 6#define SIG_IGN ((__force __sighandler_t)1) /* ignore signal */ 7#define SIG_ERR ((__force __sighandler_t)-1) /* error return from signal */

其实看到这里我们已经比较清楚了, 在Signal-defs.h中定义了一个类型转换的宏定义, 将一个整型转换成__sighandler_t型,然后再一起传给singal去做处理, 至于signal怎么处理, 咱们下次再深究.

nohup原理简单分析到这为止

欢迎各位大牛指点教育, 转载请注明: https://my.oschina.net/u/2291453/blog/799561

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Linux后台运行进程 node screen

1\.后台运行的例子nohupcommand/dev/null2&1&解释:nohup:nohangup,不挂断地运行命令。只用nohup命令,关闭终端,进程还存在。若在终端中直接使用Ctrlc,则会关闭进程。command:command是用户输入的命

Linux nohup命令和后台运行符&的使用

文章目录1.nohup的使用2.后台运行符&的使用3.总结1.nohup的使用作用:当你在终端执行命令脚本,当脚本没有执行完,如果你关闭终端,那么跟随这个终端有关的进程都会退出执行,nohup命令的作用就是让使用此命令的脚本可以脱离终端继续执行,不受终端退出的影响。

Linux进程后台执行nohup(OpenTSDB后台运行方法)

1.问题描述OpenTSDB执行./tsdbtsd启动之后,占有控制台执行并且CtrlC后就退出了,关闭控制台同样会退出。2.解决方法(在/opt/module/opentsdb2.3.1/build/目录下)nohup./tsdbtsd/dev/null2&1&其中: nohup:不挂断运行 

Linux学习20

前言django在linux上运行,一般在xshell远程连接后,是通过pythonmanage.pyrunserver0.0.0.0:8000启动服务。但是这样有个弊端,窗口关闭服务就停止了。nohup可以启动的时候挂后台运行nohup后台运行cd到django的manage.py目录,启动之前先杀掉进程