天天看點

南京大學《作業系統》筆記(二)

并行:允許多個執行流同時執行(要求多個處理器);

并發:多個執行流可以不按照一個特定的順序執行。

南京大學《作業系統》筆記(二)

并發性的來源:程序會調用作業系統的API,作業系統在并發執行兩個執行流的時候,要保證它們互不幹擾,是以,對處理器和記憶體提出了要求。

處理器

記憶體

典型的并發系統

并發/并行

單處理器

共享記憶體

OS核心/多線程程式

并發不并行

多處理器

并發、并行

不共享記憶體

分布式系統(消息通信)

多線程之間,寄存器組的值、堆棧(包括堆棧中的局部變量)是互相獨立的;代碼是共享的。

在C++中,可以使用pthread_create和pthread_join得到共享代碼、共享資料、獨立堆棧的多個線程。(will新開一個部落格寫)

Q1:每個線程的堆棧範圍和大小?

A1:每個線程有8MB的記憶體映射區域和前後各4KB的保護頁。

Q2:每個線程有8M的堆棧,那麼為什麼1000個線程沒有耗盡記憶體?

A2:因為雖然每個線程有8MB的記憶體區域,但是它們是虛拟記憶體,并且在沒有被使用的時候不會映射到實體記憶體上(Lazy allocate),是以實體記憶體的占用并沒有那麼多。

編譯器優化->順序的喪失:指令的執行不保證按照代碼書寫的順序發生;

中斷/并行->原子性的喪失:代碼的原子性随時被破壞;

亂序執行->可見性的喪失:執行過的指令可能在多處理器之間不可見。

從狀态機的理論去了解程式,程式的狀态(記憶體/寄存器的快照)可以看成有限狀态機的節點,程式的運作可以看成狀态轉換的過程。假如系統上有16MB的記憶體,即16*8Mb,那麼可以有2^(16*8M)種狀态。

大部分狀态有唯一的後續狀态,執行一條指令可以得到确定的結果。不确定的指令可能有多個後續狀态,例如執行rdrand指令(生成真随機數),會對rax指令賦予不同的值,是以可以帶給狀态機不确定性。

對于程式來說,不确定性的來源可能是:

(時間)rdtsc/rdtscp:擷取處理器的“時間戳”用于精确定時;

(機器狀态)rdrand:處理器自身提供的“真”随機數指令;

(系統調用)syscall:應用程式的最大不确定性來源,包括使用者的鍵入、磁盤檔案的讀取等。

在計算機硬體上的應用:高性能處理器實作,當狀态機的執行具有确定性的時候,A->B->C的狀态轉換可以優化為A->C。

在計算機系統上的應用:程式分析技術。靜态分析是根據程式代碼推導出狀态機的性質;動态分析是運作時觀測到狀态機的執行。

程式的執行是随着時間“前進”的(\(s_1\)->\(s_2\)->\(s_3\)->...),能否在時間上“後退”(Time-travel)?記錄所有的狀态\(s_i\),就能實作time-traveling。

如果記錄下所有的狀态,就可以回到任意一個狀态去檢視當時的資訊。但難點在于,上文讨論過,狀态的數量非常龐大,這樣的記錄非常耗費空間。已知每條指令隻會對狀态中很少一部分的寄存器/記憶體進行修改。

由此一個好主意是隻記錄初始狀态和每條指令前後狀态的diff。

gdb提供了這樣的time-travel debugging功能:

target record-full 開始記錄(需要程式開始執行);

record stop 結束記錄;

reverse-step/reverse-stepi “時間旅行調試”。

系統中有n個線程,線程之間是共享記憶體的,則并發程式的狀态s = (M, R1, R2, R3, ..., Rn)

并發系統執行指令的順序是不确定的,每個(M, R1), (M, R2), (M, R3), ...都可以看成是一個串行程式,在任意一個狀态,可以選擇任意一個線程執行一條指令。

即使每個線程都是确定的,在任意一個時刻,處理器可以選擇任意線程執行,此處帶有不确定性,是以即使沒有上述的程式三大不确定性來源,多線程程式也擁有天生的不确定性。

假如有n個線程,每個線程都執行m個線上程和時間上都獨一無二的操作,那麼從初始狀态到結束狀态(程式運作結束)是O(n^(mn)),這是一個非常大的數值。

狀态機是了解程式執行的重要工具。

程式是指令序列的靜态描述,高度概括、精簡,行為有時難以了解;

狀态機是所有動态行為的集合,将靜态時的分支、循環全部展開為了順序結構,行為明确、容易了解。

C++11 新标準中引入了五個頭檔案來支援多線程程式設計,它們分别是 <code>&lt;atomic&gt;, &lt;thread&gt;, &lt;mutex&gt;, &lt;condition_variable&gt;</code> 和 <code>&lt;future&gt;</code>。

<code>&lt;atomic&gt;</code>:該頭文主要聲明了兩個類, <code>std::atomic</code> 和 <code>std::atomic_flag</code>,另外還聲明了一套 C 風格的原子類型和與 C 相容的原子操作的函數。

<code>&lt;thread&gt;</code>:該頭檔案主要聲明了 <code>std::thread</code> 類,另外 <code>std::this_thread</code> 命名空間也在該頭檔案中。

<code>&lt;mutex&gt;</code>:該頭檔案主要聲明了與互斥量(Mutex)相關的類,包括 <code>std::mutex_*</code> 一系列類,<code>std::lock_guard</code>, <code>std::unique_lock</code>, 以及其他的類型和函數。

<code>&lt;condition_variable&gt;</code>:該頭檔案主要聲明了與條件變量相關的類,包括 <code>std::condition_variable</code> 和 <code>std::condition_variable_any</code>。

<code>&lt;future&gt;</code>:該頭檔案主要聲明了 <code>std::promise</code>, <code>std::package_task</code> 兩個 Provider 類,以及 <code>std::future</code> 和 <code>std::shared_future</code> 兩個 Future 類,另外還有一些與之相關的類型和函數,<code>std::async()</code> 函數就聲明在此頭檔案中。

使用示例:(編譯時需要加上-std=c++11 -pthread)

&lt;thread&gt;頭檔案的摘要如下,聲明了std::thraed線程類和std::swap輔助函數,以及命名空間std::this_thread。

std::thread類如下。