一、進程與線程
1、進程
進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。-----------百度百科
在Linux中進程是運行中的程序的一個副本,是被載入內存的一個指令集合,使用進程ID(PID)來標記各個進程。我們可使用echo $$來查看當前程序的進程號。
[ root@vinsent ~ ]#echo $$ # 獲取當前bash的進程號 3267 [ root@vinsent ~ ]# [ root@vinsent ~ ]#pstree -p systemd(1)─┬─NetworkManager(520)─┬─dhclient(623) │ ├─{NetworkManager}(531) │ └─{NetworkManager}(533) ...中間省略... ├─sshd(935)───sshd(3263)───bash(3267)───pstree(7269) ├─systemd-journal(361) ---|--- ├─systemd-logind(496) # 通過pstree驗證,和echo $$的結果一樣 ├─systemd-udevd(380) ├─tuned(920)─┬─{tuned}(1105) │ ├─{tuned}(1109) │ ├─{tuned}(1111) │ └─{tuned}(1116) └─vmtoolsd(495) [ root@vinsent ~ ]#
進程得到的權限,是對應之執行者的權限,除了SUID、SGID等權限。
進程是動態概念,有生命週期;可附加SUI、SGID等權限。
進程號隨機分配( 進程號唯一標識一個進程;一個inode唯一標識一個磁盤文件 )。
在 CentOS 6 及之前的系統中第一個進程爲:init;CentOS 7 中第一個進程爲:systemd。
2、守護進程
守護進程(daemon)是一類在後臺運行的特殊進程,用於執行特定的系統任務。很多守護進程在系統引導的時候啓動,並且一直運行直到系統關閉。另一些只在需要的時候才啓動,完成任務後就自動結束。用戶使守護進程獨立於所有終端是因爲,在守護進程從一個終端啓動的情況下,這同一個終端可能被其他的用戶使用。守護進程沒有控制終端,因此當某些情況發生時,不管是一般的報告性信息,還是需由管理員處理的緊急信息,都需要以某種方式輸出。Syslog 函數就是輸出這些信息的標準方法,它把信息發送給 syslogd 守護進程。例如我們用來監控某些非獨立服務的程序xinetd,他就是一個超級守護進程,用來管理系統的的非獨立守護進程(瞬時守護進程),這些非獨立進程通常情況是處於關閉狀態,由xinetd進程來監控,如果有人使用相關的服務,則喚醒這些非獨立守護進程,進行工作。我們可使用chkconfig來查看。
[root@localhost ~]# yum install xinetd # 我用的是最小化安裝,自動不安裝xinetd服務 [root@localhost ~]# chkconfig auditd 0:off 1:off 2:on 3:on 4:on 5:on 6:off # 獨立服務 autofs 0:off 1:off 2:on 3:on 4:on 5:on 6:off blk-availability 0:off 1:on 2:on 3:on 4:on 5:on 6:off ...中間省略 xinetd based services: # 下面的服務全是非獨立服務,由xinetd來代爲管理 chargen-dgram: off chargen-stream: off daytime-dgram: off daytime-stream: off discard-dgram: off discard-stream: off echo-dgram: off echo-stream: off tcpmux-server: off time-dgram: off time-stream: off [root@localhost ~]# chkconfig --add crond # 添獨立服務是添加不到xinetd中的
NOTE:我們每次新添加(chkconfig --add server_name)一個服務或者刪除(chkconfig --del server_name)一個服務後,我們必須重啓xinetd服務(service restart xinetd)。
3、線程
有時被稱爲輕量級進程(Lightweight Process,LWP),是程序執行流的最小單元。一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資源。
一個線程可以創建和撤消另一個線程,同一進程中的多個線程之間可以併發執行。由於線程之間的相互制約,致使線程在運行中呈現出間斷性。線程也有就緒、阻塞和運行三種基本狀態。就緒狀態是指線程具備運行的所有條件,邏輯上可以運行,在等待處理機;運行狀態是指線程佔有處理機正在運行;阻塞狀態是指線程在等待一個事件(如某個信號量),邏輯上不可執行。
有線程,那麼就有多線程的說法。線程是程序中一個單一的順序控制流程。進程內一個相對獨立的、可調度的執行單元,是系統獨立調度和分派CPU的基本單位指運行中的程序的調度單位。在單個程序中同時運行多個線程完成不同的工作,稱爲多線程。線程共享整個程序的內存資源;但是也存在一個線程出故障,影響其他線程,爭奪資源。爲了避免這種爭奪資源的情況,系統加入了鎖 fork()機制clone()機制。通常採用寫時複製的原則:
在Linux中我們通過pstree能看到線程通常在進程樹中表現爲"{ thread_name }"形式。
[ root@vinsent ~ ]#yum -y install httpd &>/dev/null [ root@vinsent ~ ]#pstree -p systemd(1)─┬─NetworkManager(520)─┬─dhclient(623) │ ├─{NetworkManager}(531) # NetworkManager開啓的多線程 │ └─{NetworkManager}(533) ├─atd(509) ├─auditd(471)───{auditd}(482) ├─automount(929)─┬─{automount}(930) │ ├─{automount}(931) │ ├─{automount}(948) │ └─{automount}(960) ├─crond(506) ├─dbus-daemon(492)───{dbus-daemon}(493) ├─firewalld(515)───{firewalld}(619) ├─httpd(7491)─┬─httpd(7494) # httpd服務開啓的多個進程 │ ├─httpd(7495) │ ├─httpd(7496) │ ├─httpd(7497) │ └─httpd(7498) ...後面省略... [ root@vinsent ~ ]#
4、線程與進程的關係
一個進程可包含多個線程;
每一個程序都至少有一個線程,若程序只有一個線程,那就是程序本身;
地址空間和其它資源(如打開文件):進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。
通信:進程間通信IPC,線程間可以直接讀寫進程數據段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數據的一致性。
調度和切換:線程上下文切換比進程上下文切換要快得多。
在多線程OS中,進程不是一個可執行的實體。
二、進程優先級
有時我們面臨着很多選擇,同一時間有許多事情要辦,那麼我們總會有選擇性的去執行,先做什麼後做什麼,這就是一種優先級,同樣的作爲一個高速運轉的計算機而言,有序的優先級約定顯得異常重要。
優先級(priority)是一種約定,優先級高的先做,優先級低的後做。優先級是計算機分時操作系統在處理多個作業程序時,決定各個作業程序接受系統資源的優先等級的參數。優先級是計算機操作系統給任務指定的優先等級。它決定任務在使用資源時的優先次序。給設備指定的優先等級。它決定設備在提出中斷請求時,得到處理機響應的先後次序。任務調度優先級主要是指任務被調度運行時的優先級,主要與任務本身的優先級和調度算法有關。特別在實時系統中,任務調度優先級反應了一個任務重要性與緊迫性。-------百度百科
1、系統優先級
進程優先級起作用的方式從發明以來基本沒有什麼變化,無論是隻有一個cpu的時代,還是多核cpu時代,都是通過控制進程佔用cpu時間的長短來實現的。就是說在同一個調度週期中,優先級高的進程佔用的時間長些,而優先級低的進程佔用的短些。在Linux系統中系統優先級的範圍爲 0-139 共 140 及等級 ;其值越小則優先級越高 ;CentOS 6之後爲 系統優先級的範圍變成了0-98。
2、實時優先級與非實時優先級
實時操作系統需要保證相關的實時進程在較短的時間內響應,不會有較長的延時,並且要求最小的中斷延時和進程切換延時。對於這樣的需求,一般的進程調度算法,無論是O1還是CFS都是無法滿足的,所以內核在設計的時候,將實時進程單獨映射了100個優先級,這些優先級都要高與正常進程的優先級(nice值),而實時進程的調度算法也不同,它們採用更簡單的調度算法來減少調度開銷。總的來說,Linux系統中運行的進程可以分成兩類實時優先級與非實時優先級。實時優先級又稱動態優先級,其的範圍是0-99;其值越大,優先級越高;動態優先級不能人爲調整,系統自己調度。所有優先級值在0-99範圍內的,都是實時進程,所以這個優先級範圍也可以叫做實時進程優先級,而100-139範圍內的是非實時進程。在系統中可以使用chrt命令來查看、設置一個進程的實時優先級狀態。
3、靜態優先級
靜態優先級又稱非實時優先級:通常用nice值表示;
nice值所在範圍對應系統優先級的 100-139 ;其值爲:-20~19;
進程默認啓動時的nice值爲0,優先級爲120;
只有根用戶才能降低nice值(提高優先性);
nice 優先級 其值越小則優先級越高;
在幾種優先級中,nice的值是我們可以手動調整的,我們可以指定以某個nice值打開某個程序;使用renice命令可以對一個正在運行的進程進行nice值的調整,我們也可以使用比如top、ps等命令查看進程的nice值,具體方法我會在下一章給大家介紹,但對於普通用戶來說:renice在調整程序的優先級的時候只能將其值調低。
[ root@vinsent ~ ]#ps axo pid,cmd,pri,nice # 只顯示進程進程號,進程名,pri優先級,nice值 PID CMD PRI NI 1 /usr/lib/systemd/systemd -- 19 0 ...中間省略... 4344 -bash 19 0 4381 vim switch_arry.sh 19 0 # vim進程默認的NICE值爲0 4388 [kworker/0:2H] 39 -20 4416 /sbin/dhclient -d -q -sf /u 19 0 4422 ps axo pid,cmd,pri,nice 19 0 [ root@vinsent ~ ]# [ root@vinsent ~ ]#renice -n -1 4381 # 修改vim的nice值 4381 (process ID) old priority 0, new priority -1 [ root@vinsent ~ ]#ps axo pid,cmd,pri,nice PID CMD PRI NI 1 /usr/lib/systemd/systemd -- 19 0 ...中間省略... 4344 -bash 19 0 4381 vim switch_arry.sh 20 -1 # 修改了NICE 4388 [kworker/0:2H] 39 -20 4428 [kworker/0:0H] 39 -20 4429 ps axo pid,cmd,pri,nice 19 0 [ root@vinsent ~ ]#su - tom # 切換至普通用戶 [ tom@vinsent root ]$renice -n -5 4602 # 調高NICE優先級,受到限制; renice: failed to set priority for 4602 (process ID): Permission denied [ tom@vinsent root ]$renice -n 5 4602 #調低NICE值成功;說明普通用戶只能調低NICE優先級 4602 (process ID) old priority 0, new priority 5
需要大家注意的是,我在這裏都在使用nice值這一稱謂,而非優先級(priority)這個說法。當然,nice和renice的man手冊中,也說的是priority這個概念,但是要強調一下,請大家真的不要混淆了系統中的這兩個概念,一個是nice值,一個是priority值,他們有着千絲萬縷的關係,但對於當前的Linux系統來說,它們並不是同一個概念。nice值雖然不是priority,但是它確實可以影響進程的優先級。在英語中,如果我們形容一個人nice,那一般說明這個人的人緣比較好。什麼樣的人人緣好?往往是謙讓、有禮貌的人。比如,你跟一個nice的人一起去吃午飯,點了兩個一樣的飯,先上了一份後,nice的那位一般都會說:"你先吃你先吃!",這就是人緣好,這人nice!但是如果另一份上的很晚,那麼這位nice的人就要餓着了。這說明什麼?越nice的人搶佔資源的能力就越差,而越不nice的人搶佔能力就越強。這就是nice值大小的含義,nice值越低,說明進程越不nice,搶佔cpu的能力就越強,優先級就越高。在原來使用O1調度的Linux上,我們還會把nice值叫做靜態優先級,這也基本符合nice值的特點,就是nice值設定好了之後,除非我們用renice去改它,否則它是不變的。而priority的值在之前內核的O1調度器上表現是會變化的,所以也叫做動態優先級。
4、三種優先級的關係
系統優先級、實時優先級、nice值之間有其對應關係,其關係如下圖:
三、調度算法
1、實時優先級的調度策略
在上面第二節中我們提到了一點調度算法這個概念,這一小節重點來給大家說說一些常見的系統調度算法。調度策略主要是針對實時優先級與非實時優先級而言的。我們可以通過chrt命令來查看或設置進程實時優先級的狀態。
[ root@vinsent ~ ]#chrt Show or change the real-time scheduling attributes of a process. Set policy: chrt [options] <priority> <command> [<arg>...] chrt [options] --pid <priority> <pid> Get policy: chrt [options] -p <pid> Policy options: -b, --batch set policy to SCHED_BATCH -d, --deadline set policy to SCHED_DEADLINE -f, --fifo set policy to SCHED_FIFO -i, --idle set policy to SCHED_IDLE -o, --other set policy to SCHED_OTHER -r, --rr set policy to SCHED_RR (default) Scheduling options: -R, --reset-on-fork set SCHED_RESET_ON_FORK for FIFO or RR -T, --sched-runtime <ns> runtime parameter for DEADLINE -P, --sched-period <ns> period parameter for DEADLINE -D, --sched-deadline <ns> deadline parameter for DEADLINE ...後面省略...
從上面的Policy options選項中我們發現,系統給我們提供了6中調度策略。但是這裏並沒有說明的是,這五種調度策略是分別給兩種進程用的,對於實時進程可以用的調度策略是:SCHED_FIFO、SCHED_RR,而對於非實時進程則是:SCHED_OTHER、SCHED_BATCH、SCHED_IDLE。系統的整體優先級策略是:如果系統中存在需要執行的實時進程,則優先執行實時進程。直到實時進程退出或者主動讓出CPU時,纔會調度執行非實時進程。實時進程可以指定的優先級範圍爲1-99,將一個要執行的程序以實時方式執行的方法爲:
[ root@vinsent ~ ]#bash # 新打開一個bash [ root@vinsent ~ ]#chrt -p $$ # 查看該bash的調度策略 pid 5140's current scheduling policy: SCHED_OTHER pid 5140's current scheduling priority: 0 [ root@vinsent ~ ]#
可以看到,新打開的bash已經是實時進程,默認調度策略爲SCHED_OTHER,優先級爲0。如果想修改調度策略,就加個參數;
[ root@vinsent ~ ]#chrt -f 10 bash # 以優先級10 ,SCHED_FIFO策略打開一個bash [ root@vinsent ~ ]#chrt -p $$ pid 5169's current scheduling policy: SCHED_FIFO pid 5169's current scheduling priority: 10
SCHED_RR和SCHED_FIFO都是實時調度策略,只能給實時進程設置。對於所有實時進程來說,優先級高的(就是priority數字小的)進程一定會保證先於優先級低的進程執行。SCHED_RR和SCHED_FIFO的調度策略只有當兩個實時進程的優先級一樣的時候纔會發生作用,其區別也是顧名思義:
SCHED_FIFO:以先進先出的隊列方式進行調度,在優先級一樣的情況下,誰先執行的就先調度誰,除非它退出或者主動釋放CPU。
SCHED_RR:以時間片輪轉的方式對相同優先級的多個進程進行處理。時間片長度爲100ms。
2、非實時優先級調度策略
上面講述了Linux對於實時進程的優先級和相關調度算法的描述。整體很簡單,也很實用。而相對更麻煩的是非實時進程,它們纔是Linux上進程的主要分類。對於非實時進程優先級的處理,我們首先還是要來介紹一下它們相關的調度算法:O1和CFS。
(1)O1調度器的調度過程
首先,進程產生(fork)的時候會給一個進程分配一個時間片長度。這個新進程的時間片一般是父進程的一半,而父進程也會因此減少它的時間片長度爲原來的一半。就是說,如果一個進程產生了子進程,那麼它們將會平分當前時間片長度。比如,如果父進程時間片還剩100ms,那麼一個fork產生一個子進程之後,子進程的時間片是50ms,父進程剩餘的時間片是也是50ms。這樣設計的目的是,爲了防止進程通過fork的方式讓自己所處理的任務一直有時間片。不過這樣做也會帶來少許的不公平,因爲先產生的子進程獲得的時間片將會比後產生的長,第一個子進程分到父進程的一半,那麼第二個子進程就只能分到1/4。對於一個長期工作的進程組來說,這種影響可以忽略,因爲第一輪時間片在耗盡後,系統會在給它們分配長度相當的時間片。
針對所有R狀態進程,O1算法使用兩個隊列組織進程,其中一個叫做活動隊列,另一個叫做過期隊列。活動隊列中放的都是時間片未被耗盡的進程,而過期隊列中放時間片被耗盡的進程。
新產生的進程都會先獲得一個時間片,進入活動隊列等待調度到CPU執行。而內核會在每個tick間隔期間對正在CPU上執行的進程進行檢查。一般的tick間隔時間就是cpu時鐘中斷間隔,每秒鐘會有1000個,即頻率爲1000HZ。每個tick間隔週期主要檢查兩個內容:
(1) 當前正在佔用CPU的進程是不是時間片已經耗盡了?
(2) 是不是有更高優先級的進程在活動隊列中等待調度?如果任何一種情況成立,就把則當前進程的執行狀態終止,放到等待隊列中,換當前在等待隊列中優先級最高的那個進程執行。
(2)CFS完全公平調度
O1已經是上一代調度器了,由於其對多核、多CPU系統的支持性能並不好,並且內核功能上要加入cgroup等因素,Linux在2.6.23之後開始啓用CFS作爲對一般優先級(SCHED_OTHER)進程調度方法。在這個重新設計的調度器中,時間片,動態、靜態優先級以及IO消耗,CPU消耗的概念都不再重要。
CFS採用了一種全新的方式,對上述功能進行了比較完善的支持。其設計的基本思路是,我們想要實現一個對所有進程完全公平的調度器。又是那個老問題:如何做到完全公平?答案跟上一篇IO調度中CFQ的思路類似:如果當前有n個進程需要調度執行,那麼調度器應該再一個比較小的時間範圍內,把這n個進程全都調度執行一遍,並且它們平分cpu時間,這樣就可以做到所有進程的公平調度。那麼這個比較小的時間就是任意一個R狀態進程被調度的最大延時時間,即:任意一個R狀態進程,都一定會在這個時間範圍內被調度相應。這個時間也可以叫做調度週期,其英文名字叫做:sched_latency_ns。進程越多,每個進程在週期內被執行的時間就會被平分的越小。調度器只需要對所有進程維護一個累積佔用CPU時間數,就可以衡量出每個進程目前佔用的CPU時間總量是不是過大或者過小,這個數字記錄在每個進程的vruntime中。所有待執行進程都以vruntime爲key放到一個由紅黑樹組成的隊列中,每次被調度執行的進程,都是這個紅黑樹的最左子樹上的那個進程,即vruntime時間最少的進程,這樣就保證了所有進程的相對公平。在基本驅動機制上CFS跟O1一樣,每次時鐘中斷來臨的時候,都會進行隊列調度檢查,判斷是否要進程調度。當然還有別的時機需要調度檢查,發生調度的時機可以總結爲這樣幾個:
當前進程的狀態轉換時。主要是指當前進程終止退出或者進程休眠的時候。
當前進程主動放棄CPU時。狀態變爲sleep也可以理解爲主動放棄CPU,但是當前內核給了一個方法,可以使用sched_yield()在不發生狀態切換的情況下主動讓出CPU。
當前進程的vruntime時間大於每個進程的理想佔用時間時(delta_exec > ideal_runtime)。這裏的ideal_runtime實際上就是上文說的sched_latency_ns/進程數n。當然這個值並不是一定這樣得出,下文會有更詳細解釋。
當進程從中斷、異常或系統調用返回時,會發生調度檢查。比如時鐘中斷。
參閱:http://www.linuxidc.com/Linux/2016-05/131244.htm