爲了幫助你們理解什麼是進程,以廚師作蛋糕爲例。廚師作蛋糕,首先須要廚師(CPU),其次,須要食譜(程序)和原料(輸入數據),而用原料作蛋糕的一些列動做的總和就是進程。某天廚師正在後廚作着蛋糕,突來聽到兒子哭着跑進後廚,說本身被蜜蜂蟄了 ,廚師放下手中工具,並記錄下當前作到哪一步了(保存上下文信息) ,而後拿出急救手冊,按其中的說明爲兒子進行處理(開始另一個進程)。前端
咱們知道文件是對I/O設備的抽象,虛擬存儲器是對文件和主存的抽象,指令集是對CPU的抽象,進程是對指令集和虛擬存儲器的抽象。以下圖所示 。java
從上可知,進程包括指令集和虛擬存儲器。咱們着重介紹進程在虛擬存儲器中的邏輯佈局,它包括用戶棧、堆、程序數據和程序代碼,其中,用戶棧從上往下生長,堆從下往上生長,程序數據和程序代碼從可執行文件加載而來,將程序代碼改寫成彙編指令就是相似於movl、imul、addl等指令
。以下圖所示後端
此時,CPU運行到地址爲304的指令, 假設CPU時間片恰好用完,就須要進行進程切換,在進行進程切換以前,須要保護現場,即保存寄存器信息、PC、打開的文件, 代碼段地址、數據地址、堆棧信息等,這些信息稱爲進程的上下文。當操做系統切換到進程時,首先將進程2的上下文信息加載到操做系統中,找到PC,而後接着執行就能夠了。緩存
進程的上下文信息是以某個數據結構保存在內存中的,而這種數據結構就是PCB。在Linux操做系統中PCB對應的數據結構就是task_struct,它保存着進程的重要信息。bash
struct task_struct{ pid_t pid://進程號 long state;//狀態 cputime_t utime,stime;//cpu在用戶態和 核心態下執行的時間 struct files_struct *files;//打開的文件 struct mm_struct *mm;//進程使用的內存 ... }
像上文描述的那樣,CPU調度就是到底哪一個進程佔有CPU,它能夠分爲非搶佔式和搶佔式。非搶佔式是指調度程序一旦把CPU分配給某一進程後便讓它一直運行下去,直到進程完成或發生某件事件而不能運行時,纔將CPU分配給其餘進程。它適合批處理系統,簡單、系統開銷小。搶佔式是指當一個進程正在執行時,系統能夠基於某種策略剝奪CPU給其餘進程。剝奪的原則有優先權原則、端進程優先原則、時間片原則,它適用於交互式系統。微信
咱們知道,打印機有一個緩存,叫作打印隊列,以下圖所示,打印隊列有5個空格,就是說這個打印隊列最多能夠容納5個待打印文件,打印機進程就是消費者,而其餘待打印進程是生產者,生產者不斷地向隊列中放數據,例如:A.java、B.doc等。數據結構
臨界區:多個進程須要互斥的訪問共享資源,共享資源能夠是變量、表和文件等,例如打印隊列就是共享資源。多線程
當生產者將隊列放滿時,須要等待消費者;若是消費者把全部文件都打印完了,則須要等待生產者,這就是進程間的同步問題。函數
關閉中斷
缺點:把中斷操做(CPU收到時鐘中斷之後,會檢查當前進程的時間片是否用完,用完則切換)開放給應用程序,這是極其危險的事情,例如:當某個程序關閉中斷以後,執行完畢以後,忘記打開中斷,致使整個系統都終止了。工具
用硬件指令來實現鎖
boolean TestAndSet(boolean *lock){ boolean rv = *lock; *lock = TRUE; return rv; } // 使用TestAndSet boolean lock = false; do{ while(TestAndSet(&lock)){ ...//什麼也不作 } 臨界區 lock = false; 剩餘區 }while(true);
wait(S){ while(S<=0){ ...//啥也不作 } S--; } signal(S){ S++; } // semaphore mutext = 1; wait(mutex); 進入臨界區 signal(mutex); 剩餘區
用硬件指令實現鎖的方案和信號量方案都有忙等問題,即某個進程得到了CPU時間片,可是啥事幹不了,while(S < = 0){...}
typedef struct{ int value; struct process *list; } semaphore; wait(semaphore *s){ s -> value--; if(s->value<0){ //把當前進程加入到s->list中 block(); } signal(semaphore *s){ s -> value++; if(s -> value <=0){ //從s->list取出一個進程p wakeup(p); } }
因爲進程之間是相互獨立的,因此進程間數據共享只能經過內核實現,開銷很麻煩,所以咱們提出了線程這個概念。線程之間的數據是共享的;一個進程能夠只有一個線程,也能夠有多個線程(一個進程至少有一個線程);當一個進程有多個線程時,每一個線程都有一套獨立的寄存器和堆棧信息,而代碼、數據和文件是共享的,以下圖所示。
徹底在用戶層實現(當用戶要執行硬件設備,必須從用戶空間到內核空間,這是一種保護措施,保護操做系統不被惡意程序所破壞),線程在應用層實現有一個優勢就是線程切換不用內核介入,線程切換會很是的快。也就是說線程的調度策略是本身實現的。可是這裏也有一個巨大的缺陷:因爲內核只知道進程而不知道線程,那麼進程1中的任何一個線程被阻塞,致使進程1中的其餘線程也被阻塞
內核實現線程和用戶空間一一對應,能夠有效的解決方案一中的缺點,可是因爲在內核中實現用戶空間相同數量的線程數,開銷比較大
用戶空間中多個線程映射到內核中的一個線程,這樣一來,內核中的線程就不用建立那麼多, 並且阻塞的機率也下降了,這是一種平衡和折中的方式。JVM就是實現了這種方式 。JVM自己就是一個進程,JVM能夠建立不少線程,而後對應內核中的線程,內核中的線程調度CPU。
歡迎關注微信公衆號:木可大大,全部文章都將同步在公衆號上。