# 練習 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)。
- 笨辦法學 Linux 中文版
- 練習 0:起步
- 練習 1:文本編輯器,vim
- 練習 2:文本瀏覽器,少即是多
- 練習 3:Bash:Shell、.profile、.bashrc、.bash_history
- 練習 4:Bash:處理文件,pwd,ls,cp,mv,rm,touch
- 練習 5:Bash:環境變量,env,set,export
- 練習 6:Bash:語言設置,LANG,locale,dpkg-reconfigure locales
- 練習 7:Bash:重定向,stdin,stdout,stderr,<,>,>>,|,tee,pv
- 練習 8:更多的重定向和過濾:head,tail,awk,grep,sed
- 練習 9:Bash:任務控制,jobs,fg
- 練習 10:Bash:程序退出代碼(返回狀態)
- 練習 11:總結
- 練習 12:文檔:man,info
- 練習 13:文檔:Google
- 練習 14:包管理:Debian 包管理工具aptitude
- 練習 15:系統啟動:運行級別,/etc/init.d,rcconf,update-rc.d
- 練習 16:處理進程,ps,kill
- 練習 17:任務調度:cron,at
- 練習 18:日志:/var/log,rsyslog,logger
- 練習 19:文件系統:掛載,mount,/etc/fstab
- 練習 20:文件系統:修改和創建文件系統,tune2fs,mkfs
- 練習 21:文件系統:修改根目錄,chroot
- 練習 22:文件系統:移動數據,tar,dd
- 練習 23:文件系統:權限,chown,chmod,umask
- 練習 24:接口配置,ifconfig,netstat,iproute2,ss,route
- 練習 25:網絡:配置文件,/etc/network/interfaces
- 練習 26:網絡:封包過濾配置,iptables
- 練習 27:安全 Shell,ssh,sshd,scp
- 練習 28:性能:獲取性能情況,uptime,free,top
- 練習 29:內核:內核消息,dmesg
- 練習 30:打磨、洗練、重復:總復習
- 下一步做什么
- Debian 手動安裝