Stopped(ttyoutput)异常分析和解决
关键词: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"))
exited
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()进⾏交互?

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。