天天學五分鐘 Liunx 0011 | 服務篇:進程


1. 進程

程序放在硬盤中,在運行它的時候加載到內存,在內存里程序以進程的方式運行,進程有惟一的 ID ,叫 PID。
 
寫個簡單的 Hellow world 程序,讓它產生 PID:
[root@test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
 
int main(void)
{
    int i = 0;
    for(i==0;i<100;i++)
    {
        sleep(2);
        printf("Hello World\n");
    }
}
 
[root@test]$ gcc test.cpp -o test.out
[root@test]$ ./test.out &
[1] 35166
[root@test]$ Hello World
Hello World
^C
[root@test]$ Hello World
ps Hello World
-lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
0 S root     35166 34824  0  80   0 -  1056 hrtime 22:51 pts/15   00:00:00 ./test.out
0 R root     35174 34824  0  80   0 - 38356 -      22:51 pts/15   00:00:00 ps -lf
[root@test]$ Hello World
執行命令 ./test.out ,Hello World 程序被執行,產生了一個 PID 爲 35166 的進程。
 
上面示例中用到了 ps -lf 命令,該命令會查看當前終端的進程信息。各參數的意思是:
F:進程標誌(Process Flags),說明進程權限。常見號碼有: 4 表示進程權限爲 root,1 表示該進程只能夠複製(fork),不能被執行。
S:進程狀態。S 表示 sleep,該進程正在睡眠狀態 idle,能夠被喚醒(signal)。R 表示 running,即該進程正在運行。T 表示 stop,該進程出於中止狀態,多是後臺暫停或者 traced 狀態。Z 表示 zombie 殭屍進程,該進程已經終止可是還常駐在內存中。D 表示該進程處於不可被喚醒的睡眠狀態,如等待 I/O 的狀況。
UID: 執行該進程的用戶身份。
PID: 進程 ID。
PPID: 父進程的 ID。
C:表示 CPU 的使用率,單位爲百分比。 
PRI:Priority,表示該進程執行的優先級,系統動態調整,不可設置。
NI: Nice,加權的優先級係數,可設置;PRI/NI 數值越小表示該進程越快被 CPU 執行。
ADDR:內存相關。表示進程在內存的哪一個部分,running 的進程通常顯示 「-」。
SZ:內存相關。表示進程用掉多少內存。
WCHAN:表示目前進程是否正在運行,「-」表示正在運行。
STIME: 進程的啓動時間。
TTY: 登錄用戶的終端機位置,遠程登錄則顯示 pts/n 。
TIME: 進程的運行時間。
CMD: 什麼命令觸發了該進程。
 
 
能夠看出,進程 35166 的父進程是 34824 即 bash 的進程。
在遠程客戶端登錄主機服務端時,主機會提供一個 bash 進程給遠端,這樣客戶端就能夠以 tty 終端來訪問服務端主機了。在 bash 進程上執行命令時,bash 進程就成了執行命令產生進程的父進程。好比:
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
0 R root     38815 34824  0  80   0 - 38356 -      23:30 pts/15   00:00:00 ps -lf
[root@test]$ bash
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
4 S root     38838 34824  0  80   0 - 28715 do_wai 23:30 pts/15   00:00:00 bash
0 R root     38882 38838  0  80   0 - 38356 -      23:30 pts/15   00:00:00 ps -lf
[root@test]$ exit
exit
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
0 R root     38900 34824  0  80   0 - 38356 -      23:30 pts/15   00:00:00 ps -lf
在 bash 進程 34824 上執行 bash 命令產生一個新的 bash 進程 38838,它的父進程正是 34824,而且父進程是處於 sleep 狀態的。
 
 
執行 Hello World 程序使用的是 ./test.out & 命令,在命令後加 & 號會將當前工做轉到後臺執行,同時終端界面會打印 [1] 35166,[1] 表示該工做的 ID,35166 表示產生的進程 PID。
不過,即便工做轉到後臺了,程序執行的輸出仍是會打印到終端上。能夠加個輸入流,將執行的正確信息流到 std.out 中,錯誤信息流到 std.err 中:
[root@test]$ ./test.out 1> std.out 2> std.err &
[1] 41371
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai Apr21 pts/15   00:00:00 -bash
0 S root     41371 34824  0  80   0 -  1056 hrtime 00:00 pts/15   00:00:00 ./test.out
0 R root     41379 34824  0  80   0 - 38356 -      00:00 pts/15   00:00:00 ps -lf
[root@test]$ cat std.out
[root@test]$ 
 
數據流到 std.out 中,可是 cat std.out 卻沒有記錄。將 Hello World 更改下從新執行:
[root@test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
 
int main(void)
{
    int i = 0;
    for(i==0;i<10;i++)
    {
        sleep(2);
        printf("Hello World\n");
    }
}
[root@test]$ gcc test.cpp -o test.out
[root@test]$ cat std.out
[root@test]$ ./test.out 1> std.out 2> std.err &
[1] 41775
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai Apr21 pts/15   00:00:00 -bash
0 S root     41775 34824  0  80   0 -  1056 hrtime 00:04 pts/15   00:00:00 ./test.out
0 R root     41779 34824  0  80   0 - 38356 -      00:04 pts/15   00:00:00 ps -lf
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     34824 34817  0  80   0 - 28750 do_wai Apr21 pts/15   00:00:00 -bash
0 R root     41870 34824  0  80   0 - 38356 -      00:05 pts/15   00:00:00 ps -lf
[1]+  Done                    ./test.out > std.out 2> std.err
[root@test]$ cat std.out
Hello World
Hello World
Hello World
...
[root@test]$
此次 std.out 就有輸出了,對比上面兩個例子能夠發現,當進程執行完以後纔會把數據寫到 std.out 中,而不是執行一行寫一行,系統會有個數據緩衝區,進程在執行的時候會將數據寫到緩衝區,在進程結束以後在取出緩衝區中的數據,寫到文件 std.out 中。
 
 
固然,除了 ps -lf 命令能夠查看進程信息,還有諸如 ps aux 和 ps -lA 命令也能夠查看進程信息。還有很多 ps 加各類選項參數的命令也也能夠查看進程信息,可是最經常使用的仍是 ps aux , ps - lA 和 ps -lf 這幾個:
[root@test:/home/robot]
# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.4  0.0 171620 11604 ?        Ss   14:16   0:26 /usr/lib/systemd/systemd --switched-root --system --deserialize 17
systemd+   591  0.0  0.0  19152  6400 ?        Ss   14:17   0:00 /usr/lib/systemd/systemd-networkd
avahi      761  0.0  0.0   9656  6244 ?        Ss   14:17   0:04 avahi-daemon: running [test
root       766  0.0  0.0   6028  2152 ttyS0    Ss+  14:17   0:00 /sbin/agetty -o -p -- \u --keep-baud 115200,38400,9600 ttyS0 vt220
root       767  0.0  0.0   6388  1604 tty1     Ss+  14:17   0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
root       777  0.0  0.0 614464 18100 ?        Ssl  14:17   0:04 python3 -u /opt/nokia/libexec/StorageUtils/script/lsa/local_storage_agent.py
avahi      778  0.0  0.0   8944   432 ?        S    14:17   0:00 avahi-daemon: chroot helper
_test+   779  0.0  0.0 164384  5784 ?        Ssl  14:17   0:01 /opt/nokia/bin/eventdaemon --global
root       784  0.0  0.0      0     0 ?        Z    14:17   0:00 [cat] <defunct>
root       785  0.0  0.0   7672  1196 ?        S    14:17   0:00 avahi-publish -a test.local
_test+   787  0.0  0.0  26736 16288 ?        Ss   14:17   0:00 /opt/nokia/test-acl/bin/ipsetmanager
...
 
[root@test:/home/robot]
# ps -lA
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0     1     0  0  80   0 - 42905 SyS_ep ?        00:00:26 systemd
0 S     0   531   525  0  80   0 -  1455 hrtime ?        00:00:00 schedmon
4 S     0   534     1  0  80   0 -  1879 hrtime ?        00:00:00 hypertracerolcd
4 S     0   550     1  0  80   0 - 205661 core_s ?       00:00:15 python3
4 S   192   591     1  0  80   0 -  4788 SyS_ep ?        00:00:00 systemd-network
...
 
ps aux 和 ps -lA 均可以查看系統運行的全部進程,不一樣的是輸出信息不一樣,ps -lA 的輸出信息和 ps -lf 一致,而 ps aux 的輸出信息是 %CPU / %MEM / VSZ / RSS / STAT 等等。
各參數的意思分別是:
%CPU:進程使用 CPU 的資源百分比;
%MEM:進程佔用物理內存百分比;
VSZ:進程使用的虛擬內存量(KB);
RSS:進程佔用的固定內存量(KB);
STAT:進程目前的狀態。有的進程會同時有多個字符,它們表明的意思是:
    D    不可中斷,Uninterruptible sleep (usually IO)
    R    正在運行,或在隊列中的進程
    S    處於休眠狀態
    T    中止或被追蹤
    Z    殭屍進程
    X    死掉的進程
    <    高優先級
    N    低優先級
    L    有些頁被鎖進內存
    s    包含子進程
    +    位於後臺的進程組
    l    多線程,克隆線程 multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
 
 
除了 ps 命令查看進程信息的命令,還有 pstree 命令可查看各進程的依賴關係:
[root@test]$ pstree -p
systemd(1)─┬─abrt-dbus(45043)─┬─{abrt-dbus}(45044)
           │                  └─{abrt-dbus}(45046)
           ├─agetty(2037)
           ├─agetty(2039)
           ├─atd(1989)
           ├─auditd(604)─┬─audispd(19274)─┬─sedispatch(19275)
           │             │                └─{audispd}(19276)
           │             └─{auditd}(605)
           ├─automount(2029)─┬─{automount}(2030)
           │                 ├─{automount}(2031)
           │                 └─{automount}(2056)
...
其中 -p 選項可查看各進程的 PID,能夠看到有個 PID 爲 1 的 systemd 進程,它是 Liunx 的啓動的第一個進程是 systemd 進程,是全部進程的進程之王,它下面管理着不少子進程,這些子進程又會生出多個子子進程。如 PID 爲 2029 的 automount 進程,它的父進程是 systemd ,它也是 2030/2031/2056 的父進程。2030/2031/2056 它們幾個是兄弟進程。
 
 
ps 和 pstree 執行的結果是靜態的,而使用 top 命令能夠動態查看進程的變化狀況:
[root@test]$ top -H
top - 20:49:51 up 264 days,  3:07, 14 users,  load average: 0.13, 0.08, 0.02
Threads: 567 total,   1 running, 565 sleeping,   0 stopped,   1 zombie
%Cpu(s):  0.2 us,  0.1 sy,  0.0 ni, 99.6 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 37077464 total, 12919928 free,  2649664 used, 21507872 buff/cache
KiB Swap:     1020 total,        0 free,     1020 used. 33678848 avail Mem
 
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
44644 root      20   0   12.7g 335660  21284 S  0.7  0.9   0:04.60 java
43794 root      20   0   12.7g 323332  21332 S  0.7  0.9   0:06.44 java
1959 fluentd   20   0 1382452  28436   4896 S  0.3  0.1  42:04.92 fluentd
 
top 的輸出信息有好幾行,從上到下分別表示:
第一行: 當前時間;系統開機到如今過了多長時間(看看 264 天沒關機,這是 windows 能比的了噠);當前登錄系統的用戶人數;系統在 1/5/15 分鐘的平均工做負載,分別是 0.13/0.08/0.02,越小表示系統越閒置,若高於 1 表示系統 load 偏高。
第二行: 當前進程的總量,以及進程的整體運行狀態。
第三行: CPU 的總體負載。us 表示 CPU 消耗在用戶空間(user space)的時間百分比;sy 表示 CPU 消耗在內核空間(kernel space)的時間百分比;<關於用戶空間和內核空間的文章可看這裏>ni(niceness) 表示 CPU 消耗在 nice 進程(低優先級)的時間百分比;id(idle) 表示 CPU 消耗在閒置進程的時間百分比,值越低表示 CPU 越忙,這裏 id 是 99.6 ,看來 CPU 不忙啊。wa(wait) CPU 等待外部 I/O 的時間百分比,在等待 I/O 的時間裏 CPU 不能幹其它事情;hi(hardware interrupt) CPU 響應硬件中斷請求的時間百分比;si(software interrupt) CPU 響應軟件中斷請求的時間百分比;
第四行:物理內存 Mem 使用狀況;
第五行:虛擬內存 swap 使用狀況;
第六行: PR(Priority) 進程的優先級執行順序,越小表示越早被執行,該值由系統指定,用戶不能更改;NI(nice)與優先級相關,用戶可更改。
 
 

2. 工做管理

將程序/命令轉到後臺執行的形式都屬於工做管理的範疇,顧名思義,工做管理就是管理 Liunx 系統下各個工做的。使用 & 將命令轉到後臺執行,搭配流輸入將數據轉到文件中,從而避免在終端界面顯示,前面已經演示過了。
 
& 命令將工做轉到後臺,工做是出於運行狀態的。而直接使用快捷鍵 Ctrl + Z 會將工做轉到後臺並處於暫停狀態:
[root@test test]# vi log.txt
 
[1]+  Stopped                 vi log.txt
[root@test test]# ps -lf
F S UID          PID    PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
0 S root      107543  107542  0  80   0 -  6374 -      15:58 pts/0    00:00:00 -bash
0 T root      108848  107543  0  80   0 -  9126 -      15:58 pts/0    00:00:00 vi log.txt
0 R root      109559  107543  0  80   0 - 14894 -      15:58 pts/0    00:00:00 ps -lf
[root@test test]#
命令 vi log.txt 產生的進程被轉到後臺,而且它的狀態是 T stopped。
 
jobs 命令能夠查看當前工做的狀況,fg 命令可將當前工做從後臺轉到前臺,而且該工做是出於運行狀態:
[root@test test]# jobs
[1]+  Stopped                 vi log.txt
[root@test test]# fg %1
vi log.txt
 
[root@test test]# jobs
[1]-  Stopped                 vi log.txt
[2]+  Stopped                 vi abc
[root@test test]# fg
vi abc
注意,fg 後加 job 編號即表示將該 job 從後臺轉到前臺,前面要加 % 號。jobs 的輸出 job 編號後有 + 和 - 號,+ 表示默認調用的工做,不加編號的 fg 命令會調用 + 所在的那個 job。
 
fg 命令是將工做從後臺轉到前臺。bg 命令會將後臺出於暫停狀態的工做轉爲運行狀態,一樣的,bg 後需加 % 和工做編號:
[root@test]$ ./test.out
Hello World
^Z
[1]+  Stopped                 ./test.out
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     49612 49605  0  80   0 - 28750 do_wai 00:13 pts/15   00:00:00 -bash
0 T root     49854 49612  0  80   0 -  1056 do_sig 00:14 pts/15   00:00:00 ./test.out
0 R root     49880 49612  0  80   0 - 38356 -      00:14 pts/15   00:00:00 ps -lf
[root@test]$ bg %1
[1]+ ./test.out &
Hello World
[root@test]$ Hello World
Hello World
^C
[root@test]$ Hello World
Hello World
Hello World
 
上面的例子中,程序轉到後臺執行時會循環打印 Hello World,使用 Ctrl + C 也沒辦法將終止程序,仍是會繼續打印。此時就要用到 kill 命令殺掉執行該程序的進程或者殺掉該工做。
kill -l 命令可查看 kill 可以使用的信號:
[root@test]$ kill -l
1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
 
一般使用的最多的信號就是 1/9/15。1 SIGHUP 表示從新讀取參數的配置文件,9 SIGKILL 強制刪除工做/進程,15 SIGTERM 以正常方式結束一個工做/進程,若是一個工做/進程不能以正常方式結束掉,仍是要使用 9 強制結束的。
不加信號的 kill 默認使用的是 15 SIGTERM 這個信號。若是要 kill 工做的話,須要在工做編號前加 % 號,默認 kill 的是進程的 PID,下面用兩種方式 kill Hello World 程序產生的進程:
# kill -9 %n
[root@test]$ ./test.out
Hello World
Hello World
^Z
[1]+  Stopped                 ./test.out
[root@test]$ kill -9 %1
[1]+  Killed                  ./test.out
 
# kill -9 PID
[root@test]$ ./test.out
Hello World
^Z
[1]+  Stopped                 ./test.out
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     49612 49605  0  80   0 - 28750 do_wai 00:13 pts/15   00:00:00 -bash
0 T root     50944 49612  0  80   0 -  1056 do_sig 00:28 pts/15   00:00:00 ./test.out
0 R root     50950 49612  0  80   0 - 38356 -      00:28 pts/15   00:00:00 ps -lf
[root@test]$ bg %1
[1]+ ./test.out &
Hello World
[root@test]$ Hello World
Hello World
[1]+  Killed                  ./test.out
[root@tu-qiaolin-683ci-1 ~]$ kill -9 50944   # exec at another tty
 
使用 & 和 Ctrl + Z 將工做放到後臺運行時,若是當前終端關閉了那麼相應的工做也會被停掉(當前終端 bash 是 & 和 Ctrl + Z 的父進程,父進程關掉了,相應的子進程也會隨之結束),使得工做不能繼續。Liunx 提供了 nohup 命令能夠將工做放到系統後臺運行,即便當前終端關閉了,工做仍是能夠繼續運行(當前終端關閉了,nohup 會將 systemd 做爲本身的父進程繼續執行),舉例:
[root@test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
 
int main(void)
{
    int i = 0;
    for(i==0;i<20;i++)
    {
        sleep(2);
        printf("Hello World\n");
    }
}
[root@test]$ gcc test.cpp -o test.out
[root@test]$ nohup ./test.out &
[1] 50772
[root@test]$ nohup: ignoring input and appending output to ‘nohup.out’
[root@test]$ ps -lf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root     45024 45018  0  80   0 - 28752 do_wai 20:39 pts/15   00:00:00 -bash
0 S root     50772 45024  0  80   0 -  1056 hrtime 21:41 pts/15   00:00:00 ./test.out
0 R root     50777 45024  0  80   0 - 38356 -      21:41 pts/15   00:00:00 ps -lf
[root@test]$ exit
logout
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Session stopped
 
Last login: Thu Apr 23 23:48:09 2020 from 10.140.119.177
[root@tu-qiaolin-683ci-1 ~]$ ps -lA | grep 50772
0 S     0 50772     1  0  80   0 -  1056 hrtime ?        00:00:00 test.out
[root@tu-qiaolin-683ci-1 ~]$ cat test/nohup.out
Hello World
Hello World
Hello World
...
[root@tu-qiaolin-683ci-1 ~]$
退出了當前終端,工做仍是繼續在系統後臺運行,且 nohup 產生的進程被 systemd 進程給接管了。
 
 
 
(完)
相關文章
相關標籤/搜索