🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 练习 16:处理进程,`ps`,`kill` > 原文:[Exercise 16. Processes: working with proccesses, ps, kill](https://archive.fo/CSqm9) > 译者:[飞龙](https://github.com/wizardforcel) > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) > 自豪地采用[谷歌翻译](https://translate.google.cn/) 最简单的程序是硬盘上的文件,它包含中央处理器执行的指令。当你启动它的时候,它被复制到内存,控制权传递给它。被执行的程序称为进程。在例如 Linux 的多任务操作系统中,你可以启动程序的许多实例,因此可以从一个程序启动许多进程,所有程序将同时运行(执行)。 这是执行`ls`时发生的事情的概述: ``` 你 把 ls 和它的参数输入到你的终端模拟器,然后按 <ENTER> 控制权现在传递给 Bash Bash 在你的硬盘上查找 ls 将自身派生到 Bash 克隆体,也就是将自己克隆到内存中的新位置 成为 Bash 克隆体的父进程 控制权传给了传递给 Bash 克隆体 Bash 克隆体 成为 Bash 的子进程 保存 Bash 父进程的环境 知道它是一个克隆体并且做出相应的反应 使用 ls 覆盖自身 控制权现在传递给 ls ls 为你打印一个目录列表或返回错误 返回退出代码 控制权现在传递给 Bash Bash 将 ls 退出代码赋给 ? 变量 等待你的输入 你 可以再次输入内容 ``` 一些进程不像`ls`那样交互,只是在后台静静地工作,就像`ssh`一样。进程有许多可能的状态,并且有许多操作,你可以通过信号机制对它们执行。 首先让我们谈论状态。如果你键入`ps ax -forest`,它将打印出所有进程,你会得到这样的东西(跳过一些与硬件有关的进程): ``` user1@vm1:/etc$ ps --forest ax PID TTY STAT TIME COMMAND 1 ? Ss 0:16 init [2] 297 ? S<s 0:00 udevd --daemon 392 ? S< 0:00 \_ udevd --daemon 399 ? S< 0:00 \_ udevd --daemon 691 ? Ss 0:00 /sbin/portmap 703 ? Ss 0:00 /sbin/rpc.statd 862 ? Sl 0:00 /usr/sbin/rsyslogd -c4 886 ? Ss 0:00 /usr/sbin/atd 971 ? Ss 0:00 /usr/sbin/acpid 978 ? Ss 0:01 /usr/sbin/cron 1177 ? Ss 0:00 /usr/sbin/sshd 6671 ? Ss 0:00 \_ sshd: user1 [priv] 6675 ? S 0:00 \_ sshd: user1@pts/0 6676 pts/0 Ss 0:00 \_ -bash 7932 pts/0 R+ 0:00 \_ ps --forest ax 1191 ? Ss 0:00 /usr/sbin/exim4 -bd -q30m 1210 tty2 Ss+ 0:00 /sbin/getty 38400 tty2 1211 tty3 Ss+ 0:00 /sbin/getty 38400 tty3 1212 tty4 Ss+ 0:00 /sbin/getty 38400 tty4 1213 tty5 Ss+ 0:00 /sbin/getty 38400 tty5 1214 tty6 Ss+ 0:00 /sbin/getty 38400 tty6 6216 tty1 Ss+ 0:00 /sbin/getty 38400 tty1 ``` 让我们浏览这个列表: `PID` - 进程 ID。每个进程都有与之相关联的唯一编号,用于唯一标识它。这意味着没有两个进程可以拥有相同的 PID。 `TTY` - 与进程相关联的电传模拟器,允许进程与你交换信息。 `STAT` - 当前进程状态。这将在下面详细描述。 `TIME` - 这是在 CPU 上执行此进程的时间(以分钟为单位)。 `COMMAND` - 这是带有参数的程序名称。请注意`/usr/sbin/sshd`是`sshd: user1`的父进程,而`sshd: user1`又是`sshd: user1@pts/0`的父进程,而`sshd: user1@pts/0`又是`bash`的父进程,而`bash`又是`ps –forest ax`的父进程。你需要更深入一些! 现在让我们讨论可能的进程状态。你可以参考`man ps`的 PROCESS STATE CODES。 | 状态 | 描述 | | --- | --- | | D | 不中断睡眠(通常为 IO)。进程繁忙或挂起,不响应信号,例如硬盘已经崩溃,读操作无法完成。 | | R | 运行或可运行(在运行队列中)。进程正在执行中。 | | S | 中断睡眠(等待事件完成)。例如,终端进程和 Bash 通常处于此状态,等待你键入某些内容。 | | T | 停止,由任务控制信号或由于被追踪。 | | W | 分页(从 2.6.xx 内核起无效,所以不用担心)。 | | X | 死亡(不应该看到)。 | | Z | 已停止(“僵尸”)进程,已终止,但未被父项收回。这种情况发生在错误终止的进程上。 | | < | 高优先级(对其他用户不好) | | N | 低优先级(对其他用户很好) | | L | 将页面锁定到内存中(用于实时和自定义 IO) | | s | 是会话领导。Linux 中的相关进程被视为一个单元,并具有共享会话 ID(SID)。如果进程 ID(PID)= 会话 ID(SID),则此进程将是会话领导。 | | L | 是多线程的(使用 CLONE_THREAD,例如 NPTL pthreads) | | + | 位于前台进程组。这样的处理器允许输入和输出到电传模拟器,tty。 | 现在让我们讨论,与所有这些进程沟通的方式。你可以通过发送信号来实现它。信号可能是一种鞭策进程方式,所以进程会听你的话,并做出你想要的行为。这是可能的进程的缩略列表,我从`man kill`的`SIGNALS`部分获得: | 信号 | 编号 | 行为 | 描述 | | 0 | 0 | N/A | 退出代码表示是否可以发送信号 | | HUP | 1 | 退出 | 控制终端挂起或父进程死亡 | | INT | 2 | 退出 | 来自键盘的中断 | | QUIT | 3 | 内核 | 来自键盘的退出 | | ILL | 4 | 内核 | 非法指令 | | TRAP | 5 | 内核 | 跟踪/断点捕获 | | ABRT | 6 | 内核 | 来自`abort(3)`的中止信号 | | FPE | 8 | 内核 | 浮点异常 | | KILL | 9 | 退出 | 不可捕获,不可忽视的杀死 | | SEGV | 11 | 内核 | 内存引用无效 | | PIPE | 13 | 退出 | 损坏的管道:写入没有读者的管道 | | ALRM | 14 | 退出 | 来自`alarm(2)`的定时器信号 | | TERM | 15 | 退出 | 终止进程 | 同样,不要因为不理解而害怕,因为现在你只需要了解`HUP`,`TERM`和`KILL`。甚至有一首关于`KILL`信号的歌曲 ,命名为[《Kill dash nine》](http://www.monzy.com/intro/killdashnine_lyrics.html)。另外,注意到`abort(3)`和`alarm(2)`了么?这意味着你可以通过键入`man 3 abort`和`man 2 alarm`来阅读相应的手册页。 现在,你将学习如何列出正在运行的进程并向其发送信号。 ## 这样做 ``` 1: ps x 2: ps a 3: ps ax 4: ps axue --forest 5: dd if=/dev/zero of=~/test.img bs=1 count=$((1024*1024*1024)) & 6: kill -s USR1 $! 7: <ENTER> 8: kill -s USR1 $! 9: <ENTER> 10: kill -s TERM $! 11: <ENTER> ``` ## 你会看到什么 ``` user1@vm1:/etc$ ps x PID TTY STAT TIME COMMAND 6675 ? S 0:00 sshd: user1@pts/0 6676 pts/0 Ss 0:00 -bash 8193 pts/0 R+ 0:00 ps x user1@vm1:/etc$ ps a PID TTY STAT TIME COMMAND 1210 tty2 Ss+ 0:00 /sbin/getty 38400 tty2 1211 tty3 Ss+ 0:00 /sbin/getty 38400 tty3 1212 tty4 Ss+ 0:00 /sbin/getty 38400 tty4 1213 tty5 Ss+ 0:00 /sbin/getty 38400 tty5 1214 tty6 Ss+ 0:00 /sbin/getty 38400 tty6 6216 tty1 Ss+ 0:00 /sbin/getty 38400 tty1 6676 pts/0 Ss 0:00 -bash 8194 pts/0 R+ 0:00 ps a user1@vm1:/etc$ ps ax PID TTY STAT TIME COMMAND 1 ? Ss 0:16 init [2] --- skipped --- skipped --- skipped --- 691 ? Ss 0:00 /sbin/portmap 703 ? Ss 0:00 /sbin/rpc.statd 862 ? Sl 0:00 /usr/sbin/rsyslogd -c4 886 ? Ss 0:00 /usr/sbin/atd 971 ? Ss 0:00 /usr/sbin/acpid 978 ? Ss 0:01 /usr/sbin/cron 1177 ? Ss 0:00 /usr/sbin/sshd 1191 ? Ss 0:00 /usr/sbin/exim4 -bd -q30m 1210 tty2 Ss+ 0:00 /sbin/getty 38400 tty2 1211 tty3 Ss+ 0:00 /sbin/getty 38400 tty3 1212 tty4 Ss+ 0:00 /sbin/getty 38400 tty4 1213 tty5 Ss+ 0:00 /sbin/getty 38400 tty5 1214 tty6 Ss+ 0:00 /sbin/getty 38400 tty6 6216 tty1 Ss+ 0:00 /sbin/getty 38400 tty1 6671 ? Ss 0:00 sshd: user1 [priv] 6675 ? S 0:00 sshd: user1@pts/0 6676 pts/0 Ss 0:00 -bash 8198 pts/0 R+ 0:00 ps ax user1@vm1:/etc$ ps axue --forest USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND --- skipped --- skipped --- skipped --- root 1 0.0 0.0 8356 820 ? Ss Jun06 0:16 init [2] root 297 0.0 0.0 16976 1000 ? S<s Jun06 0:00 udevd --daemon root 392 0.0 0.0 16872 840 ? S< Jun06 0:00 \_ udevd --daemon root 399 0.0 0.0 16872 836 ? S< Jun06 0:00 \_ udevd --daemon daemon 691 0.0 0.0 8096 532 ? Ss Jun06 0:00 /sbin/portmap statd 703 0.0 0.0 14384 868 ? Ss Jun06 0:00 /sbin/rpc.statd root 862 0.0 0.1 54296 1664 ? Sl Jun06 0:00 /usr/sbin/rsyslogd -c4 daemon 886 0.0 0.0 18716 436 ? Ss Jun06 0:00 /usr/sbin/atd root 971 0.0 0.0 3920 644 ? Ss Jun06 0:00 /usr/sbin/acpid root 978 0.0 0.0 22400 880 ? Ss Jun06 0:01 /usr/sbin/cron root 1177 0.0 0.1 49176 1136 ? Ss Jun06 0:00 /usr/sbin/sshd root 6671 0.0 0.3 70496 3284 ? Ss Jun26 0:00 \_ sshd: user1 [priv] user1 6675 0.0 0.1 70496 1584 ? S Jun26 0:00 \_ sshd: user1@pts/0 user1 6676 0.0 0.6 23644 6536 pts/0 Ss Jun26 0:00 \_ -bash LANG=en_US.UTF-8 USER=user1 LOGNAME=user1 HOM user1 8199 0.0 0.1 16312 1088 pts/0 R+ 17:07 0:00 \_ ps axue --forest TERM=screen-bce SHELL=/bin/bas 101 1191 0.0 0.1 44148 1076 ? Ss Jun06 0:00 /usr/sbin/exim4 -bd -q30m root 1210 0.0 0.0 5932 616 tty2 Ss+ Jun06 0:00 /sbin/getty 38400 tty2 root 1211 0.0 0.0 5932 612 tty3 Ss+ Jun06 0:00 /sbin/getty 38400 tty3 root 1212 0.0 0.0 5932 612 tty4 Ss+ Jun06 0:00 /sbin/getty 38400 tty4 root 1213 0.0 0.0 5932 612 tty5 Ss+ Jun06 0:00 /sbin/getty 38400 tty5 root 1214 0.0 0.0 5932 616 tty6 Ss+ Jun06 0:00 /sbin/getty 38400 tty6 root 6216 0.0 0.0 5932 612 tty1 Ss+ Jun14 0:00 /sbin/getty 38400 tty1 user1@vm1:/etc$ dd if=/dev/zero of=~/test.img bs=1 count=$((1024*1024*1024)) & [1] 8200 user1@vm1:/etc$ kill -s USR1 $! user1@vm1:/etc$ 1455424+0 records in 1455424+0 records out 1455424 bytes (1.5 MB) copied, 1.76646 s, 824 kB/s user1@vm1:/etc$ kill -s USR1 $! user1@vm1:/etc$ 3263060+0 records in 3263060+0 records out 3263060 bytes (3.3 MB) copied, 3.94237 s, 828 kB/s user1@vm1:/etc$ kill -s TERM $! user1@vm1:/etc$ [1]+ Terminated dd if=/dev/zero of=~/test.img bs=1 count=$((1024*1024*1024)) user1@vm1:/etc$ ``` ## 解释 1. 打印你拥有(启动)的进程。 1. 仅打印与终端(`tty`)相关的进程和你拥有(启动)的进程。 1. 打印所有正在运行的进程。 1. 以树形式打印所有正在运行的进程,并包含附加信息,例如可用的相关用户名和环境。请注意,此信息仅适用于你拥有(启动)的进程。为了查看所有进程的环境信息,请输入`sudo ps axue -forest`。 1. 开始创建一个零填充文件(填充为空字符,现在不要纠结),并通过在末尾指定`&`发送到后台。 1. 查询`dd`的状态。 1. 因为 bash 只能打印一些东西来回应你的输入,你需要按`<ENTER>`(发出空指令)。 1. 再次查询`dd`的状态。 1. 同样,你需要按`<ENTER>`来查看输出。 1. 向`dd`发送终止信号,所以`dd`退出了。 1. 为了看到它确实发生了,你需要再次按`<ENTER>`键 。 ## 附加题 + 阅读`man ps`,`man kill`。 + 阅读[进程的生命周期](http://www.linux-tutorial.info/modules.php?name=MContent&pageid=84),并研究这张图片:[进程的工作流](http://www.linux-tutorial.info/Linux_Tutorial/The_Operating_System/The_Kernel/Processes/procflowa.gif)。 + 打印并填写[信号表](https://nixsrv.com/llthw/ex16/signals)。你可以使用 [kernel.org 中的文档](http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html%23DESCRIPTION)。