作業系統思考 第二章 程式

飛龍發表於2019-05-11

第二章 程式

作者:Allen B. Downey

原文:Chapter 2 Processes

譯者:飛龍

協議:CC BY-NC-SA 4.0

2.1 抽象和虛擬化

在我們談論程式之前,我打算先定義幾個東西:

  • 抽象(Abstraction):抽象是複雜事物的簡單表示。例如,如果你開車的話,應該知道車輪向左轉的時候車也會向左行駛,反之亦然。當然,方向盤由一系列機械和傳動系統所連線,用於使輪子轉向,並且輪子和路面的相互作用方式也很複雜。但是作為一個司機,你通常不需要考慮這些細節。你可以僅僅建立方向盤的心智模型,這種心智模型就是一個抽象。

    軟體工程的很大一部分就是設計類似這樣的抽象,允許使用者和其它程式設計師使用強大而複雜的系統,而不必知道其實現的細節。
    
  • 虛擬化(Virtualization):一類非常重要的抽象就是虛擬化,它是建立可取的幻像的過程。例如,許多公共圖書館都參與了館際合作,允許它們互相借閱圖書。當我需要一本書時,有時它在我的本地圖書館的架子上,但更多情況下它會被運到其它的館藏中。無論是哪一種,我都會收到它可借閱的提醒。我並不需要知道它來自哪裡,我也不需要知道我的圖書館擁有哪一本書。一般來說,這個系統建立了一個幻象,好像我的圖書館擁有全世界的每一本書。

    在物理上,我的圖書館的館藏可能很小,但是虛擬上我能獲得的館藏包含了館際合作的每一本書。
    
    另外一個例子,大多數電腦都只連線到一個網路中,而這個網路又連結到其它網路,等等。我們所談論的“網際網路”,是一系列網路和協議的合集,它將資料包從一個網路傳送到另一個網路。從使用者和程式設計師的角度來看,整個系統的行為就像是網際網路的每臺計算機都互相連線。物理連線的數量十分少,但是虛擬連線的數量十分龐大。
    

“虛擬”這個詞通常用於虛擬機器的語境中,它是一種軟體,可以建立執行特定系統的專用計算機的幻象。實際上,虛擬機器可能和其它虛擬機器一起執行在不同的作業系統上。

在虛擬化的語境中,我們通常把真實發生的事情叫做“物理的”,而把虛擬上發生的事情叫做“邏輯的”或者“抽象的”。

2.2 隔離

工程最重要的原則之一就是隔離(Isolation):當你設計一個帶有多個元件的系統時,將它彼此隔離是個很好的方法,這樣某個元件中的改變就不會對其它元件造成不良影響。

作業系統最重要的目標之一,就是將每個程式和其它程式隔離,使程式設計師不必考慮每個可能的互動情況。提供這種隔離的軟體物件叫做程式(Process)。

程式是表示執行中程式的軟體物件。我按照物件導向程式設計把它稱之為“軟體物件”。工程一個物件包含資料,並且提供用於運算元據的方法。程式正是包含以下資料的物件:

  • 程式文字,通常是機器語言的指令序列。

  • 程式相關的資料,包括靜態資料(編譯時分配)和動態資料,後者包括執行時的棧和堆。

  • 任何等待中的IO狀態。例如,如果程式正在等待從磁碟中讀取的資料,或者從網路到達的資料包,這些操作的狀態也是程式的一部分。

  • 程式的硬體狀態,這包括儲存在暫存器中的資料,狀態資訊,以及程式計數器,它表示當前執行了哪個指令。

通常一個程式執行一個程式,但是對於程式來說,載入並執行新的程式也是可能的。

也可以在多於一個程式中執行相同的程式,這非常常見。這種情況下,各個程式共享程式文字,但是擁有不同的資料和硬體狀態。

大多數作業系統提供了隔離程式的基本功能:

  • 多工:大多數作業系統有能力在幾乎任何時候中斷一個程式,儲存它的硬體狀態,並且在以後恢復它。通常,程式設計師不需要考慮這些中斷。程式的行為就像在一個專用的處理器上持續執行,除了兩條指令之間的時間是不可預測的。

  • 虛擬記憶體:大多數作業系統會建立幻象,每個程式看似擁有獨立記憶體片並且孤立於其他程式。同樣,程式設計師通常也不需要考慮虛擬記憶體如何工作,他們可以當做每個程式都擁有專用的記憶體片來處理。

  • 裝置抽象:執行於同一臺計算機的程式共享磁碟、網路介面、顯示卡和其它硬體。如果程式直接和這些硬體互動而不加協調,就一定會產生混亂。例如,一個程式預期的網路資料可能會被另一個程式讀取。或者多個程式可能嘗試在磁碟的相同位置儲存資料。作業系統負責通過提供合適的抽象來維持秩序。

作為程式設計師,你不需要知道太多關於這些功能如何實現的事情。但是如果你很好奇,你可以在這個遮蔽層的後面發現一大堆有趣的事情。而且,如果你知道其中所發生的事情,你會成為更好的程式設計師。

2.3 Unix 程式

當我寫這本書的時候,我最關注的程式就是我的文字編輯器,Emacs。偶爾我也會切換到終端視窗,它是一個執行Unix shell並提供命令列介面的視窗。

當我移動滑鼠時,視窗的管理器會被喚醒,看到滑鼠在終端視窗上方,並且喚醒終端。終端又喚醒shell。如果我在shell中鍵入make,它就會建立一個新的程式來執行Make。Make會建立另一個程式來執行LaTeX,之後另一個程式會顯示結果。

如果我需要查詢一些東西,我會切換到另一個桌面,這會再次喚醒視窗管理器。如果我點選Web瀏覽器的圖示,視窗管理器會建立進行來執行Web瀏覽器。許多瀏覽器,類似Chrome,會為每個視窗和每個選項卡建立新的程式。

並且這些只是我所瞭解的程式,同時還有許多其它程式“在後臺”執行。它們中許多都在執行作業系統相關的工作。

Unix命令ps能列印出執行中程式的資訊。如果你在終端裡執行它,可能會看到這些:

  PID TTY          TIME CMD
 2687 pts/1    00:00:00 bash
 2801 pts/1    00:01:24 emacs
24762 pts/1    00:00:00 ps

第一列是唯一的程式ID。第二列是建立程式的終端,“TTY”代表“電傳打字機”(Teletypewriter),它是原始的機械終端。

第三行是用於該程式的處理器時間總計,依次為時、分、秒。最後一行是所執行程式的名稱。這個例子中,bash是shell的名稱,用於解釋我鍵入到終端中的命令。Emacs是我的文字編輯器,而ps是生成這份輸出的程式。

通常,ps只會列出有關當前終端的程式。如果你使用-e選項,你會得到所有程式(也包括屬於其他使用者的程式,我認為這是個安全缺陷)。

在我的系統上有233個程式,下面是它們的一部分:

  PID TTY          TIME CMD
    1 ?        00:00:17 init
    2 ?        00:00:00 kthreadd
    3 ?        00:00:02 ksoftirqd/0
    4 ?        00:00:00 kworker/0:0
    8 ?        00:00:00 migration/0
    9 ?        00:00:00 rcu_bh
   10 ?        00:00:16 rcu_sched
   47 ?        00:00:00 cpuset
   48 ?        00:00:00 khelper
   49 ?        00:00:00 kdevtmpfs
   50 ?        00:00:00 netns
   51 ?        00:00:00 bdi-default
   52 ?        00:00:00 kintegrityd
   53 ?        00:00:00 kblockd
   54 ?        00:00:00 ata_sff
   55 ?        00:00:00 khubd
   56 ?        00:00:00 md
   57 ?        00:00:00 devfreq_wq

init是作業系統啟動時首先建立的程式。它又會建立許多其它程式,之後會閒置,直到它建立的程式執行完畢。

kthreadd是作業系統用於建立新的“執行緒”的程式。之後我們將會談論更多關於執行緒的東西,但是你暫時你可以認為執行緒是一種程式。

相關文章