练习 16:处理进程,`ps`,`kill`
最简单的程序是硬盘上的文件,它包含中央处理器执行的指令。当你启动它的时候,它被复制到内存,控制权传递给它。被执行的程序称为进程。在例如 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》。另外,注意到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 中的文档。