练习 16:处理进程,pskill

原文:Exercise 16. Processes: working with proccesses, ps, kill

译者:飞龙

协议:CC BY-NC-SA 4.0

自豪地采用谷歌翻译

最简单的程序是硬盘上的文件,它包含中央处理器执行的指令。当你启动它的时候,它被复制到内存,控制权传递给它。被执行的程序称为进程。在例如 Linux 的多任务操作系统中,你可以启动程序的许多实例,因此可以从一个程序启动许多进程,所有程序将同时运行(执行)。

这是执行ls时发生的事情的概述:

  1. ls 和它的参数输入到你的终端模拟器,然后按 <ENTER>
  2. 控制权现在传递给 Bash
  3. Bash
  4. 在你的硬盘上查找 ls
  5. 将自身派生到 Bash 克隆体,也就是将自己克隆到内存中的新位置
  6. 成为 Bash 克隆体的父进程
  7. 控制权传给了传递给 Bash 克隆体
  8. Bash 克隆体
  9. 成为 Bash 的子进程
  10. 保存 Bash 父进程的环境
  11. 知道它是一个克隆体并且做出相应的反应
  12. 使用 ls 覆盖自身
  13. 控制权现在传递给 ls
  14. ls
  15. 为你打印一个目录列表或返回错误
  16. 返回退出代码
  17. 控制权现在传递给 Bash
  18. Bash
  19. ls 退出代码赋给 ? 变量
  20. 等待你的输入
  21. 可以再次输入内容

一些进程不像ls那样交互,只是在后台静静地工作,就像ssh一样。进程有许多可能的状态,并且有许多操作,你可以通过信号机制对它们执行。

首先让我们谈论状态。如果你键入ps ax -forest,它将打印出所有进程,你会得到这样的东西(跳过一些与硬件有关的进程):

  1. user1@vm1:/etc$ ps --forest ax
  2. PID TTY STAT TIME COMMAND
  3. 1 ? Ss 0:16 init [2]
  4. 297 ? S<s 0:00 udevd --daemon
  5. 392 ? S< 0:00 \_ udevd --daemon
  6. 399 ? S< 0:00 \_ udevd --daemon
  7. 691 ? Ss 0:00 /sbin/portmap
  8. 703 ? Ss 0:00 /sbin/rpc.statd
  9. 862 ? Sl 0:00 /usr/sbin/rsyslogd -c4
  10. 886 ? Ss 0:00 /usr/sbin/atd
  11. 971 ? Ss 0:00 /usr/sbin/acpid
  12. 978 ? Ss 0:01 /usr/sbin/cron
  13. 1177 ? Ss 0:00 /usr/sbin/sshd
  14. 6671 ? Ss 0:00 \_ sshd: user1 [priv]
  15. 6675 ? S 0:00 \_ sshd: user1@pts/0
  16. 6676 pts/0 Ss 0:00 \_ -bash
  17. 7932 pts/0 R+ 0:00 \_ ps --forest ax
  18. 1191 ? Ss 0:00 /usr/sbin/exim4 -bd -q30m
  19. 1210 tty2 Ss+ 0:00 /sbin/getty 38400 tty2
  20. 1211 tty3 Ss+ 0:00 /sbin/getty 38400 tty3
  21. 1212 tty4 Ss+ 0:00 /sbin/getty 38400 tty4
  22. 1213 tty5 Ss+ 0:00 /sbin/getty 38400 tty5
  23. 1214 tty6 Ss+ 0:00 /sbin/getty 38400 tty6
  24. 6216 tty1 Ss+ 0:00 /sbin/getty 38400 tty1

让我们浏览这个列表:

PID - 进程 ID。每个进程都有与之相关联的唯一编号,用于唯一标识它。这意味着没有两个进程可以拥有相同的 PID。
TTY - 与进程相关联的电传模拟器,允许进程与你交换信息。
STAT - 当前进程状态。这将在下面详细描述。
TIME - 这是在 CPU 上执行此进程的时间(以分钟为单位)。
COMMAND - 这是带有参数的程序名称。请注意/usr/sbin/sshdsshd: 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 killSIGNALS部分获得:

| 信号 | 编号 | 行为 | 描述 |
| 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 | 退出 | 终止进程 |

同样,不要因为不理解而害怕,因为现在你只需要了解HUPTERMKILL。甚至有一首关于KILL信号的歌曲 ,命名为《Kill dash nine》。另外,注意到abort(3)alarm(2)了么?这意味着你可以通过键入man 3 abortman 2 alarm来阅读相应的手册页。

现在,你将学习如何列出正在运行的进程并向其发送信号。

这样做

  1. 1: ps x
  2. 2: ps a
  3. 3: ps ax
  4. 4: ps axue --forest
  5. 5: dd if=/dev/zero of=~/test.img bs=1 count=$((1024*1024*1024)) &
  6. 6: kill -s USR1 $!
  7. 7: <ENTER>
  8. 8: kill -s USR1 $!
  9. 9: <ENTER>
  10. 10: kill -s TERM $!
  11. 11: <ENTER>

你会看到什么

  1. user1@vm1:/etc$ ps x
  2. PID TTY STAT TIME COMMAND
  3. 6675 ? S 0:00 sshd: user1@pts/0
  4. 6676 pts/0 Ss 0:00 -bash
  5. 8193 pts/0 R+ 0:00 ps x
  6. user1@vm1:/etc$ ps a
  7. PID TTY STAT TIME COMMAND
  8. 1210 tty2 Ss+ 0:00 /sbin/getty 38400 tty2
  9. 1211 tty3 Ss+ 0:00 /sbin/getty 38400 tty3
  10. 1212 tty4 Ss+ 0:00 /sbin/getty 38400 tty4
  11. 1213 tty5 Ss+ 0:00 /sbin/getty 38400 tty5
  12. 1214 tty6 Ss+ 0:00 /sbin/getty 38400 tty6
  13. 6216 tty1 Ss+ 0:00 /sbin/getty 38400 tty1
  14. 6676 pts/0 Ss 0:00 -bash
  15. 8194 pts/0 R+ 0:00 ps a
  16. user1@vm1:/etc$ ps ax
  17. PID TTY STAT TIME COMMAND
  18. 1 ? Ss 0:16 init [2]
  19. --- skipped --- skipped --- skipped ---
  20. 691 ? Ss 0:00 /sbin/portmap
  21. 703 ? Ss 0:00 /sbin/rpc.statd
  22. 862 ? Sl 0:00 /usr/sbin/rsyslogd -c4
  23. 886 ? Ss 0:00 /usr/sbin/atd
  24. 971 ? Ss 0:00 /usr/sbin/acpid
  25. 978 ? Ss 0:01 /usr/sbin/cron
  26. 1177 ? Ss 0:00 /usr/sbin/sshd
  27. 1191 ? Ss 0:00 /usr/sbin/exim4 -bd -q30m
  28. 1210 tty2 Ss+ 0:00 /sbin/getty 38400 tty2
  29. 1211 tty3 Ss+ 0:00 /sbin/getty 38400 tty3
  30. 1212 tty4 Ss+ 0:00 /sbin/getty 38400 tty4
  31. 1213 tty5 Ss+ 0:00 /sbin/getty 38400 tty5
  32. 1214 tty6 Ss+ 0:00 /sbin/getty 38400 tty6
  33. 6216 tty1 Ss+ 0:00 /sbin/getty 38400 tty1
  34. 6671 ? Ss 0:00 sshd: user1 [priv]
  35. 6675 ? S 0:00 sshd: user1@pts/0
  36. 6676 pts/0 Ss 0:00 -bash
  37. 8198 pts/0 R+ 0:00 ps ax
  38. user1@vm1:/etc$ ps axue --forest
  39. USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  40. --- skipped --- skipped --- skipped ---
  41. root 1 0.0 0.0 8356 820 ? Ss Jun06 0:16 init [2]
  42. root 297 0.0 0.0 16976 1000 ? S<s Jun06 0:00 udevd --daemon
  43. root 392 0.0 0.0 16872 840 ? S< Jun06 0:00 \_ udevd --daemon
  44. root 399 0.0 0.0 16872 836 ? S< Jun06 0:00 \_ udevd --daemon
  45. daemon 691 0.0 0.0 8096 532 ? Ss Jun06 0:00 /sbin/portmap
  46. statd 703 0.0 0.0 14384 868 ? Ss Jun06 0:00 /sbin/rpc.statd
  47. root 862 0.0 0.1 54296 1664 ? Sl Jun06 0:00 /usr/sbin/rsyslogd -c4
  48. daemon 886 0.0 0.0 18716 436 ? Ss Jun06 0:00 /usr/sbin/atd
  49. root 971 0.0 0.0 3920 644 ? Ss Jun06 0:00 /usr/sbin/acpid
  50. root 978 0.0 0.0 22400 880 ? Ss Jun06 0:01 /usr/sbin/cron
  51. root 1177 0.0 0.1 49176 1136 ? Ss Jun06 0:00 /usr/sbin/sshd
  52. root 6671 0.0 0.3 70496 3284 ? Ss Jun26 0:00 \_ sshd: user1 [priv]
  53. user1 6675 0.0 0.1 70496 1584 ? S Jun26 0:00 \_ sshd: user1@pts/0
  54. 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
  55. user1 8199 0.0 0.1 16312 1088 pts/0 R+ 17:07 0:00 \_ ps axue --forest TERM=screen-bce SHELL=/bin/bas
  56. 101 1191 0.0 0.1 44148 1076 ? Ss Jun06 0:00 /usr/sbin/exim4 -bd -q30m
  57. root 1210 0.0 0.0 5932 616 tty2 Ss+ Jun06 0:00 /sbin/getty 38400 tty2
  58. root 1211 0.0 0.0 5932 612 tty3 Ss+ Jun06 0:00 /sbin/getty 38400 tty3
  59. root 1212 0.0 0.0 5932 612 tty4 Ss+ Jun06 0:00 /sbin/getty 38400 tty4
  60. root 1213 0.0 0.0 5932 612 tty5 Ss+ Jun06 0:00 /sbin/getty 38400 tty5
  61. root 1214 0.0 0.0 5932 616 tty6 Ss+ Jun06 0:00 /sbin/getty 38400 tty6
  62. root 6216 0.0 0.0 5932 612 tty1 Ss+ Jun14 0:00 /sbin/getty 38400 tty1
  63. user1@vm1:/etc$ dd if=/dev/zero of=~/test.img bs=1 count=$((1024*1024*1024)) &
  64. [1] 8200
  65. user1@vm1:/etc$ kill -s USR1 $!
  66. user1@vm1:/etc$ 1455424+0 records in
  67. 1455424+0 records out
  68. 1455424 bytes (1.5 MB) copied, 1.76646 s, 824 kB/s
  69. user1@vm1:/etc$ kill -s USR1 $!
  70. user1@vm1:/etc$ 3263060+0 records in
  71. 3263060+0 records out
  72. 3263060 bytes (3.3 MB) copied, 3.94237 s, 828 kB/s
  73. user1@vm1:/etc$ kill -s TERM $!
  74. user1@vm1:/etc$
  75. [1]+ Terminated dd if=/dev/zero of=~/test.img bs=1 count=$((1024*1024*1024))
  76. user1@vm1:/etc$

解释

  1. 打印你拥有(启动)的进程。
  2. 仅打印与终端(tty)相关的进程和你拥有(启动)的进程。
  3. 打印所有正在运行的进程。
  4. 以树形式打印所有正在运行的进程,并包含附加信息,例如可用的相关用户名和环境。请注意,此信息仅适用于你拥有(启动)的进程。为了查看所有进程的环境信息,请输入sudo ps axue -forest
  5. 开始创建一个零填充文件(填充为空字符,现在不要纠结),并通过在末尾指定&发送到后台。
  6. 查询dd的状态。
  7. 因为 bash 只能打印一些东西来回应你的输入,你需要按<ENTER>(发出空指令)。
  8. 再次查询dd的状态。
  9. 同样,你需要按<ENTER>来查看输出。
  10. dd发送终止信号,所以dd退出了。
  11. 为了看到它确实发生了,你需要再次按<ENTER>键 。

附加题