進程可以說是OS的最基本的構件,因為有了進程才有了 偽並行 ,所謂偽並行是指 多道程式之間進行快速切換,以達到多任務處理的能力.更細緻的說 是在 就緒態, 運行態 和 阻塞態之間的轉換 .
Running State -> Blocking State:
可能是進程發生IO請求,比如一個進程等待另一個進程提供輸入時,或者等待來自另一個進程的資訊時 ...
Running State -> Ready State:
這個最常見,比如時鐘中斷,比如進程優先順序
Ready State -> Running State:
當前啟動並執行進程用完CPU的時間,發送器就從就緒態的進程中選擇一個運行
Blocking State -> Ready State:
當滿足阻塞到就緒態的事件發生時,就會轉換.
偽並行
可以看到A ,B ,C 三個進程, A遇到I/O讀寫,暫時放棄CPU,發送器選擇一個進程(B)來運行,這是來個高優先順序的C ,B暫時掛起,讓C運行,AI/O讀寫成功後 ,由於A優先順序大於C所以C繼續運行,運行完後先運行優先順序高的C再運行B !
進程樹
進程是程式的一個動態執行實體,隨著程式的執行 而不斷進行變化,它具有生命週期,進程就像大千生物一樣 ,也有自己的父母,產生它的進程叫做父進程,每個進程只有一個父進程,可以有多個子進程,linux中可以通過fork來建立子進程.來看看linux中的進程,linux中有個init進程,PID為 1 , 可以說是所有進程的祖先進程,如果某個進程的父進程終止後,自己還live, 則該進程則變成孤兒進程(因為沒有父進程了),但是有它祖先保佑著它,負責養它(它祖先長命著呢) 來看看這棵進程樹!
進程式控制制塊
建立一個進程時,就要為它建立一個PCB(process control block),進程退出時收回PCB,PCB可謂進程的核心,裡面囊括了
標識符, 狀態, 優先順序, PC, 記憶體指標, 上下文, IO 等資訊,正因為有了這些才可以中斷一個進程的執行.
linux中把對進程的描述結構叫做task_struct (PCB)
struct task_struct { long state; long counter; long priority; long signal; struct sigaction sigaction[32]; long blocked; int exit_code; unsigned long start_code,end_code,end_data,brk,start_stack; long pid,father,pgrp,session,leader; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; long alarm; long utime,stime,cutime,cstime,start_time; unsigned short used_math; /* file system info */ int tty; unsigned short umask; struct m_inode * pwd; struct m_inode * root; struct m_inode * executable; unsigned long close_on_exec; struct file * filp[NR_OPEN]; struct desc_struct ldt[3]; struct tss_struct tss;}
另外要說的是PCB是核心中頻繁讀寫的資料結構,所以被常駐在記憶體中.
還有一個重點是,PCB的存放,它是和核心棧存放在一起的,這是因為linux為了節約空間,所以把兩者放在一起,佔用8K的記憶體區.
union task_union {struct task_struct task;unsigned long kernel_stack[2408];};
這是大概的資料結構,PCB大概佔1KB左右,所以核心棧的大小不能超過7KB,否則核心張會覆蓋PCB,從而導致崩潰!
除了節約空間,把兩者放在一起 可以使核心更快的找到PCB,另外防止在建立進程時動態分配額外的記憶體.
進程切換
進程切換需要OS獲得控制權,如果你有些底層代碼的經驗,你會知道進程切換的代價是非常高的! 我們來看看哪些會造成進程切換?
-> 第一想到的就是系統中斷中的IO操作了 .
-> 第二想到的是system call,調用系統函數也會產生進程切換,所以調用系統函數開銷是很大的.
-> 還有些 正常的時鐘中斷, 也就是說進程超過了最大規定的時間片,如果超過了進程必須切換到就緒態.調入另外一個進程