Nohup源碼分析 原 薦

   在我們日常工作中, 總是不可避免的需要將進程放置後臺運行, 於是我們就會使用& 或者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, 沒有&, 那麼我們的終端就無法使用, 必須等到命令結束才能恢復, 例如: 

當我們將這個終端關閉, 從另外的終端是, 是可以看到這個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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章