文章目錄
- 進程
- 線程
- 進程與線程比較
進程
什么是進程?
概念上來說,進程是擔當OS資源分配的實體。通俗來說,進程是我們OS上一個在運行的程序。
我們的OS上不止有一個進程,當我們的某一個進程像是去磁盤上讀文件時,由于磁盤的速度很慢,這是為了提高CPU的利用率,這時就會將該進程掛起,而去執行另一個進程,直到磁盤讀寫完畢,給OS一個信號,OS從而在去調度原來被掛起的進程。
進程不止有一個,所以就需要我們對進程進行管理,怎么管理:先描述,后組織,將進程的屬性抽象成結構體,然后將每個進程的結構體通過鏈表組織起來。其中所謂 的結構體就是我們的PCB—進程控制模塊,在Linux中就是我們的task_struct。每一個進程都有其獨立的PCB,那么操作系統對進程的管理就變為了實際上對PCB進行管理。PCB放在哪個組織結構里實則對應著的是其不同的進程狀態,我們的進程至少具備三種基本狀態:運行狀態,就緒狀態,阻塞狀態。其分別對應著運行隊列,就緒隊列,阻塞隊列這樣的組織結構。
運行狀態:正在運行的進程
就緒狀態:隨時可以運行,但CPU正在被其他進程所占有
阻塞狀態:由于等待輸入輸出等時間,無法運行,及時給它CUP控制權,也無法運行。
若有大量的阻塞狀態,回導致我們的內存利用率不高,所以通常會把阻塞狀態的進程的物理內存換入到磁盤中,只留有其內核數據在內存中,當需要再運行的時候,再從磁盤中寫回,我們將這種狀態成為掛起狀態。
task_struct中有什么?
標示符: 描述本進程的唯一標示符,用來區別其他進程。
狀態: 任務狀態,退出代碼,退出信號等。
優先級: 相對于其他進程的優先級。
程序計數器: 程序中即將被執行的下一條指令的地址。
內存指針: 包括程序代碼和進程相關數據的指針,還有和其他進程共享的內存塊的指針
上下文數據: 進程執行時處理器的寄存器中的數據。
I/O狀態信息: 包括顯示的I/O請求,分配給進程的I/O設備和被進程使用的文件列表。
記賬信息: 可能包括處理器時間總和,使用的時鐘數總和,時間限制,記賬號等。
其他信息
那我們的進程都要是處于就緒狀態,OS該如何決策調度哪一個進程呢?這就是是OS調度器要干的事情啦?我們可以在可以簡單的說名以下:每個進程都有優先級,優先級越高,則越是容易被調度,當然這個優先級的計算,是調度器幫我們去評判的,那么我們人為的可以干預嗎,是可以的!我們的nice值就可以幫助我們進行優先級的干預。
PRI是進程的優先級,其表示該進程被cpu執行的先后順序,其值越小,優先級越高。 那么NI是啥呢?可以將其理解為優先級的修正數值。
因此調整nice值就是調整進程的優先級。nice值的取值范圍是-19 - 20。
那么如何更改nice值
可以使用top命令進行修改。其使用格式如下: 進入top后按“r”–>輸入進程PID–>輸入nice值。
調整已存在進程的nice:renice
我們的每個進程都擁有其獨立的進程地址空間,以及頁表。這樣的設計使得我們多進程得以實現,避免了直接對物理內存操作而引起地址沖突,再者,由于虛擬地址到物理地址之間要經過映射,并且我們對進程地址空間進行了分段,因此在映射時,我們自然就可以進行一些檢查,檢查其訪問的地址是否安全,以及該地址所存儲數據的屬性是否允許我們進行操作等;進程地址空間使得讓每一個進程都覺得自己擁有整個內存,它不關心其他的進程,這也是進程獨立的體現,并且,由于有局部性原理,我們可以將一些暫時不用的其他進程所占用的內存,換出到磁盤中,從而給當前進程用,提高了內存的使用效率。
我們可以在當前進程中,利用fork函數創建子進程,我們的進程具有獨立性,創建的子進程通過拷貝父進程的PCB,再更具自己的情況,稍加更改,就成了自己的PCB結構體,因此在父進程在創建子進程前的一些屬性和數據,例如文件描述符表等子進程都是可以看見的。當然子進程也會創建出自己的進程地址空間。只不過為了提高效率,節約內存其采用讀時共享,寫時拷貝的策略,在讀時,和父進程映射同一塊物理內存,只有寫時,才會重新將數據映射到心的物理內存上。
新創建出來的子進程,若我們不進行等待,就會形成僵尸進程,僵尸進程會占用一定的資源,我們的子進程在退出時,其會保留一些退出信息,當然由于進程并未完全退出,所以其PCB當然也存在在內核中,所以為了減少資源的浪費,我們要對子進程進行回收,回收可以采用父進程阻塞式的等待,父進程進行輪詢判斷等方式進行回收,還可以通過信號的方式:因為我們的子進程在退出時,會像父進程發送SIGCHILD信號,所以我們若不關心退出信息,可以通過顯式的忽略該信號(特例),關心的話可以在自定義信號處理函數中進行等待(循還+非阻塞式等待)。
我們在的進程還可以進行替換,利用execv函數族進行進程替換,其本質就是將該進程的代碼和數據進行替換,PCB等結構不變。
進程終止我們可以通過exit函數,信號,直接return等方式進行退出。
線程
什么叫線程?
線程是OS進行調度運行的最小單位,線程運行在進程的內部,是進程實際運行的單位。進程是承擔OS資源的實體,那么線程就是承擔進程部分資源的一個實體。
在一個進程內部,不止有一個進程,那么我們是不是要對其進行管理,先描述,后組織,將其屬性抽象從某種結構體,我們將這種結構體成為TCB。也就是說,OS實際調度的是一個個TCB,當然這是對于一般OS來說。Linux有自己的方案。
Linux認為既然線程的結構和進程類似,那么我就不用去再實現線程的一套結構,而是將一個線程當作一個特殊的進程來看待,這樣的優勢則是,減少了我們結構的復雜度,從而降低了BUG的產生。
因此,現在對于CPU而言,其不管你是線程還是進程,它所認的就是PCB結構體。
為了能夠實現線程,我們就要將原來進程的那一套進行一些更改。這就涉及到了線程的一些特性。我們的同一個進程的多個線程之間是能夠共享代碼段,數據段,打開的文件等資源的,也就是說其要共享進程地址空間上的一些數據。這當然好辦,我們在創建這個特殊的進程時,將其和創建它的進程的地址空間進行共享。
當然除了共享的數據以外,每個線程都是一個執行流,它也應該擁有自己的私有棧,獨立的上下文數據,信號屏蔽字,線程ID,調度優先級等。后面的一些數據,由于線程有獨立的PCB,因此可以私有化,但是,棧怎么辦?用戶棧只有一個,要是都放在用戶棧里,豈不是太過于混亂。Linux的解決辦法是這樣的:此時,我們的線程對于上層用戶來說,操作是有難度的,所以有第三方庫經過對Linux 的線程接口封裝為我們提供了使用Linux線程更方便的方法,在使用線程時,程序運行起來后,會將第三方庫加載到我們的mmp共享區域,這個庫不光為我們解決了使用線程系統調用接口難的問題,還幫我們提供了線程所使用的棧。這個棧在哪呢?就在我們的mmp中,并且我們的線程id就是其棧的起始地址,而我們ps -aL中顯式的LWP則是內核中標記線程的id。它與我們用戶所看到的線程id是一一對應的。
所以Linux下的進程<=其他OS下的進程。OS下的進程我們稱為輕量級進程。
進程與線程比較
進程承擔OS資源分配的實體,而線程是CPU的基本調度單位,線程在進程中。
線程之間大多數資源是共享的,所以線程間通信方便,而進程是獨立的,所以進程間通信難度較大。
線程能夠減少并發執行的時間和空間開銷-----體現在:
線程的創建更加簡單。
線程進行切換時效率更快。因為我們在進行切換時,不僅僅是將PCB換下去,保存上下文,我們的計算機為了更快的效率,在CPU和內存之間還由多級緩存,我們的線程中的資源好多都是共享的,所以緩存命中率高,而進程切換,則需要重新加載緩存。
線程的釋放也要比進程快。
但是我們同一進程內的線程異常退出,那么我們整個進程也都退出了,而且線程由于一些數據共享的問題,會帶來線程安全。