原文:Exercise 16. Processes: working with proccesses, ps, kill
译者:飞龙
自豪地采用谷歌翻译
最简单的程序是硬盘上的文件,它包含中央处理器执行的指令。当你启动它的时候,它被复制到内存,控制权传递给它。被执行的程序称为进程。在例如 Linux 的多任务操作系统中,你可以启动程序的许多实例,因此可以从一个程序启动许多进程,所有程序将同时运行(执行)。
这是执行ls时发生的事情的概述:
你把 ls 和它的参数输入到你的终端模拟器,然后按 <ENTER>控制权现在传递给 BashBash在你的硬盘上查找 ls将自身派生到 Bash 克隆体,也就是将自己克隆到内存中的新位置成为 Bash 克隆体的父进程控制权传给了传递给 Bash 克隆体Bash 克隆体成为 Bash 的子进程保存 Bash 父进程的环境知道它是一个克隆体并且做出相应的反应使用 ls 覆盖自身控制权现在传递给 lsls为你打印一个目录列表或返回错误返回退出代码控制权现在传递给 BashBash将 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》。另外,注意到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$
解释
- 打印你拥有(启动)的进程。
- 仅打印与终端(
tty)相关的进程和你拥有(启动)的进程。 - 打印所有正在运行的进程。
- 以树形式打印所有正在运行的进程,并包含附加信息,例如可用的相关用户名和环境。请注意,此信息仅适用于你拥有(启动)的进程。为了查看所有进程的环境信息,请输入
sudo ps axue -forest。 - 开始创建一个零填充文件(填充为空字符,现在不要纠结),并通过在末尾指定
&发送到后台。 - 查询
dd的状态。 - 因为 bash 只能打印一些东西来回应你的输入,你需要按
<ENTER>(发出空指令)。 - 再次查询
dd的状态。 - 同样,你需要按
<ENTER>来查看输出。 - 向
dd发送终止信号,所以dd退出了。 - 为了看到它确实发生了,你需要再次按
<ENTER>键 。
附加题
- 阅读
man ps,man kill。 - 阅读进程的生命周期,并研究这张图片:进程的工作流。
- 打印并填写信号表。你可以使用 kernel.org 中的文档。
