Nohup源码分析

Stella981
• 阅读 931

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

& 
If  a  command  is terminated by the control operator &, the shell executes the command in the background 
in a subshell.  The shell does not wait for the command to finish, and the return status is 0. 
Commands separated by a ; are executed sequentially; the shell waits for each command to terminate
in turn.  The return  status is the exit status of the last command executed.

nohup
   Run COMMAND, ignoring hangup signals

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

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

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

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

Nohup源码分析

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

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

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

nohup.c (抽取关键位置)
 main (int argc, char **argv)
 {
  bool redirecting_stdout;        //是否重定向输出
  ...
  ...
  redirecting_stdout = isatty (STDOUT_FILENO);  //判断标准输出是不是 tty
  ...

  // 如果重定向的文件描述符, 是tty 而不是文件, 则写入nohup.out
  if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))
    {
      char *in_home = NULL;
      char const *file = "nohup.out";
      int flags = O_CREAT | O_WRONLY | O_APPEND;
      mode_t mode = S_IRUSR | S_IWUSR;
      mode_t umask_value = umask (~mode);
      out_fd = (redirecting_stdout
                ? fd_reopen (STDOUT_FILENO, file, flags, mode)
                : open (file, flags, mode));

      if (out_fd < 0)
        {
          int saved_errno = errno;
          char const *home = getenv ("HOME");
          if (home)
            {
              in_home = file_name_concat (home, file, NULL);
              out_fd = (redirecting_stdout
                        ? fd_reopen (STDOUT_FILENO, in_home, flags, mode)
                        : open (in_home, flags, mode));
            }
          if (out_fd < 0)
            {
              int saved_errno2 = errno;
              error (0, saved_errno, _("failed to open %s"), quote (file));
              if (in_home)
                error (0, saved_errno2, _("failed to open %s"),
                       quote (in_home));
              exit (exit_internal_failure);
            }
          file = in_home;
        }

      umask (umask_value);
      error (0, 0,
             _(ignoring_input
               ? N_("ignoring input and appending output to %s")
               : N_("appending output to %s")),
             quote (file));
      free (in_home);
    }
    ...
    signal (SIGHUP, SIG_IGN);   // 注册SIGHUP信号处理函数 SIG_IGN, SIG_IGN 宏定义在下一行
                                // typedef void (*__sighandler_t)(int);  类型转换宏定义
                                // #define SIG_IGN    ((__force __sighandler_t)1)    /* ignore signal */


{
    int exit_status;
    int saved_errno;
    char **cmd = argv + optind;

    execvp (*cmd, cmd);  //执行真正的命令
    exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
    saved_errno = errno;

    /* The execve failed.  Output a diagnostic to stderr only if:
       - stderr was initially redirected to a non-tty, or
       - stderr was initially directed to a tty, and we
         can dup2 it to point back to that same tty.
       In other words, output the diagnostic if possible, but only if
       it will go to the original stderr.  */
    if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
      error (0, saved_errno, _("failed to run command %s"), quote (*cmd));

    exit (exit_status);
  }
}

从代码中的:

signal (SIGHUP, SIG_IGN);                              

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

取自: Signal-defs.h

typedef void (*__sighandler_t)(int);  //类型转换宏定义

#define SIG_DFL    ((__force __sighandler_t)0)    /* default signal handling */
#define SIG_IGN    ((__force __sighandler_t)1)    /* ignore signal */
#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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Linux后台运行进程 node screen
1\.后台运行的例子nohupcommand/dev/null2&1&解释:nohup:nohangup,不挂断地运行命令。只用nohup命令,关闭终端,进程还存在。若在终端中直接使用Ctrlc,则会关闭进程。command:command是用户输入的命
Stella981 Stella981
3年前
Linux nohup命令和后台运行符&的使用
文章目录1.nohup的使用2.后台运行符&的使用3.总结1.nohup的使用作用:当你在终端执行命令脚本,当脚本没有执行完,如果你关闭终端,那么跟随这个终端有关的进程都会退出执行,nohup命令的作用就是让使用此命令的脚本可以脱离终端继续执行,不受终端退出的影响。
Stella981 Stella981
3年前
Linux进程后台执行nohup(OpenTSDB后台运行方法)
1.问题描述OpenTSDB执行./tsdbtsd启动之后,占有控制台执行并且CtrlC后就退出了,关闭控制台同样会退出。2.解决方法(在/opt/module/opentsdb2.3.1/build/目录下)nohup./tsdbtsd/dev/null2&1&其中: nohup:不挂断运行 
Stella981 Stella981
3年前
Linux学习20
前言django在linux上运行,一般在xshell远程连接后,是通过pythonmanage.pyrunserver0.0.0.0:8000启动服务。但是这样有个弊端,窗口关闭服务就停止了。nohup可以启动的时候挂后台运行nohup后台运行cd到django的manage.py目录,启动之前先杀掉进程
Stella981 Stella981
3年前
Linux 进程后台运行的方法
当用户注销(logout)或者网络断开时,终端会收到HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略HUP信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。nohupnohup的用途就是让提交的命令忽略hangup信号。nohup的使用是十分方便的,只需在要处理
Stella981 Stella981
3年前
Linux下部署 jar包
1、windows和linux执行jar命令是一样的,javajarxxx.jar2、使用nohup命令将jar程序设置成后台运行,运行日志输出到nohup.out,关闭窗口无影响nohupjavajarxxx.jarnohup.out2&1&!(https://oscimg.oschina.net/oscnet/
Stella981 Stella981
3年前
Linux中标准输出和标准错误的重导向
如果一个命令需要长时间在服务器上运行,那么很多时候会用到nohup命令,这时即便远程登录ssh中断了与服务器的联系,那么在服务器上运行的命令也不会因此而被迫停止。通常情况下,nohup与&连用,&的意思是将该命令放在后台执行。如下:nohupexample.sh&将exmaple.sh通过&放在服务器后台运行,nohup确保了即便当前
九路 九路
9个月前
linux nohup命令使用教程
nohup的作用可以将程序以忽略挂起信号(SIGHUP)的方式运行。常见的用法是和&命令一同使用,将命令放置到后台运行,即使终端挂掉,进程会忽略挂起信号,继续运行。平时我们直接执行命令,只是在终端中运行,如果关闭了终端,我们的命令也就结束了。尤其是服务,需