1)爲實現操做系統的併發性和共享性,引入進程html
2)進程是程序執行時的一個實例,相似於一個活動,它有程序、輸入、輸出和狀態這幾個部分組成。舉個例子,一個程序員爲他的女兒作蛋糕,他有作蛋糕的食譜和各類原料。食譜是程序(用某種形式描述的算法),程序員是CPU,各類原料是輸入數據。進程就是程序員閱讀食譜、取來各類原料來製做蛋糕一系列動做的總和。同時,進程它也有狀態,假設這個程序員在作蛋糕的時候,他的兒子哭着跑過來,說他的手被小刀割破了,程序員此時就記錄下他按照食譜作到哪了(保存進程的當前狀態,進程被掛起),而後按照急救手冊處理傷口。這裏,就至關於CPU從一個進程切換到另外一個高優先級的進程。當傷口處理完後,程序員又回去作蛋糕,從他所記錄的地方繼續作下去。(程序和進程的區別能夠用這個作蛋糕的例子來講明)linux
3)在進程模型中,進程是資源分配和獨立調度的基本單位。程序員
主要是記前3點算法
4)進程具備五大特性:緩存
動態性(最基本):進程具備生命週期,是動態地建立、變化、結束的。安全
併發性:多個進程能夠併發運行。數據結構
獨立性:進程是資源分配和獨立調度的基本單位。多線程
異步性:因爲進程間共享資源和協同合做,所以產生相互制約的關係,使進程具備執行的間斷性(走走停停)。併發
結構性:每一個進程都配置一個PCB(進程控制塊,一種數據結構)對其進行描述。異步
某一時刻進程的內容和狀態的集合,進程映像一般由程序塊、數據塊和一個PCB(進程控制塊)組成,進程映像是靜態的,進程是動態的。
嚴格說,CPU在某一瞬間只能運行一個進程。但在1秒鐘內,它可能運行多個進程,這樣就產生了並行的錯覺。
1.4.1三個狀態
1)運行態:進程正在佔用CPU的時期
2)就緒態:是一種可運行的狀態,由於其餘進程正在運行而暫時中止
3)阻塞態:等待外部事件的發生(典型的例子:它在等待可以使用的輸入)
1.4.2四種轉換(見圖)
1)運行態->阻塞態
2)運行態->就緒態
3)就緒態->運行態
4)阻塞態->就緒態:外部事件的一旦發生,阻塞態轉換到就緒態,若是此時CPU空閒,馬上發生轉換3,轉換到運行態
新進程都是由已有進程而建立:
UNIX:父進程使用fork建立子進程,內核會分配一個新的PCB給子進程,子進程是父進程的副本,子進程拷貝父進程的數據段、堆、棧,而正文段與父進程共享;可是fork以後經常跟隨exec調用讓子進程執行不一樣於父進程的任務,因此fork會使用「寫時複製」技術,開始的時候子父進程共享數據段、堆、棧和正文段,當任何一方想要更改某個區域時,內核再爲相應區域製做副本
WINDOWS:父進程使用CreateProcess建立子進程,子父進程從一開始就不共享任何區域。
操做系統維護一張進程表,每個進程佔用一個表項,表項包含這個進程狀態的各類信息。
1.7.1正常終止
1)從main返回
2)在任何地方調用exit、_exit、_Exit(包括在進程的線程裏調用)
3)進程中的最後一個線程從其啓動例程返回
4)最後一個線程調用pthread_exit函數
1.7.2異常終止
1)調用abort函數(此函數將發送SIGABRT信號給進程)
2)接收到一個信號
3)最後一個線程取消請求作出響應
1)線程是一種輕量級進程,更容易建立,也更容易銷燬
2)在線程模型中,「資源分配」與「調度」分離,線程是獨立調度的基本單位,進程是資源分配的基本單位
3)線程的優點(爲何要提出線程):提高了操做系統的併發性能,首先線程是輕量級進程,更容易建立,也更容易銷燬,其次,進程切換須要切換虛擬地址空間,而線程切換則不須要,減小了切換的性能損耗
1)一個進程能夠有多個線程,但至少有一個線程。
2)進程有獨立的地址空間,同一進程下的多線程共享地址空間,更確切的說是共享正文段、數據段和堆,不共享棧,各線程擁有本身的棧
3)線程是調度的基本單位,進程是資源分配的基本單位,同一進程下的多線程共享該進程資源。
4)通訊方面:進程間通訊須要管道、消息隊列、共享內存等手段,而同一進程下的多線程直接經過共享的數據段和堆來通訊
1)須要頻繁建立銷燬的優先使用線程,由於對進程來講建立和銷燬一個進程代價是很大的
2)線程的切換速度快,因此在須要大量計算,切換頻繁時用線程,還有耗時的操做使用線程可提升應用程序的響應
3)由於對CPU系統的效率使用上線程更佔優,因此可能要發展到多機分佈的用進程,多核分佈用線程;
4)須要更穩定安全時,適合選擇進程
與進程同樣,有三個狀態,四種轉換
進程的主線程使用thread_create建立線程,全部線程都是平等的
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t * attr, void *(*func)(void*), void* arg ); //若成功則返回0,不然返回錯誤編號
1.用戶級線程:不須要內核支持而在用戶程序中實現的線程,內核對線程包一無所知,內核中用進程表管理進程,進程中用線程表管理多線程
優勢:1.能夠在不支持線程的操做系統中實現 2.每一個進程可制訂本身的調度算法調度線程
缺點:1.發送系統阻塞時內核不知道多線程的存在於是阻塞進程從而阻塞全部線程 2.進程內部沒有時鐘中斷,因此不能輪轉調度線程
2.內核級線程:由內核建立和撤銷,內核用線程表來管理內核線程
優勢:1.一個線程阻塞,內核能夠運行同一進程內的另外一個線程
缺點:1.代價大:阻塞線程的調用都是系統調用、內核中建立和撤銷的開銷更大 2.信號是發給進程而不是線程,信號到達進程由哪個線程去處理?
1)線程從啓動例程中返回
2)線程被同一進程的其餘線程取消
void pthread_cancel(pthread_t pid); //若成功,返回0;不然,返回錯誤編號
某線程能夠經過調用pthread_cancel函數來請求取消另外一線程,僅僅是請求,另外一線程能夠忽略或者控制如何被取消
int pthread_setcancelstate(int state, int *oldstate); //成功則返回0,不然返回錯誤編號。
有兩個線程並無包含在pthread_attr_t結構中,他們是可取消狀態和可取消類型,這兩個屬性影響着線程在響應pthread_cancel函數調用時所呈現的行爲。可取消狀態屬性能夠是PTHREAD_CANCEL_ENABLE和PTHREAD_CANCEL_DISABLE,線程的默承認取消狀態是PTHREAD_CANCEL_ENABLE,可調用pthread_setcancelstate修改它的可取消狀態,當狀態設爲disable時,對pthread_cancel的調用並不會殺死線程,而是掛起這個取消請求(若是以後取消狀態再次變爲enable,則掛起的請求能夠被處理)
void pthread_testcancel(void);
推遲取消:調用pthread_cancel之後,在線程到達取消點以前,並不會出現真正的取消。當調用下表中列出的任何函數,取消點都會出現,或者調用pthread_testcancel設置取消點
int pthread_setcanceltype(int type, int *oldtype); //成功則返回0,不然返回錯誤編號。
經過調用pthread_setcanceltype來修改取消類型,取消類型能夠是PTHERAD_CANCEL_DEFERRED(延遲取消)或PTHREAD_CANCEL_ASYNCHRONOUS(異步取消),使用異步取消時,線程能夠在任什麼時候間取消,而不是非要等到遇到取消點才能被取消
3)線程調用pthread_exit函數
2.8.1共享:
1)正文段
2)數據段(data段、bss段)
3)堆
4)進程ID
5)每種信號的處理方式
6)文件描述符
2.8.2獨享:
1)棧
2)線程ID
3)線程的優先級(線程須要被調度)
4)信號屏蔽字(每一個線程所感興趣的信號不一樣,因此線程的信號屏蔽字由線程本身管理)
5)寄存器的值(線程間是併發運行的,每一個線程有本身不一樣的運行線索)
6)錯誤返回碼errno(同一個進程中有多個線程在同時運行,若是某個線程設置了errno值,當該線程還沒來得及處理這個錯誤,另一個線程也設置了errno值,這樣的話前一個線程的錯誤就沒有辦法被處理了。 因此,不一樣的線程應該擁有本身的錯誤返回碼變量)
1)一個線程要麼是結合的,要麼是分離的
2)一個結合的線程能夠被其餘線程回收資源和殺死
3)一個分離的線程不能被別的線程回收資源和殺死,等到這個線程終止後,系統自動釋放資源
4)默認狀況下,建立的線程是結合的(設置pthread_create函數的參數可建立分離的進程)
5)若是一個結合的線程終止後卻沒有被pthread_join,則它將成爲僵死線程(相似於僵死進程,還有一部分資源沒有被回收),因此建立線程者應該調⽤用pthread_join來等待線程運行結束,回收線程資源
6)調用pthread_join後,當等待線程沒有終止時,父線程將處於阻塞狀態;若是要避免阻塞,在子線程中調用pthread_detach(pthread_self())或者父線程中調用pthread_detach(thread_id),這會將子線程的狀態設置爲分離的,這樣一來該線程終止後系統會自動釋放資源,父線程就不用阻塞等待了
1)子進程繼承父進程的互斥量、讀寫鎖、條件變量
2)linux中,若是父進程包含多線程,fork的時候只複製調用fork的線程到子進程,其餘線程在子進程中不被複制
3)假設在fork以前,父進程的一個線程對某個鎖進行的lock,而後另一個線程調用了fork建立子進程。此時在子進程中持有那個鎖的線程卻不被複制,然而子進程又會繼承父進程的鎖,從子進程的角度來看,這個鎖被「永久」的上鎖了,由於它的持有者「蒸發」了。若是子進程再對這個鎖進行lock的話,就會發生死鎖。
4)解決:
1)進程切換分兩步:
2)對於linux來講,線程和進程的最大區別就在於地址空間,對於線程切換,第1步是不須要作的,第2是進程和線程切換都要作的
3)切換的性能消耗:
1)pthread_create()相似於fork(),用來建立線程
2)pthread_exit()相似於exit(),用來終止線程
3)pthread_self()相似於getpid(),獲取線程號
4)pthread_join()相似於waitpid(),用來處理終止的線程
1)是一種比線程更加輕量級的存在。正如一個進程能夠擁有多個線程同樣,一個線程能夠擁有多個協程;
2)協程不是被操做系統內核管理,而徹底是由程序所控制
3)協程的開銷遠遠小於線程
4)協程能保留上一次調用時的狀態,每次過程重入時,就至關於進入上一次調用的狀態:協程擁有本身寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方,在切換回來的時候,恢復先前保存的寄存器上下文和棧
5)每一個協程表示一個執行單元,有本身的本地數據,與其餘協程共享全局數據和其餘資源
6)協程極高的執行效率,和多線程相比,線程數量越多,協程的性能優點就越明顯;