Linux進程控制和管理


1.1 什么是進程?

進程是 UNIX/Linux 用來表示正在運行的程序的一種抽象概念,所有系統上面運行的的數據都會以進程的形態存在。

1.2 進程的組成部分

一個進程由一個地址空間和內核內部的一組數據公同組成,地址空間是由內核標記出來供進程使用的一組內存頁面(頁面是管理內存的單位,頁面大小通常是 1KB 或 8KB)。它包含進程正在執行的代碼、庫、進程變量、進程棧以及進程正在運行時內核所需要的各種其他信息。

內核的內部數據結構記錄了有關每個進程的各種信息,其中非常重要的一些信息有:

  • 進程的屬主;
  • 進程的信號掩碼(一個記錄,確定要封鎖哪些信號);
  • 進程已打開的文件和網絡端口的信息;
  • 進程執行的優先級;
  • 進程的當前狀態(睡眠狀態、停止狀態、可運行狀態等等);
  • 進程的地址空間映射。

1.3 子進程與父進程

每個進程都有一個唯一的 PID(Process ID),進程必須克隆自身去創建一個新進程。克隆出的進程能夠把它正在運行的那個程序替換成另一個不同的程序。

當一個進程被克隆時,原來的進程就叫做父進程 PPID(Parent Process ID),而克隆出的副本則叫做子進程。進程的 PPID 屬性就是克隆它的父進程的 PID

我們以一個實例加深對子進程與父進程的理解:在目前的 bash 環境下,再出發一次 bash ,並以 ps -l 命令觀察進程 PIDPPID 的輸出信息。

[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

進程的狀態主要有:
R(Running/Runnable):進程正在運行中或可運行(在隊列上);
S(Interruptible Sleep):可中斷睡眠狀態,進程等待某個事件而被系統掛起。當進程等待的事件發生時,它會被喚醒並進入 R 狀態;

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操作系統》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM