1.1 什么是進程?
進程是 UNIX/Linux 用來表示正在運行的程序的一種抽象概念,所有系統上面運行的的數據都會以進程的形態存在。
1.2 進程的組成部分
一個進程由一個地址空間和內核內部的一組數據公同組成,地址空間是由內核標記出來供進程使用的一組內存頁面(頁面是管理內存的單位,頁面大小通常是 1KB 或 8KB)。它包含進程正在執行的代碼、庫、進程變量、進程棧以及進程正在運行時內核所需要的各種其他信息。
內核的內部數據結構記錄了有關每個進程的各種信息,其中非常重要的一些信息有:
- 進程的屬主;
- 進程的信號掩碼(一個記錄,確定要封鎖哪些信號);
- 進程已打開的文件和網絡端口的信息;
- 進程執行的優先級;
- 進程的當前狀態(睡眠狀態、停止狀態、可運行狀態等等);
- 進程的地址空間映射。
1.3 子進程與父進程
每個進程都有一個唯一的 PID(Process ID),進程必須克隆自身去創建一個新進程。克隆出的進程能夠把它正在運行的那個程序替換成另一個不同的程序。
當一個進程被克隆時,原來的進程就叫做父進程 PPID(Parent Process ID),而克隆出的副本則叫做子進程。進程的 PPID 屬性就是克隆它的父進程的 PID。
我們以一個實例加深對子進程與父進程的理解:在目前的 bash 環境下,再出發一次 bash ,並以 ps -l 命令觀察進程 PID、PPID 的輸出信息。
[root@web ~]# ps -l //第一個 bash 的 PID 是 1363 F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 1363 1322 0 80 0 - 28864 do_wai pts/1 00:00:00 bash 0 R 0 1484 1363 0 80 0 - 38314 - pts/1 00:00:00 ps [root@web ~]# bash //執行 bash 進入到子程序的環境中 [root@web ~]# ps -l //第二個 bash 的 PID 是 1487 , 它的 PPID 就是第一個 bash 的 PID 1363 F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 1363 1322 0 80 0 - 28864 do_wai pts/1 00:00:00 bash 4 S 0 1487 1363 0 80 0 - 28864 do_wai pts/1 00:00:00 bash 0 R 0 1500 1487 0 80 0 - 38314 - pts/1 00:00:00 ps
1.4 特殊進程
Linux 有三個特殊進程,idle 進程(PID=0),init 進程(PID=1),kthreadd(PID=2)。
idle 進程
idle 進程由系統自動創建的第一個進程, 運行在內核態,也是唯一一個沒有通過 fork 或者 kernel_thread 產生的進程。完成加載系統后,演變為進程調度、交換。
init 進程
Linux 的所有進程都是有 init 進程創建並運行的。首先 Linux 內核啟動,然后在用戶空間中啟動 init 進程,再啟動其他系統進程。在系統啟動完成完成后,init 將變為守護進程監視系統其他進程。
在我的 CentOS 7 系統里面,可以 ls 看到 init 進程是被軟連接到 systemd 的。
[root@web ~]# ls -al /usr/sbin/init lrwxrwxrwx 1 root root 22 Apr 26 11:07 /usr/sbin/init -> ../lib/systemd/system
系統啟動之后,init 進程會啟動很多 daemon 進程,為啟動運行提供服務,比如 httpd、ssh 等等,然后就是 agetty,讓用戶登錄,登錄后運行 shell(bash),用戶啟動的進程都是通過shell 運行的。
執行 ps -ef 命令會發現 PID 1 的進程就是我們的 init 進程 systemd ,PID 2 的進程是內核現場 kthreadd ,這兩個在內核啟動的時候都見過,其中用戶態的不帶中括號,內核態的帶中括號。
[root@web ~]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 2019 ? 00:12:54 /usr/lib/systemd/systemd --system --deserialize 24 root 2 0 0 2019 ? 00:00:01 [kthreadd] root 3 2 0 2019 ? 00:03:10 [ksoftirqd/0] root 5 2 0 2019 ? 00:00:00 [kworker/0:0H] root 7 2 0 2019 ? 00:00:00 [migration/0] ... root 23086 23085 0 10:23 pts/3 00:00:00 su - root root 23087 23086 0 10:23 pts/3 00:00:00 -bash root 24529 2 0 16:28 ? 00:00:00 [kworker/0:2] root 25448 2 0 16:33 ? 00:00:00 [kworker/0:0] root 25861 2 0 16:36 ? 00:00:00 [kworker/u2:0] root 25863 1 0 Mar20 ? 01:58:45 /usr/local/qcloud/YunJing/YDEyes/YDService root 25916 1 0 Mar20 ? 00:06:03 /usr/local/qcloud/YunJing/YDLive/YDLive root 26359 2 0 16:38 ? 00:00:00 [kworker/0:1] root 26957 2 0 16:42 ? 00:00:00 [kworker/u2:1] root 27254 23087 0 16:43 pts/3 00:00:00 ps -ef root 27732 1 0 Apr28 ? 00:00:00 ./fork.o root 29944 1 0 Apr06 ? 00:18:37 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --ex root 29949 29944 0 Apr06 ? 00:12:40 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --star root 30648 1 0 2019 ? 00:00:00 /usr/lib/systemd/systemd-udevd root 30936 1 0 2019 ? 00:01:06 /usr/sbin/crond -n chrony 32061 1 0 2019 ? 00:00:07 /usr/sbin/chronyd
sshd 的父進程是 1,pts 的父進程是 sshd,bash 的父進程是 pts,ps -ef 這個命令的父進程是 bash,這樣這個子父進程的關系就比較清晰了。
[root@web ~]# ps -ef|grep sshd root 1299 1 0 12:06 ? 00:00:04 /usr/sbin/sshd -D root 6008 1299 0 16:49 ? 00:00:00 sshd: root@pts/0 root 6415 1299 2 17:07 ? 00:00:00 sshd: root [priv] sshd 6416 6415 0 17:07 ? 00:00:00 sshd: root [net] root 6420 6012 0 17:07 pts/0 00:00:00 grep --color=auto sshd [root@web ~]# ps -ef|grep bash root 6012 6008 0 16:49 pts/0 00:00:00 -bash root 6457 6012 0 17:10 pts/0 00:00:00 grep --color=auto bash [root@web ~]#

kthreadd 進程
kthreadd 進程由 idle 通過 kernel_thread 創建,並始終運行在內核空間,負責所有內核線程的調度和管理,所有的內核線程都是直接或者間接的以 kthreadd 為父進程。
1.5 進程的優先級
Linux 是多人多任務的環境,由 top 的輸出結果我們也發現, 系統同時間有非常多的程序在運行中,叧是大部分的程序都在休眠 (sleeping) 狀態而已。如果所有的程序同時被喚醒,那 CPU 應該要先處理那個程序呢?
具有優先級的程序隊列圖:

我們知道 CPU 一秒鍾可以運作多達數 G 的微指令次數,透過核心的 CPU 排程可以讓各程序被 CPU 所切換運作, 因此每個程序在一秒鍾內或多或少都會被 CPU 執行部分的腳本。Linux 給予程序一個所謂的優先執行順序 (priority, PRI), 這個PRI 值越低代表越優先的意思。不過這個 PRI 值是由內核動態調整的,用戶無法直接調整 PRI 值的。
[root@web ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 6012 6008 0 80 0 - 28864 do_wai pts/0 00:00:00 bash 0 R 0 7028 6012 0 80 0 - 38314 - pts/0 00:00:00 ps
由於 PRI 是內核動態調整的,無法去干涉 PRI,可以通過調整 Nice 的值去調整程序的優先執行順序,就是上面的 NI 值。PRI 與 NI的相關性如下:
PRI(new)= PRI(old)+ Nice
nice 值是有正負的,PRI 越小越早被執行, 所以當nice 值為負值時,就會降低 PRI 值,即是會較優先被處理。
- nice 值范圍為 -20 ~ 19 ;
- root 可隨意調整自己或他人程序的 Nice 值;
- 一般用戶僅可條整自己程序的 Nice 值,范圍僅為 0 ~ 19 (避免一般用戶搶占系統資源);
- 一般使用者僅可將 nice 值調高,例如本來 nice 為 5 ,只能調整到大於 5;
使用 nice 或 renice 命令調整程序的 nice 值
nice:給予新執行指令新的 nice 值
以下只是一個示例,在實際環境並沒有作用,一般在系統中不重要的程序才需要調大 nice 的值,比如備份工作,由於備份比較消耗資源,調大備份指令的 nice 值,可以使系統的資源分配更為公平。
[root@web ~]# nice -n 19 vim /tmp/a.txt & [1] 7720 [root@web ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 6012 6008 0 80 0 - 28864 do_wai pts/0 00:00:00 bash 0 T 0 7720 6012 1 99 19 - 36725 do_sig pts/0 00:00:00 vim 0 R 0 7725 6012 0 80 0 - 38314 - pts/0 00:00:00 ps
renice:已運行程序的 nice 重新調整
[root@web ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 6012 6008 0 80 0 - 28864 do_wai pts/0 00:00:00 bash 0 T 0 7720 6012 0 99 19 - 36725 do_sig pts/0 00:00:00 vim 0 R 0 7911 6012 0 80 0 - 38314 - pts/0 00:00:00 ps [root@web ~]# renice 19 6012 6012 (process ID) old priority 0, new priority 19 [root@web ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 6012 6008 0 99 19 - 28864 do_wai pts/0 00:00:00 bash 0 T 0 7720 6012 0 99 19 - 36725 do_sig pts/0 00:00:00 vim 0 R 0 7921 6012 0 99 19 - 38314 - pts/0 00:00:00 ps
1.6 /proc 文件系統
Linux 的 ps、top 命令都是從 /proc 目錄讀取進程的狀態信息,但是它里面的信息卻不局限於進程的信息--內核產生的所有狀態信息和統計信息也都在 /proc 。
進程的信息都分別放到 /proc/[PID] 按 PID 命名的子目錄里面
[root@web ~]# ls /proc 1 114 119 1252 1296 139 17 21 30499 31763 32066 32174 346 375 380 538 550 609 667 9 bus crypto execdomains iomem keys kpageflags misc pagetypeinfo scsi swaps timer_list vmstat 10 115 120 1254 1299 14 18 22 30680 318 32078 32175 355 376 381 546 551 610 705 973 cgroups devices fb ioports key-users loadavg modules partitions self sys tty zoneinfo 11 116 123 126 13 140 19 29178 31314 319 321 32176 356 377 4 547 552 615 708 979 cmdline diskstats filesystems irq kmsg locks mounts pressure slabinfo sysrq-trigger uptime 112 117 124 127 1310 15 2 29184 31610 31952 32113 32177 373 378 460 548 583 618 8 acpi consoles dma fs kallsyms kpagecgroup mdstat mtrr sched_debug softirqs sysvipc version 113 118 125 128 1314 16 20 3 317 320 32116 32178 374 379 487 549 6 627 878 buddyinfo cpuinfo driver interrupts kcore kpagecount meminfo net schedstat stat thread-self vmallocinfo
/proc 目錄包含的幾個最有用的文件/目錄內容信息:
| 文件/目錄 | 內容 |
| /proc/cmdline | 加載 kernel 時下達的相關參數,查看此文件可了解系統啟動的一些信息 |
| /proc/meminfo | 內存信息 |
| /proc/cpuinfo | cpu 相關信息,包含頻率、類型、運算功能等等 |
| /proc/mounts | 系統已經掛載的數據,執行 mount 命令直接讀取此文件 |
| /proc/modules | 已經加載的模塊列表(驅動程序) |
| /proc/[pid]/attr | 此目錄中的文件提供了用於安全相關模塊的 API |
| /proc/[pid]/cwd | 鏈到進程當前目錄的符號鏈接(軟鏈接) |
| /proc/[pid]/cmdline | 進程的完整命令行 |
| /proc/[pid]/environ | 進程的環境變量 |
| /proc/[pid]/exe | 鏈到正在被執行的文件的符號鏈接 |
| /proc/[pid]/fd | 子目錄,包含鏈到每個打開文件的描述符的鏈接 |
| /proc/[pid]/maps | 內存映射信息(共享段、庫等) |
| /proc/[pid]/root | 鏈到進程的根目錄(由 chroot 設置)的符號鏈接 |
| /proc/[pid]/status | 進程大量的信息,包含進程的 name、state、ppid 等等 |
| /proc/[pid]/stat | 進程的總體狀態信息(ps使用這個) |
注:man proce 查看更多更詳細的 /proc信息
1.7 信號
進程是可以通過信號(signal)控制的,比如重啟、關閉、停止進程。
主要的信號代號名稱以及內容:
| 代號 | 名稱 | 內容 |
| 1 | SIGHUB | 啟動被終止的進序,可讓該 PID 重新讀取自己的配置文件,類似重新啟動 |
| 2 | SIGINT | 相當於用鍵盤輸入【ctrl +c】來中斷一個程序的進行 |
| 9 | SIGKILL | 強制中斷進程,如果該進程運行到一半,那么尚未完成的部分可能會有【半產品】產生, 類似 vim 會有 .filename.swp 保留下來 |
| 15 | SIGTERM | 以正常的結束進程來終止該進程,由於是正常的終止,所以后續的動作會將它完成, 如果該進程已經發生問題,無法使用正常的終止方法,使用這個也沒用 |
| 17 | SIGSTOP | 相當於用鍵盤輸入【ctrl +z】來暫停一個進程 |
注:更多詳細信息可執行 kill -l 或 man 7 signal 命令查看
kill:發送信號
顧名思義,kill 命令最常見的用法是終止一個進程,默認情況下發送 TERM 信號,語法是:
kill [signal] pid
signal 就是要發送信號的編號,沒有帶信號的編號的kill命令不保證進程會被殺死,因為 TERM 信號可能會被捕獲、封鎖或忽略。下面的命令:
kill -9 pid
將“保證”進程的消亡,因為是信號 9,即 KILL 不能被捕獲到。給“保證”加引號是因為進程的生命力有時候能夠變得相當“旺盛”,以致連 kill 9 也不能影響他們(通常是由於有些退化的 I/O 虛假鎖定,例如等等已經停止旋轉的磁盤)。
重新啟動系統通常是解決這些“不聽話”進程的唯一方法。
killall 命令可以按照進程的名字殺死所有進程,語法:
killall process_name
1.8 ps:監視進程
ps 用於報告當前系統的進程狀態,是最基本同時也是非常強大的進程查看命令,ps 通過讀取 /proc 中的虛擬文件來工作。
ps 的選項參數眾多,只需記住幾個常用的選項就可以滿足平時查看系統進程 99% 的需要,遇到特殊情況的 %1,不懂的時候使用 man ps 。
ps 常用選項:
-A:顯示所有進程,等於 -e 選項
-C:通過名字搜索進程 -f:顯示當前用戶運行進程的 UID、PID、PPID 等等 f:以 ASCII 字符顯示樹狀結構,表達進程間的相互關系,類似 -H 選項 L:列出欄位相關信息 -l 或 l:采用詳細的格式顯示當前用戶的進程信息 -o:自定義顯示進程信息,比如可以根據ps L顯示的欄目信息進行自定義輸出的進程信息:ps -eo 'tty,comm,pid,ppid,%cpu,%mem',或者根據AIX格式: ps -eo "%p %y %x %c" -p:列出指定 pid的進程信息,類似 n 選項
-t/t:指定 tty 終端機編號,並列出屬於該終端機的程序的狀況。比如 ps -t 4 或 ps -t pts/4
-u/-U:列出指定用戶的進程信息
x:顯示所有程序,不以終端機來區分。
ps 常用命令組合:
查看所有進程信息: ps -ef ps aux 查看進程樹: ps -ejH ps -axjf 查看線程有關的信息: ps -eLf ps axms
ps常用實例
ps axo pid,comm,pcpu # 查看進程的 PID、名稱以及 CPU 占用率 ps aux | sort -rnk 4 # 按內存資源的使用量對進程進行排序 ps aux | sort -nk 3 # 按 CPU 資源的使用量對進程進行排序 ps -u root # 顯示指定用戶信息 ps -e -o "%C : %p :%z : %a" | sort -k5 -nr # 查看進程並按內存使用大小排列 ps -ef # 顯示所有進程信息,連同命令行 ps -ef | grep ssh # ps 與 grep 常用組合用法,查找特定進程 ps -C nginx # 通過名字搜索進程 ps aux --sort=-pcpu,+pmem # CPU或者內存進行排序,-降序,+升序 ps -f --forest -C nginx # 用樹的風格顯示進程的層次關系 ps -o pid,ppid,uname,comm -C nginx # 顯示一個父進程的子進程 ps -e -o pid,uname=USERNAME,pcpu=CPU_USAGE,pmem,comm # 重定義欄目名字 ps -e -o pid,comm,etime # 顯示進程運行的時間 ps -aux | grep named # 查看named進程詳細信息 ps -o command -p 91730 | sed -n 2p # 通過進程id獲取服務名稱
ps 的字段說明
[root@web ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 3672 3649 0 80 0 - 28864 do_wai pts/2 00:00:00 bash 0 R 0 4596 3672 0 80 0 - 38314 - pts/2 00:00:00 ps
| key | long | description |
| F | flags | 4代表進程的權限位 root;1代表此子進程僅進行復制(fork)而沒有實際執行(exec) |
| S | Stat | 進程的狀態主要有: D(Disk Sleep):不可中斷狀態說明(Uninterruptible Sleep),一般表示進程正在跟硬件交互不允許被其他進程中斷; T(Stopped/Traced):停止狀態,背景暫停或調試狀態; Z(Zombie):僵屍狀態,進程已經被終止但卻無法被移除至內存外。 |
| UID | User ID | 用戶ID |
| PID | Process ID | 進程ID |
| C | Cpu | cpu 使用率,單位是百分比 |
| PRI | Priority | Priority值 |
| NI | Nice | nice值 |
| ADDR | 內存有關 | ADDR是kernel function,指出進程在內存的哪個部分,如果是running,一般顯示- |
| SZ | Size | 進程使用的內存 |
| WCHAN | WCHAN | 進程所在的內核函數的名稱,如果進程是多線程則顯示* |
| TTY | controlling tty (terminal) | 登錄者的終端機位置,若為遠程則使用動態端口接口(pts/n) |
| TIME | TIME | 消費CPU運作時間,這里不是系統時間,"[DD-]HH:MM:SS"格式。(別名cputime)。 |
| CMD | COMMAND | 觸發進程的指令名稱 |
| VSZ | VSIZE | 進程的虛擬內存大小,單位為KB |
| RSS | RSS | 進程占用的固定內存,單位KB |
1.9 top:動態監視進程
ps 命令只能提供系統進程的一次性快照,因此,需要使用 top 命令來獲得系統正在發生事情的“全景”。
常用 top 選項:
c/-c:顯示完整的 Command-line/Program-name -d:屏幕刷新間隔時間,默認是1秒 u/-u/U/-U:指定用戶名 -p/p:指定進程 pid -n:循環顯示的次數
top 顯示 cpu 相關參數說明:

us, user : 用戶進程占用cpu百分比 sy, system :內核進程占用cpu百分比 ni, nice :經過改變優先級的用戶進程占用cpu百分比 id, idle : 空閑cpu百分比 wa, IO-wait : 等待I/O進程的百分比 hi : time spent servicing hardware interrupts si : time spent servicing software interrupts st : time stolen from this vm by the hypervisor
常用 top 交互指令:
f/F: 從當前顯示中添加或者刪除項目。 l:切換顯示平均負載和啟動時間信息。 m/M:切換顯示內存信息/根據駐留內存大小進行排序。 t:切換顯示進程和CPU狀態信息。 c:切換顯示命令名稱和完整命令行。 P:根據CPU使用百分比大小進行排序。 q 退出
1.10 strace:追蹤信號和系統調用
有時候通過 ps、top 這樣的命令來判斷一個進程實際正在做什么相當困難,但通過 strace 命令直接觀察一個進程,進程每調用一次系統調用,以及接受到一個信息,這個命令都能顯示出來。
在 man strace 手冊里有其中大多數功能的說明,例如,-f 標准后面跟 fork 出來的進程,這個可以幫助跟蹤像 httpd 這個生出好多子進程的守護進程,-e 這個文件選項只顯示文件操作,對應找到難以定位的配置文件的位置特別方便。
把 strace 附在正在執行的進程上,監視一會該進程,再從這個進程脫離。從而獲取進程的活動信息情況。
比如下面是 strace 附在一個活動的 top 進程上獲得的信息:
[root@web ~]# strace -p 29669 strace: Process 29669 attached pselect6(1, [0], NULL, NULL, {1, 921941517}, {[WINCH], 8}) = 0 (Timeout) lseek(6, 0, SEEK_SET) = 0 read(6, "MemTotal: 1882216 kB\nMemF"..., 8191) = 1282 lseek(5, 0, SEEK_SET) = 0 read(5, "12352734.94 12094491.68\n", 8191) = 24 openat(AT_FDCWD, "/proc", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 8 getdents(8, /* 168 entries */, 32768) = 4776 stat("/proc/1", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 open("/proc/1/stat", O_RDONLY) = 9 read(9, "1 (systemd) S 0 1 1 0 -1 4202752"..., 1024) = 390 close(9) = 0 open("/proc/1/statm", O_RDONLY) = 9 read(9, "12925 919 600 355 0 2354 0\n", 1024) = 27 close(9) = 0 stat("/proc/2", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 open("/proc/2/stat", O_RDONLY) = 9 read(9, "2 (kthreadd) S 0 0 0 0 -1 213817"..., 1024) = 170 close(9) = 0 open("/proc/2/statm", O_RDONLY) = 9 read(9, "0 0 0 0 0 0 0\n", 1024) = 14
......
在本例中,top 先打開 /proc 目錄,用stat獲得其信息,然后讀取該目錄的內容,由此獲得當前正在運行的進程清單,top 接着用 stat 獲得代表 init 進程的那個目錄的信息,然后打開 /proc/1/stat 讀取 init 的狀態信息。
strace 實踐
//編寫一個簡單的c語言代碼test.c #include <stdio.h> int main() { int a; scanf("%d", &a); printf("%09d\n", a); return 0; } //編譯后得到可執行文件test,然后使用strace調用執行 [root@web demo]# gcc -o test test.c [root@web demo]# ./test // 直接執行test的結果 68 000000068 [root@web demo]# strace ./test //通過strace執行的結果 execve("./test", ["./test"], [/* 20 vars */]) = 0 brk(NULL) = 0x16c9000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e181000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=38904, ...}) = 0 mmap(NULL, 38904, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4d7e177000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0 mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4d7db93000 mprotect(0x7f4d7dd56000, 2097152, PROT_NONE) = 0 mmap(0x7f4d7df56000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f4d7df56000 mmap(0x7f4d7df5c000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4d7df5c000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e176000 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e174000 arch_prctl(ARCH_SET_FS, 0x7f4d7e174740) = 0 mprotect(0x7f4d7df56000, 16384, PROT_READ) = 0 mprotect(0x600000, 4096, PROT_READ) = 0 mprotect(0x7f4d7e182000, 4096, PROT_READ) = 0 munmap(0x7f4d7e177000, 38904) = 0 fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e180000 read(0, 68 "68\n", 1024) = 3 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e17f000 write(1, "000000068\n", 10000000068 ) = 10 exit_group(0) = ? +++ exited with 0 +++ [root@web demo]# strace -c ./test //獲取進程所有的系統調用的統計分析 ^Cstrace: Process 2009 detached % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 1 read 0.00 0.000000 0 2 open 0.00 0.000000 0 2 close 0.00 0.000000 0 3 fstat 0.00 0.000000 0 8 mmap 0.00 0.000000 0 4 mprotect 0.00 0.000000 0 1 munmap 0.00 0.000000 0 1 brk 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 arch_prctl ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 25 1 total
1.11 lsof:列出被進程打開的文件
常用選項:
-a:列出打開某文件的進程; -c<進程名>:列出指定進程所打開的文件; +D<目錄>:遞歸列出目錄下被打開的文件; -i<條件>:列出符合條件的進程。(4、6、協議、:端口、 @ip ) -p<進程號>:列出指定進程號所打開的文件; -u:列出UID號進程詳情;
lsof 實例
[root@web ~]# lsof -i // 顯示所有連接 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 2808 root 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) sshd 2816 david 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) ...... [root@web ~]# lsof -i 6 // 顯示 ipv6連接 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME master 5880 root 14u IPv6 52188842 0t0 TCP VM_0_13_centos:smtp (LISTEN) chronyd 32061 chrony 6u IPv6 3289514 0t0 UDP VM_0_13_centos:323 ....... [root@web ~]# lsof -iTCP //顯示tcp連接 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 2808 root 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) sshd 2816 david 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) taskmgr 3317 root 0u IPv4 89581455 0t0 TCP web:53644->103.117.121.93:6677 (SYN_SENT) master 5880 root 13u IPv4 52188841 0t0 TCP VM_0_13_centos:smtp (LISTEN) master 5880 root 14u IPv6 52188842 0t0 TCP VM_0_13_centos:smtp (LISTEN) sshd 8673 root 3u IPv4 89580550 0t0 TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED)...... [root@web ~]# lsof -i:80 // 顯示監聽80端口相關的進程信息 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 23008 root 8u IPv4 56706878 0t0 TCP *:http (LISTEN) nginx 23009 www 8u IPv4 56706878 0t0 TCP *:http (LISTEN) [root@web ~]# lsof -i@225.73.55.115 // 顯示指定主機連接的進程信息 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 2808 root 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) sshd 2816 david 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) sshd 8673 root 3u IPv4 89580550 0t0 TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED) sshd 8676 david 3u IPv4 89580550 0t0 TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED) nginx 23009 www 3u IPv4 89582028 0t0 TCP web:http->225.73.55.115:fjhpjp (ESTABLISHED) [root@web ~]# lsof -i -sTCP:ESTABLISHED // 顯示tcp連接的進程信息 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 2808 root 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) sshd 2816 david 3u IPv4 89562037 0t0 TCP web:22999->225.73.55.115:policyserver (ESTABLISHED) sshd 8673 root 3u IPv4 89580550 0t0 TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED) sshd 8676 david 3u IPv4 89580550 0t0 TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED) nginx 23009 www 3u IPv4 89582028 0t0 TCP web:http->225.73.55.115:fjhpjp (ESTABLISHED) YDService 25863 root 6u IPv4 64551180 0t0 TCP web:51422->169.254.0.55:lsi-bobcat (ESTABLISHED) [root@web ~]# lsof -u david //顯示指定用戶打開的文件 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 2816 david cwd DIR 253,1 4096 2 / sshd 2816 david rtd DIR 253,1 4096 2 / sshd 2816 david txt REG 253,1 852856 16203 /usr/sbin/sshd sshd 2816 david mem REG 253,1 37168 16740 /usr/lib64/libnss_sss.so.2 sshd 2816 david mem REG 253,1 15480 8530 /usr/lib64/security/pam_lastlog.so ...... [root@web ~]# kill -9 `lsof -t -u david` // 殺死指定用戶運行的進程 [root@web ~]# lsof -c nginx // 列出指定進程打開的文件 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 23008 root cwd DIR 253,1 4096 919161 /data/application/nginx-1.16.0/conf/vhosts nginx 23008 root rtd DIR 253,1 4096 2 / nginx 23008 root txt REG 253,1 7299448 919146 /data/application/nginx-1.16.0/sbin/nginx nginx 23008 root mem REG 253,1 61624 21247 /usr/lib64/libnss_files-2.17.so nginx 23008 root mem REG 253,1 155784 4428 /usr/lib64/libselinux.so.1 nginx 23008 root mem REG 253,1 105824 21252 /usr/lib64/libresolv-2.17.so ...... [root@web ~]# lsof -p 23009 //列出指定進程號打開的文件 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 23009 www cwd DIR 253,1 4096 919161 /data/application/nginx-1.16.0/conf/vhosts nginx 23009 www rtd DIR 253,1 4096 2 / nginx 23009 www txt REG 253,1 7299448 919146 /data/application/nginx-1.16.0/sbin/nginx nginx 23009 www mem REG 253,1 37168 16740 /usr/lib64/libnss_sss.so.2 nginx 23009 www mem REG 253,1 61624 21247 /usr/lib64/libnss_files-2.17.so nginx 23009 www mem REG 253,1 155784 4428 /usr/lib64/libselinux.so.1 nginx 23009 www mem REG 253,1 105824 21252 /usr/lib64/libresolv-2.17.so nginx 23009 www mem REG 253,1 15688 4556 /usr/lib64/libkeyutils.so.1.5 nginx 23009 www mem REG 253,1 67104 16148 /usr/lib64/libkrb5support.so.0.1 ...... [root@web ~]#
注:lsof 真的很強大!
1.12 線程
在 Linux 中,一些進程被細分為更小的部分,我們稱為線程(thread)。線程和進程很類似,它有一個標識符(即TID)。內核運行線程的方式和運行進程基本相同。但有一點不同,即進程之間不共享內存和 I/O 這樣的系統資源,而同一個進程中的所有線程則共享該進程占用的系統資源和一些內存。
單線程進程和多線程進程
很多進程只有一個線程,叫單線程進程。有超過一個線程的叫多線程進程。所有進程最開始都是單線程,起始線程通常稱為主線程。主線程隨后可能會啟動新的線程,這樣進程就變為多線程。這個過程和進程使用 fork() 創建新進程類似。
多線程的主要優勢在於,當進程要做的事情很多時,多個線程可以同時在多個處理器上運行,這樣可以加快進程的運行速度。雖然你也可以同時在多個處理器上運行多個進程,但線程相對進程來說啟動更快,並且線程間通過共享的進程內存來相互通信,比進程間通過網絡和管道相互通信更加便捷高效。
一些應用程序使用線程來解決在管理多個I/O資源時遇到的問題。傳統上來說,進程有時候會使用 fork() 創建新的子進程來處理新的輸入輸出流。線程提供相似的機制,但卻省去了啟動新進程的麻煩。
[root@racknerd-d3e10f ~]# ps m // 查看線程 1276 tty4 - 0:00 /sbin/mingetty /dev/tty4 - - Ss+ 0:00 - 1278 tty5 - 0:00 /sbin/mingetty /dev/tty5 - - Ss+ 0:00 - 1281 tty6 - 0:00 /sbin/mingetty /dev/tty6 - - Ss+ 0:00 - 11618 pts/0 - 0:00 -bash - - Ss 0:00 - 11646 pts/0 - 0:00 ps m - - R+ 0:00 - 12287 - /usr/bin/python /usr/bin/gm-notify - 12287 - - 12288 - - 12289 - - 12295 - [root@racknerd-d3e10f ~]# ps m -o pid,tid,comm // 自定義查看線程 1276 - mingetty - 1276 - 1278 - mingetty - 1278 - 1281 - mingetty - 1281 - 11618 - bash - 11618 - 11694 - ps - 11694 - 12287 - /usr/bin/python /usr/bin/gm-notify - 12287 - - 12288 - - 12289 - - 12295 -
注意:單線程進程中的TID和PID相同,即主線程。對於多線程進程12287,線程12287是主線程。
1.13 其它
pidof:查找正在運行進程的 pid
[root@web ~]# pidof nginx 23009 23008 [root@web ~]# pidof systemd 1 [root@web ~]# pidof sshd 11079 8676 8673 2816 2808
程序 program、進程 process、線程 thread 之間的關系和區別
簡單來說,程序是一個可執行的文件,是一組指令的集合,比如一個 windows 的谷歌瀏覽器安裝文件 chrom.exe 或 shell 腳本 auto-install.sh
進程就是一個正在運行的程序,執行者的權限屬性、程序的程序代碼所需數據等都會被加載內存中, 操作系統給予這個內存內的單元一個標識符 (PID)。
線程是一個進程執行一次 fork 的結果,線程繼承了包含它的進程的很多屬性(例如,進程的地址空間),多個線程在同一個進程內按照一種稱為多線程的機制並發執行。

線程與進程之間的區別
進程不共享其地址空間,而在同一進程下執行的線程共享地址空間。
進程是相互獨立執行的,進程之間的同步僅由內核負責,而另一方面,線程同步必須由線程在其下執行的進程負責。
與進程之間的上下文切換相比,線程之間的上下文切換速度更快。
兩個進程之間的交互只能通過標准的進程間通信來實現,而在同一進程下執行的線程可以輕松進行通信,因為它們共享大多數資源,例如內存,文本段等。
小結
一個程序至少有一個進程,一個進程至少有一個線程。
進程是程序的一部分,線程是進程的一部分。
參考文獻
《鳥哥的Linux私房菜》
《Linux系統管理技術手冊》
《精通Linux》
極客時間《趣談Linux操作系統》
