6
17
2016
7

Linux 作业控制实践

本文来自依云's Blog,转载请注明。

事情的起因是这样子的。

有一个非常常用的调试工具叫 strace。输出的信息是纯文本,一大片看起来累。在 Vim 里可以给它高亮一下,就好看多了。再加上各种搜索、清理,以及非常赞的 mark.vim 插件,用起来就舒服多了!

然而我并不想每次都让 strace 写到文件里然后再拿 Vim 去读,因为还得记着清理那些文件。如果数据量不大的话,直接通过管道传给 Vim 多好。

于是有了如下 zsh 函数:

(( $+commands[strace] )) && strace () { (command strace "$@" 3>&1 1>&2 2>&3) | vim -R - }

效果是达到了,但是这样子要中断 strace 的话,得去另一个终端里去 kill。按 Ctrl-C 的话,SIGINT 也会被发给 Vim,导致 Vim 显示空白。

所以嘛,得把 Vim 放到一个单独的进程组里,这样就不会在 Ctrl-C 的时候收到 SIGINT 了。但是,Vim 还得用终端啊。

一开始,我用自制的 expect.py 模块,给 Vim 分配了一个新的终端。这样子 Ctrl-C 好用了。然后我发现 Ctrl-Z 不好用了……

Ctrl-Z 还是挺方便的功能,临时需要执行个命令,不用开新的 shell(以及 ssh),直接按一下 Ctrl-Z,完事之后再回来,多好啊!就跟 zsh 的 Alt-q 一样方便好用呢。

于是就想还是不开 pty 了。直接子进程放新组里跑。这样 Vim 在尝试向终端输出时会收到 SIGTTOU 信号,因为它不是前台进程组。找了一下,用 tcsetpgrp 就可以把指定进程组放到前台了。然后发个 SIGCONT 让可能已经停下来了的 Vim 继续。

然后,当 Vim 收到 SIGTSTP 而停止的时候,我的程序该怎么知道呢?搜了一下,原来这种情况下也会收到 SIGCHLD 的!我以前一直以为只有子进程退出才会收到 SIGCHLD 啊……然后是一个关于 SIGCHLD 的坑,之前在 pssh 里看到过的,这次没有及时想到:不给 SIGCHLD 注册信号处理器时是收不到 SIGCHLD 的!不过诡异的是,我的这个程序有时却能够收到——在我使用 strace 跟踪它的时候……

于是,当 Vim 收到 SIGTSTP 时,把我们自己设置成前台进程组,然后给自己发一个 SIGTSTP 也停下来好了。令人意外的是,后台进程在调用 tcsetpgrp 时竟然也会收到 SIGTTOU。不过没关系,忽略掉就好了。

当用户 fg 时,就再把 Vim 设置成前台进程,并给它一个 SIGCONT 让它继续就好了。

最终的成品 vimtrace 在这里我的 zsh 配置是这样子的:

if (( $+commands[vimtrace] )); then
  (( $+commands[strace] )) && alias strace='vimtrace strace'
  (( $+commands[ltrace] )) && alias ltrace='vimtrace ltrace'
else
  (( $+commands[strace] )) && strace () { (command strace "$@" 3>&1 1>&2 2>&3) | vim -R - }
  (( $+commands[ltrace] )) && ltrace () { (command ltrace "$@" 3>&1 1>&2 2>&3) | vim -R - }
fi

后记:

strace 有时候还是会改变进程的行为的。这种时候更适合用 sysdig。Arch 刚刚更新的 sysdig 版本已经修正了崩溃的问题了~不过 Vim 对 sysdig 的输出就不像 strace 那样有好看的语法着色了。

其实我当时用 systemtap 来看信号发送情况更方便一些。不过那个需要内核调试符号,几百M的东西,装起来累啊……

Category: Linux | Tags: linux 终端 Python zsh | Read Count: 11014
x u b o y i n g 说:
Jun 28, 2016 12:40:02 AM

不知道为何一定要同时执行多个程序去处理,感觉中断很混乱,容易失控

最简单的办法就是让程序去生成/删除临时文件,strace 和vim 依次执行
http://bit.ly/295kxjf

我没看出来你是不是做了一个可以程序运行过程中查看trace的功能,如果vim支持的话,用脚本启动tmux+临时文件/临时实名管道好像更容易一些,当然也可以强行自己处理pty,这样也许等于实现了tmux的功能了

//技术讨论

Avatar_small
依云 说:
Jul 01, 2016 09:52:09 AM

因为我不喜欢临时文件啊。

你这个例子是不行的,Ctrl-C 会中断你的程序。把临时文件放到当前目录也问题多多。即使把文件放到 /tmp,也尽量不要遗留临时文件,即使使用 systemd 的系统,也需要十天才能自动清除临时文件。

x u b o y i n g 说:
Jul 06, 2016 12:11:30 AM

//fact
我把程序的执行结果贴在github 的comment里了,“Ctrl-C 会中断你的程序” 你能复现这个问题么?在我的例子里我看到strace里记录到目标程序的ctrlc处理(忽略)。程序最后是ctrl+\杀掉的。也就是说我的程序透传了signal。

临时文件并不是问题。vim退出以后,脚本负责把strace log删掉。并且是简单演示,没有调用系统tmp目录函数。

//opinion
临时文件的性能管理都是os的事情。除非大量的资源占用,不用用户操心
而且在这里,你的工具的目的就是观察信号,而由于使用了工具,同时启动了vim,新引入了不确定因素。将导致潜在的维护成本增加(vim行为改变(插件),pipe行为改变导致的异常)

x u b o y i n g 说:
Jul 06, 2016 12:15:05 AM

发完评论我想到了我是用bash环境,不清楚在你shell下是否是一致的行为。如果是shell导致的,那算是一个bug,麻烦先试一下bash 作为wa,(假设各个linux都安装了bash)。

Avatar_small
依云 说:
Jul 06, 2016 08:01:46 AM

咦默认 Ctrl-C 杀不掉 Perl 程序么?我没看到你对 SIGINT 做了处理呀?

至于临时文件的管理,我们服务器是 CentOS 6 啊,并且临时文件目录不在 tmpfs 上。也就是说除非重启,否则忘记删掉的临时文件会一直留着,直到收到磁盘报警信息。strace 的结果通常会比较大的。

管道的确可能改变程序行为,虽然只是 stderr。我有空修改一下。Vim 的行为没什么改变的,至少没有对看 strace 日志有影响的改变。

x u b o y i n g 说:
Jul 07, 2016 05:53:41 PM

你的进程组操作技术值得我学习,这块我没有涉足过。
提供一个另外的思路给你

#!/bin/sh
mkfifo /tmp/PIPE_$$ || exit 1
tmux new-session -d -s foo
tmux send-keys "vim -R /tmp/PIPE_$$ ; exit" Enter
tmux split-window
tmux send-keys "strace -o /tmp/PIPE_$$ $@" Enter
tmux -2 attach-session -t foo
rm /tmp/PIPE_$$

Avatar_small
依云 说:
Jul 07, 2016 10:28:42 PM

WOW 这个办法有意思。如果需要直接输出到终端但又不想和程序本身的输出混在一起的话就更好用了。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

| Theme: Aeros 2.0 by TheBuckmaker.com