Stopped (tty output)異常分析和解決


關鍵詞:SIGTTOU、tty、nohup等。

 

設計了一個進程,這個可以通過popen()啟動其他進程。並且此進程處於后台運行。

在調用比如top的過程中出現Stopped (tty output)異常。

1. 簡單分析

從接收到的異常字符,可以判斷出應該是收到信號SIGTTOU導致了進程停止。

tty相關的導致進程停止的信號有SIGTSTP、SIGTTIN、SIGTTOU。

  init_sig (SIGHUP, "HUP", N_("Hangup"))
  init_sig (SIGINT, "INT", N_("Interrupt"))
  init_sig (SIGQUIT, "QUIT", N_("Quit"))
  init_sig (SIGILL, "ILL", N_("Illegal instruction"))
  init_sig (SIGTRAP, "TRAP", N_("Trace/breakpoint trap"))
  init_sig (SIGABRT, "ABRT", N_("Aborted"))
  init_sig (SIGFPE, "FPE", N_("Floating point exception"))
  init_sig (SIGKILL, "KILL", N_("Killed"))
  init_sig (SIGBUS, "BUS", N_("Bus error"))
  init_sig (SIGSYS, "SYS", N_("Bad system call"))
  init_sig (SIGSEGV, "SEGV", N_("Segmentation fault"))
  init_sig (SIGPIPE, "PIPE", N_("Broken pipe"))
  init_sig (SIGALRM, "ALRM", N_("Alarm clock"))
  init_sig (SIGTERM, "TERM", N_("Terminated"))
  init_sig (SIGURG, "URG", N_("Urgent I/O condition"))
  init_sig (SIGSTOP, "STOP", N_("Stopped (signal)"))
 init_sig (SIGTSTP, "TSTP", N_("Stopped"))   init_sig (SIGCONT, "CONT", N_("Continued"))
  init_sig (SIGCHLD, "CHLD", N_("Child exited"))
 init_sig (SIGTTIN, "TTIN", N_("Stopped (tty input)"))
  init_sig (SIGTTOU, "TTOU", N_("Stopped (tty output)"))
  init_sig (SIGPOLL, "POLL", N_("I/O possible"))
  init_sig (SIGXCPU, "XCPU", N_("CPU time limit exceeded"))
  init_sig (SIGXFSZ, "XFSZ", N_("File size limit exceeded"))
  init_sig (SIGVTALRM, "VTALRM", N_("Virtual timer expired"))
  init_sig (SIGPROF, "PROF", N_("Profiling timer expired"))
  init_sig (SIGUSR1, "USR1", N_("User defined signal 1"))
  init_sig (SIGUSR2, "USR2", N_("User defined signal 2"))
  init_sig (SIGWINCH, "WINCH", N_("Window changed"))

進程對不同信號的默認處理不盡相同。

Stopping

Some signals cause a process to stop: SIGSTOP (stop!), SIGTSTP (stop from tty: probably ^Z was typed), SIGTTIN (tty input asked by background process), SIGTTOU (tty output sent by background process, and this was disallowed by stty tostop).

Apart from ^Z there also is ^Y. The former stops the process when it is typed, the latter stops it when it is read.

Signals generated by typing the corresponding character on some tty are sent to all processes that are in the foreground process group of the session that has that tty as controlling tty. (Details below.)

If a process is being traced, every signal will stop it.

Continuing

SIGCONT: continue a stopped process.

Terminating

SIGKILL (die! now!), SIGTERM (please, go away), SIGHUP (modem hangup), SIGINT (^C), SIGQUIT (^\), etc. Many signals have as default action to kill the target. (Sometimes with an additional core dump, when such is allowed by rlimit.) The signals SIGCHLD and SIGWINCH are ignored by default. All except SIGKILL and SIGSTOP can be caught or ignored or blocked.

分析結論:

從日志看,應該是后台進程top嘗試向tty輸出,但是stty設置不允許,進而觸發了SIGTOU導致整個后台進程退出。

但是從后面的解決方法看,更像是top嘗試從tty讀取輸入,但是此時由於處於后台無法進程操作。

2. 解決方法

2.1 將進程輸入重定向到/dev/null

desdk_rpc_server  < /dev/null &

2.2 通過nohup重定向后台進程IN/OUT/ERR

通過nohup將進程的輸入、輸出、錯誤重定向,在出現top這種交互命令的時候popen()返回錯誤,但是desdk_rpc_server本身不退出。

nohup desdk_rpc_server &

nohup的主要作用如下:

Usage: nohup COMMAND [ARG]...
  or:  nohup OPTION
Run COMMAND, ignoring hangup signals.

      --help     display this help and exit
      --version  output version information and exit

If standard input is a terminal, redirect it from an unreadable file.
If standard output is a terminal, append output to 'nohup.out' if possible,
'$HOME/nohup.out' otherwise.
If standard error is a terminal, redirect it to standard output.
To save output to FILE, use 'nohup COMMAND > FILE'.

nohup的busybox源碼:

int nohup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int nohup_main(int argc UNUSED_PARAM, char **argv)
{
    const char *nohupout;
    char *home;

    xfunc_error_retval = 127;

    if (!argv[1]) {
        bb_show_usage();
    }

    /* If stdin is a tty, detach from it. */
    if (isatty(STDIN_FILENO)) {
        /* bb_error_msg("ignoring input"); */
        close(STDIN_FILENO);
        xopen(bb_dev_null, O_RDONLY); /* will be fd 0 (STDIN_FILENO) */
    }

    nohupout = "nohup.out";
    /* Redirect stdout to nohup.out, either in "." or in "$HOME". */
    if (isatty(STDOUT_FILENO)) {
        close(STDOUT_FILENO);
        if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) {
            home = getenv("HOME");
            if (home) {
                nohupout = concat_path_file(home, nohupout);
                xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
            } else {
                xopen(bb_dev_null, O_RDONLY); /* will be fd 1 */
            }
        }
        bb_error_msg("appending output to %s", nohupout);
    }

    /* If we have a tty on stderr, redirect to stdout. */
    if (isatty(STDERR_FILENO)) {
        /* if (stdout_wasnt_a_tty)
            bb_error_msg("redirecting stderr to stdout"); */
        dup2(STDOUT_FILENO, STDERR_FILENO);
    }

    signal(SIGHUP, SIG_IGN);

    argv++;
    BB_EXECVP_or_die(argv);
}

2.3 通過signal()忽略信號

當通過SIG_IGN來忽略此信號時,desdk_rpc_server進程會卡死。此法不可取。

signal(SIGTTIN, SIG_IGN)
signal(SIGTTOU, SIG_IGN)

2.4 進程代碼中重定向輸入

在desdk_rpc_server啟動時,將STDIN_FILENO重定向到/dev/null。

    /* If stdin is a tty, detach from it. */
  Stopp  if (isatty(STDIN_FILENO)) {
        /* bb_error_msg("ignoring input"); */
        close(STDIN_FILENO);
        open("/dev/null", O_RDONLY, 0666); /* will be fd 0 (STDIN_FILENO) */
    }

之后在desdk_rpc_server中調用top是,top自身退出並不會導致desdk_rpc_server退出。

top: failed tty get

3. 小結

雖然簡單的避過了問題,但是感覺解決的並不好。

遺留的問題包括:

1. 究竟是top中哪部分觸發了SIGTTOU信號?

2. 針對top這種命令,如何通過popen()進行交互?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM