---
# System prepended metadata

title: 'Operating System #6_1'
tags: [note]

---

# Operating System #6_1

## Race Condition

* 如果你學過計算機組織，應該知道<b>count++</b>實際上實作為：
    1. R1 = count
    2. R1 = R1 + 1
    3. count = R1
* 而<b>count--</b>則是：
    1. R2 = count
    2. R2 = R2 - 1
    3. count = R2
---
- 想想看，今天若有兩個Processes P1和P2，分別對同一個共享資料做了++和--，你想像中可能過程是這樣：(假設count初始化為5)
    1. `R1 = count` ... R1 = 5, count = 5
    2. `R1 = R1 + 1` ... R1 = 6, count = 5
    3. `count = R1` ... R1 = 6, count = 6
    4. `R2 = count` ... R2 = 6, count = 6
    5. `R2 = R2 - 1` ... R2 = 5, count = 6
    6. `count = R2` ... R2 = 5, count = 5
- 看起來似乎沒有問題。然而實際上卻可能因為中斷的關係，變成以下這樣：
    1. `R1 = count` ... R1 = 5, count = 5
    2. `R1 = R1 + 1` ... R1 = 6, count = 5
    3. `R2 = count` ... R2 = 5, count = 5 (中斷切至P2)
    4. `count = R1` ... R1 = 6, count = 6
    5. `R2 = R2 - 1` ... R2 = 4, count = 6
    6. `count = R2` ... R2 = 4, count = 4
- 這樣的情形造成了資料不同步的錯誤，我們稱這樣的狀況為**Race Condition**，因為兩個Processes就像是在賽跑一樣，途中有可能會彼此超前。

## Critical Section Protocol

* 為了解決這樣的問題，同步化(Synchronization)是必須的。Critical Section (CS) 是一種解決方法。
* CS的特性是，在眾多Processes中如果他們都擁有CS，那麼同一時間只能有一個Process在CS裡面。
    * 注意，在執行CS的Process還是可能被中斷，CS是利用同一時間只有一個Process在使用共享資料來避免不同步的問題。
* 如果一段Code要滿足CS，必須擁有以下特性：
    * Mutual Exclusion
    * Progress
    * Bounded Waiting

### Mutual Exclusion

* 如果已經有個Process在CS內了，不能有其他Process在CS裡面。
* 如果要你證明：先假設Pi和Pj都可以進入CS，再找出矛盾推翻。

### Progress

* 如果現在沒有Process在CS內，而且有一些Processes想要進去CS內，則他們必須在**有限時間內**決定誰要進去。
    * 除了想進去的Processes能夠決定誰要進去之外，其他的Processes是無法去影響他們的決策的。
    * 隱含著不會有、不能有DeadLock的要素。
* 如果要你證明：
    1. 如果沒有Process在CS裡面，要進去的一定進得去。
    2. 多個要進入，有限時間內進得去。

### Bounded Waiting

* 對於想進入CS的Process來說，必須要在**有限時間內**成功進入CS。
    * 不能有Process永遠進不去。
    * 如果有N個Processes想進入CS，則每個Process最多等待n-1次即可進入。
    * 隱含著不會有starvation的要素。
* 如果要你證明：
    * 找出上限次數。

## Peterson’s solution

```C++=1
do {
    flag[i] = TRUE;
    turn = j;
    while (flag[j] && turn == j);
    //critical section
    flag[i] = FALSE;
    //remainder section
} while (1);
```

* `flag[i]`: 代表Pi對進入CS有興趣。
* `turn`: 代表現在是誰的回合。

1. 先表示自己有興趣進入CS。
2. 將回合給對方(對方優先進入)。
3. 如果不是自己的回合，使用`while()`卡住自己。
4. 結束時將自己變成沒有興趣。

## Memory Barriers

* 有時CPU執行程式時，為了增進效率，可能會在不影響結果的前提下偷換程式碼的執行順序。
* 然而這樣可能會在多個Processes共享資源時產生不同步。
* Memory barrier指強制系統按照順序來執行。

## Compare and Swap

* Compare and Swap被定義為：
```C++=1
int compare_and_swap (int *value, int expected, int new_value){
    int temp = *value;
    if (*value == expected)
        *value = new_value;
    return temp;
}
```
* 看起來很簡單，不過他擁有一個特性，這個Function受到硬體支援，因此內部執行時**絕對不會被中斷**。
* 使用這個Function就能用幫助我們完成一些CS。
* 範例：
```C++=1
while (true) {
    waiting[i] = true;
    key = 1;
    while (waiting[i] && key == 1)
        key = compare_and_swap(&lock,0,1);
    waiting[i] = false;
    /* critical section */
    j = (i + 1) % n;
    while ((j != i) && !waiting[j])
        j = (j + 1) % n;
    if (j == i)
        lock = 0;
    else
        waiting[j] = false;
    /* remainder section */
}
```

## Bakery Algorithm

```C++=1
do {
    Choosing[i] = TRUE;
    Number[i]= Max(Number[0],…Number[n-1])+1;
    Choosing[i] = FALSE;
    for(j=0; j<n; j++){
        while(Choosing[j]) ;
        while((Number[j]!=0)&&((Number[j],j)<(Number[i],i)));
    }
    // critical section
    Number[i]=0
    // remainder section
} while (TRUE);
```

* `Choosing[0, …, n-1]`: 代表每個processes抽號碼牌了沒。
* `Number[0, …, n-1]`: 代表每個processes抽到的號碼。
---
- 藉由`Choosing[0, …, n-1]`確保每個人都抽號碼牌了才進去比較階段。
    - 如果沒有`Choosing[0, …, n-1]`，可能會在比較時才有process抽號碼，導致同時有兩個一起進去CS，造成問題。
- 不保證號碼不會重複，當號碼重複時用pid來比較。
    - 號碼小、pid小的優先執行。

## Atomic Variables

* Atomic Variables指該變數是原子等級的，即最小單位。當它進行`++`或`--`等運算時，不會在中途被中斷。
* 當你的共享資料只做簡單的運算，就可以直接使用Atomic Variables來避免不同步的問題了。

## Busy Waiting

* 上述幾種避免資料不同的方法，可以發現都是使用`while()`來卡住process，避免同時有多個processes進入CS。
* 然而這樣的方法其實CPU依然花費資源在無謂的迴圈中等待，這樣的等待方法我們稱作**Busy Waiting**。
* 很顯然的，我們需要更好的方法來解決同步問題。

## Semaphore

* Semaphore是一種不必Busy Waiting的同步工具。
* 它的使用方法大致上長這樣：
```C++=1
Semaphore mutex; // initialized to 1
do {
    wait (mutex);
    // critical Section
    signal (mutex);
    // remainder section
} while (TRUE);
```
* 如果process前面有`wait()`就必須等候其他process執行`signal()`來喚醒它。
* 內部的實作是遇到`wait()`時將該process搬到waiting queue中等待，`signal()`才將它搬回ready queue；如此一來就可以在排程時不選擇到明明就不會做事的process。

- ![](https://i.imgur.com/SIQuxZU.png)
- output is 1 2 1 2 1 2。

###### tags: `note`