侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Nohup源码分析

2023-12-07 星期四 / 0 评论 / 0 点赞 / 114 阅读 / 5983 字

在我们日常工作中, 总是不可避免的需要将进程放置后台运行, 于是我们就会使用& 或者nohup ... &, 我们有时会疑虑, 其实为什么多余添加一个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 terminatein 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.htypedef 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

广告 广告

评论区