第7章 第5節 操作系統

● 請你說一下多進程、多線程,操作系統層面的差別和聯繫

參考回答:

進程:進程是一個具有一定獨立功能的程序在一個數據集上的一次動態執行的過程,是操作系統進行資源分配和調度的一個獨立單位,是應用程序運行的載體。進程是一種抽象的概念,從來沒有統一的標準定義。進程一般由程序、數據集合和進程控制塊三部分組成。程序用於描述進程要完成的功能,是控制進程執行的指令集;數據集合是程序在執行時所需要的數據和工作區;程序控制塊(Program Control Block,簡稱PCB),包含進程的描述信息和控制信息,是進程存在的唯一標誌。

線程:在早期的操作系統中並沒有線程的概念,進程是能擁有資源和獨立運行的最小單位,也是程序執行的最小單位。任務調度採用的是時間片輪轉的搶佔式調度方式,而進程是任務調度的最小單位,每個進程有各自獨立的一塊內存,使得各個進程之間內存地址相互隔離。後來,隨着計算機的發展,對CPU的要求越來越高,進程之間的切換開銷較大,已經無法滿足越來越複雜的程序的要求了。於是就發明了線程,線程是程序執行中一個單一的順序控制流程,是程序執行流的最小單元,是處理器調度和分派的基本單位。一個進程可以有一個或多個線程,各個線程之間共享程序的內存空間(也就是所在進程的內存空間)。一個標準的線程由線程ID、當前指令指針(PC)、寄存器和堆棧組成。而進程由內存空間(代碼、數據、進程空間、打開的文件)和一個或多個線程組成。

差別:1.線程是程序執行的最小單位,而進程是操作系統分配資源的最小單位;2.一個進程由一個或多個線程組成,線程是一個進程中代碼的不同執行路線;3.進程之間相互獨立,但同一進程下的各個線程之間共享程序的內存空間(包括代碼段、數據集、堆等)及一些進程級的資源(如打開文件和信號),某進程內的線程在其它進程不可見;4.調度和切換:線程上下文切換比進程上下文切換要快得多。

聯繫:原則上一個CPU只能分配給一個進程,以便運行這個進程。通常使用的計算機中只有一個CPU,同時運行多個進程,就必須使用併發技術。通常採用時間片輪轉進程調度算法,在操作系統的管理下,所有正在運行的進程輪流使用CPU,每個進程允許佔用CPU的時間非常短(比如10毫秒),這樣用戶根本感覺不出來CPU是在輪流爲多個進程服務,就好象所有的進程都在不間斷地運行一樣。但實際上在任何一個時間內有且僅有一個進程佔有CPU。如果一臺計算機有多個CPU,情況就不同了,如果進程數小於CPU數,則不同的進程可以分配給不同的CPU來運行,這樣,多個進程就是真正同時運行的,這便是並行。但如果進程數大於CPU數,則仍然需要使用併發技術。在Windows中,進行CPU分配是以線程爲單位的,一個進程可能由多個線程組成。操作系統將CPU的時間片分配給多個線程,每個線程在操作系統指定的時間片內完成(注意,這裏的多個線程是分屬於不同進程的).操作系統不斷的從一個線程的執行切換到另一個線程的執行,如此往復,宏觀上看來,就好像是多個線程在一起執行.由於這多個線程分屬於不同的進程,就好像是多個進程在同時執行,這樣就實現了多任務。總線程數<=CPU數量時並行運行,總線程數>CPU數量時併發運行。並行運行的效率顯然高於併發運行,所以在多CPU的計算機中,多任務的效率比較高。但是,如果在多CPU計算機中只運行一個進程(線程),就不能發揮多CPU的優勢。

● 請你說一下線程通信的方法、線程的五種狀態

參考回答:

線程通信的方法:

①同步:多個線程通過synchronized關鍵字這種方式來實現線程間的通信。

②while輪詢的方式

③wait/notify機制

④管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream進行通信

線程的五種狀態:

1. 新建(NEW):新創建了一個線程對象。

2. 可運行(RUNNABLE):線程對象創建後,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。

3. 運行(RUNNING):可運行狀態(runnable)的線程獲得了cpu 時間片(timeslice) ,執行程序代碼。

4. 阻塞(BLOCKED):阻塞狀態是指線程因爲某種原因放棄了cpu 使用權,也即讓出了cpu timeslice,暫時停止運行。直到線程進入可運行(runnable)狀態,纔有機會再次獲得cpu timeslice 轉到運行(running)狀態。阻塞的情況分三種:

(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。

(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。

(三). 其他阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。

5. 死亡(DEAD):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。

● 請你說一下虛擬內存

參考回答:

虛擬內存是計算機系統內存管理的一種技術。它使得應用程序認爲它擁有連續的可用的內存(一個連續完整的地址空間),而實際上,它通常是被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數據交換

● 請你說一下線程的同步和互斥以及應用常見

參考回答:

互斥:指在某一時刻指允許一個進程運行其中的程序片,具有排他性和唯一性。

對於線程A和線程B來講,在同一時刻,只允許一個線程對臨界資源進行操作,即當A進入臨界區對資源操作時,B就必須等待;當A執行完,退出臨界區後,B才能對臨界資源進行操作。

同步:指的是在互斥的基礎上,實現進程之間的有序訪問。假設現有線程A和線程B,線程A需要往緩衝區寫數據,線程B需要從緩衝區讀數據,但他們之間存在一種制約關係,即當線程A寫的時候,B不能來拿數據;B在拿數據的時候A不能往緩衝區寫,也就是說,只有當A寫完數據(或B取走數據),B才能來讀數據(或A才能往裏寫數據)。這種關係就是一種線程的同步關係。

應用常見:多線程編程中,難免會遇到多個線程同時訪問臨界資源的問題,如果不對其加以保護,那麼結果肯定是不如預期的,因此需要線程同步與互斥。

● 請你說一下線程的五種狀態以及轉換

參考回答:

圖片

1、新生狀態

在程序中用構造方法(new操作符)創建一個新線程時,如new Thread(r),該線程就是創建狀態,此時它已經有了相應的內存空間和其它資源,但是還沒有開始執行。

2、就緒狀態

新建線程對象後,調用該線程的start()方法就可以啓動線程。當線程啓動時,線程進入就緒狀態(runnable)。由於還沒有分配CPU,線程將進入線程隊列排隊,等待 CPU 服務,這表明它已經具備了運行條件。當系統挑選一個等待執行的Thread對象後,它就會從等待執行狀態進入執行狀態。系統挑選的動作稱之爲“CPU調度"。一旦獲得CPU線程就進入運行狀態並自動調用自己的run方法。

3、運行狀態

當就緒狀態的線程被調用並獲得處理器資源時,線程就進入了運行狀態。此時,自動調用該線程對象的run()方法。run()方法定義了該線程的操作和功能。運行狀態中的線程執行自己的run方法中代碼。直到調用其他方法或者發生阻塞而終止。

4、阻塞狀態

一個正在執行的線程在某些特殊情況下,如被人爲掛起或需要執行耗時的輸入輸出操作時,suspend()、 wait()等方法,線程都將進入堵塞狀態。堵塞時,線程不能進入排隊隊列,只有當引起堵塞的原因被消除後,線程轉入就緒狀態。重將讓出 CPU 並暫時中止自己的執行,進入堵塞狀態。在可執行狀態下,如果調用 sleep()、 新到就緒隊列中排隊等待,這時被CPU調度選中後會從原來停止的位置開始繼續執行。

5、死亡狀態

線程調用stop()方法、destory()方法或 run()方法執行結束後,線程即處於死亡狀態。處於死亡狀態的線程不具有繼續運行的能力。

● 請你說一說消息隊列、信號量的實現方式

參考回答:

消息隊列是消息的鏈接表,存儲在內核中,由消息隊列ID來標識。每個隊列都有一個msgid_ds結構與其相關聯:

struct msgid_ds
{
struct ipc_perm msg_perm;
msgqnum_t msg_qnum; /* # of messages on queue */
msglen_t msg_qbytes; /* max # of bytes on queue */
pid_t msg_lspid; /* pid of last msgsnd() */
pid_t msg_lrpid; /* pid of last msgrcv() */
time_t msg_stime; /* last-msgsnd() time */
time_t msg_rtime; /* last-msgrcv() time */
time_t msg_ctime; /* last-change time */
...
};










此結構定義了隊列的當前狀態。msgget用於創建一個新隊列或打開一個現有隊列,msgsnd將消息添加到隊列的尾端(每個消息包括一個長整型類型字段,一個非負的長度,實際的數據長度),msgrcv用於從隊列中取消息(並不一定要以先進先出次序取消息,可以按消息的類型字段取消息)。


信號量是一個計數器,用於爲多個進程提供對共享對象的訪問。爲了正確地實現信號量,信號量的測試及加減1操作應當是原子操作,爲此,信號量通常是在內核中實現的。

常用的信號形式是二元信號量(binary semaphore)。它控制單個資源,其初始值爲1。但是,一般而言,信號量的初值也可以是任意一個正值,表明有多少個共享單位可供共享。

內核爲每個信號量集合維護着一個semid_ds結構:

struct semid_ds
{
struct ipc_perm sem_perm;
unsigned short sem_nsems; /* # of semaphores in set */
time_t sem_otime; /* last-semop() time */
time_t sem_ctime; /* last-change time */
...
};






每個信號量由一個無名結構體表示,至少包含下列成員:

複製代碼

1

2

3

4

5

6

7

8

struct

{

unsigned short semval; /* semaphore value, always >= 0 */

pid_t sempid; /* pid for last operation */

unsigned shortsemncnt; /* # processes awaiting semval > curval */

unsigned shortsemzcnt; /* # processes awaiting semval == 0 */

...

};


● 請你說一下進程和線程的區別

參考回答:

進程:是具有一定獨立功能的程序、它是系統進行資源分配和調度的一個獨立單位,重點在系統調度和單獨的單位,也就是說進程是可以獨立運行的一段程序。

線程:是進程的一個實體,是CPU調度和分派的基本單位,比進程更小的能獨立運行的基本單位,線程自己基本上不擁有系統資源,在運行時,只是暫用一些計數器、寄存器和棧 。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間。

一個程序至少有一個進程,一個進程至少有一個線程。

● 請你說一下死鎖的概念、原因、解決方法

參考回答:

1、死鎖是指在一組進程中的各個進程均佔有不會釋放的資源,但因互相申請被其他進程所站用不會釋放的資源而處於的一種永久等待狀態。死鎖的四個必要條件:

•    互斥條件(Mutual exclusion):資源不能被共享,只能由一個進程使用。

•    請求與保持條件(Hold and wait):已經得到資源的進程可以再次申請新的資源。

•    非剝奪條件(No pre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。

•    循環等待條件(Circular wait):系統中若干進程組成環路,該環路中每個進程都在等待相鄰進程正佔用的資源。

java中產生死鎖可能性的最根本原因是:1)是多個線程涉及到多個鎖,這些鎖存在着交叉,所以可能會導致了一個鎖依賴的閉環;2)默認的鎖申請操作是阻塞的。

如,線程在獲得一個鎖L1的情況下再去申請另外一個鎖L2,也就是鎖L1想要包含了鎖L2,在獲得了鎖L1,並且沒有釋放鎖L1的情況下,又去申請獲得鎖L2,這個是產生死鎖的最根本原因。

2、避免死鎖:

•    方案一:破壞死鎖的循環等待條件。

•    方法二:破壞死鎖的請求與保持條件,使用lock的特性,爲獲取鎖操作設置超時時間。這樣不會死鎖(至少不會無盡的死鎖)

•    方法三:設置一個條件遍歷與一個鎖關聯。該方法只用一把鎖,沒有chopstick類,將競爭從對筷子的爭奪轉換成了對狀態的判斷。僅當左右鄰座都沒有進餐時纔可以進餐。提升了併發度。

● 請你說一下多線程

參考回答:

最開始,線程只是用於分配單個處理器的處理時間的一種工具。但假如操作系統本身支持多個處理器,那麼每個線程都可分配給一個不同的處理器,真正進入“並行運算”狀態。從程序設計語言的角度看,多線程操作最有價值的特性之一就是程序員不必關心到底使用了多少個處理器。程序在邏輯意義上被分割爲數個線程;假如機器本身安裝了多個處理器,那麼程序會運行得更快,毋需作出任何特殊的調校。根據前面的論述,大家可能感覺線程處理非常簡單。但必須注意一個問題:共享資源!如果有多個線程同時運行,而且它們試圖訪問相同的資源,就會遇到一個問題。舉個例子來說,兩個線程不能將信息同時發送給一臺打印機。爲解決這個問題,對那些可共享的資源來說(比如打印機),它們在使用期間必須進入鎖定狀態。所以一個線程可將資源鎖定,在完成了它的任務後,再解開(釋放)這個鎖,使其他線程可以接着使用同樣的資源。

多線程是爲了同步完成多項任務,不是爲了提高運行效率,而是爲了提高資源使用效率來提高系統的效率。線程是在同一時間需要完成多項任務的時候實現的。

一個採用了多線程技術的應用程序可以更好地利用系統資源。其主要優勢在於充分利用了CPU的空閒時間片,可以用盡可能少的時間來對用戶的要求做出響應,使得進程的整體運行效率得到較大提高,同時增強了應用程序的靈活性。更爲重要的是,由於同一進程的所有線程是共享同一內存,所以不需要特殊的數據傳送機制,不需要建立共享存儲區或共享文件,從而使得不同任務之間的協調操作與運行、數據的交互、資源的分配等問題更加易於解決。

● 請你說一下線程之間通信的手段

參考回答:

使用全局變量

主要由於多個線程可能更改全局變量,因此全局變量最好聲明爲volatile

使用消息實現通信

在Windows程序設計中,每一個線程都可以擁有自己的消息隊列(UI線程默認自帶消息隊列和消息循環,工作線程需要手動實現消息循環),因此可以採用消息進行線程間通信sendMessage,postMessage。

使用事件CEvent類實現線程間通信

Event對象有兩種狀態:有信號和無信號,線程可以監視處於有信號狀態的事件,以便在適當的時候執行對事件的操作。

● 請你說一下死鎖

參考回答:

1、死鎖是指在一組進程中的各個進程均佔有不會釋放的資源,但因互相申請被其他進程所站用不會釋放的資源而處於的一種永久等待狀態。死鎖的四個必要條件:

互斥條件(Mutual exclusion):資源不能被共享,只能由一個進程使用。

請求與保持條件(Hold and wait):已經得到資源的進程可以再次申請新的資源。

非剝奪條件(No pre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。

循環等待條件(Circular wait):系統中若干進程組成環路,該環路中每個進程都在等待相鄰進程正佔用的資源。

java中產生死鎖可能性的最根本原因是:1)是多個線程涉及到多個鎖,這些鎖存在着交叉,所以可能會導致了一個鎖依賴的閉環;2)默認的鎖申請操作是阻塞的。

如,線程在獲得一個鎖L1的情況下再去申請另外一個鎖L2,也就是鎖L1想要包含了鎖L2,在獲得了鎖L1,並且沒有釋放鎖L1的情況下,又去申請獲得鎖L2,這個是產生死鎖的最根本原因。

2、避免死鎖:

•    方案一:破壞死鎖的循環等待條件。

•    方法二:破壞死鎖的請求與保持條件,使用lock的特性,爲獲取鎖操作設置超時時間。這樣不會死鎖(至少不會無盡的死鎖)

•    方法三:設置一個條件遍歷與一個鎖關聯。該方法只用一把鎖,沒有chopstick類,將競爭從對筷子的爭奪轉換成了對狀態的判斷。僅當左右鄰座都沒有進餐時纔可以進餐。提升了併發度。

● 請你回答一下進程同步的方法

參考回答:

1、臨界區(Critical Section):通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。

優點:保證在某一時刻只有一個線程能訪問數據的簡便辦法

缺點:雖然臨界區同步速度很快,但卻只能用來同步本進程內的線程,而不可用來同步多個進程中的線程。

2、互斥量(Mutex):爲協調共同對一個共享資源的單獨訪問而設計的。

互斥量跟臨界區很相似,比臨界區複雜,互斥對象只有一個,只有擁有互斥對象的線程才具有訪問資源的權限。

優點:使用互斥不僅僅能夠在同一應用程序不同線程中實現資源的安全共享,而且可以在不同應用程序的線程之間實現對資源的安全共享。

缺點:①互斥量是可以命名的,也就是說它可以跨越進程使用,所以創建互斥量需要的資源更多,所以如果只爲了在進程內部是用的話使用臨界區會帶來速度上的優勢並能夠減少資源佔用量。因爲互斥量是跨進程的互斥量一旦被創建,就可以通過名字打開它。

②通過互斥量可以指定資源被獨佔的方式使用,但如果有下面一種情況通過互斥量就無法處理,比如現在一位用戶購買了一份三個併發訪問許可的數據庫系統,可以根據用戶購買的訪問許可數量來決定有多少個線程/進程能同時進行數據庫操作,這時候如果利用互斥量就沒有辦法完成這個要求,信號量對象可以說是一種資源計數器。

3、信號量(Semaphore):爲控制一個具有有限數量用戶資源而設計。它允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數目。互斥量是信號量的一種特殊情況,當信號量的最大資源數=1就是互斥量了。

優點:適用於對Socket(套接字)程序中線程的同步。(例如,網絡上的HTTP服務器要對同一時間內訪問同一頁面的用戶數加以限制,只有不大於設定的最大用戶數目的線程能夠進行訪問,而其他的訪問企圖則被掛起,只有在有用戶退出對此頁面的訪問後纔有可能進入。)

缺點:①信號量機制必須有公共內存,不能用於分佈式操作系統,這是它最大的弱點;

②信號量機制功能強大,但使用時對信號量的操作分散, 而且難以控制,讀寫和維護都很困難,加重了程序員的編碼負擔;

③核心操作P-V分散在各用戶程序的代碼中,不易控制和管理,一旦錯誤,後果嚴重,且不易發現和糾正。

4、事件(Event): 用來通知線程有一些事件已發生,從而啓動後繼任務的開始。

優點:事件對象通過通知操作的方式來保持線程的同步,並且可以實現不同進程中的線程同步操作。

● 請問進程線程的區別,進程間怎麼相互通信,什麼是多線程,什麼是併發

參考回答:

進程和線程的區別有以下幾點

1、進程是資源分配的最小單位,線程是程序執行的最小單位

2、進程有自己獨立地址空間,每啓動一個進程,系統就會爲它分配地址空間,建立數據表來維護代碼段,堆棧段,數據段,而線程是共享進程中的數據的,使用相同的地址空間,但是CPU切換一個線程的花費遠比進程要小,

3、線程之間通信方式更方便,同一進程下的線程共享全局變量等數據,而進程之間的通信方式需要以通信的方式進行,

4、多線程程序中只要有一個線程死掉了,整個進程也死掉了,而一個進程死掉了,並不會對另一個進程造成影響,因爲進程有自己獨立的地址空間

進程間的通信方式:

1、無名管道通信,數據只能單向流動,只能在具有親緣的進程間使用

2、高級管道通信,將領一個程序當作一個新的進程在當前程序中啓動,則他算是當前進程的子進程

3、有名管道通信,允許無親緣關係進程間的通信

4、消息隊列通信,消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識,消息隊列克服了信息傳遞信息少等缺點

5、信號量通信,信號量用於控制多個進程對共享資源的訪問

6、信號通信,用於通知接受進程某個事件已經發生

7、共享內存通信,共享內存映射一段能被其他進程所訪問的內存,往往與其他通信機制配合使用,來實現進程間的同步和通信

8、套接字通信,他用於不同機器之間的進程通信

什麼是多線程

多線程就是指一個進程中同時有多個執行路徑正在執行

併發指在操作系統中,一個時間段中有幾個程序都已處於已啓動運行到運行完畢之間,且這幾個程序都是在同一個處理機上面,但任意時刻點上只有一個程序在處理機上運行。

圖片


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章