关键词: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()进行交互?