在咱們平常工做中, 老是不可避免的須要將進程放置後臺運行, 因而咱們就會使用& 或者nohup ... &, 咱們有時會疑慮, 其實爲何多餘添加一個nohup, 因而就是谷歌/百度, 而後就會得出一個答案: nohup 可以避免在終端斷開時, 後臺進程被殺掉. 但爲何nohup可以實現這個? 咱們先來看下bash 對& 和nohup的解析吧:shell
& 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.bash
因而咱們就能得出結論, 放置後臺的指令, 是& 而不是 nohup, nohup只是讓命令/進程 忽略SIGHUPapp
爲何忽略SIGHUP就能避免終端斷開, 進程退出的問題呢? 緣由就是, 終端斷開, 就是發送的SIGHUP函數
既然終端斷開發送的是SIGHUP, 忽略SIGHUP信號的指令是nohup, 那有沒有一種感受就是, 彷佛nohup .. 不須要後面的&呢? 上面早已經提到, & 是將命令置於subshell去運行, 因此, 若是咱們在終端只運行nohup, 沒有&, 那麼咱們的終端就沒法使用, 必須等到命令結束才能恢復, 例如: .net
當咱們將這個終端關閉, 從另外的終端是, 是能夠看到這個sleep的進程的.code
----------------------------------------------- 分割線 ------------------------------------------------------blog
上面是 咱們從表面的現象去得出的結論, 接下來, 咱們來看下源碼是怎樣實現的吧:進程
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是一個什麼東西吧:get
取自: 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原理簡單分析到這爲止
歡迎各位大牛指點教育, 轉載請註明: http://www.javashuo.com/article/p-prgohcbm-hp.html