---
# System prepended metadata

title: 2021 年暑期「Linux 核心」課程先決測驗題 回答
tags: [linux2021-summer]

---

---
tags: linux2021-summer
---

# 2021 年暑期「Linux 核心」課程先決測驗題 回答
> [先決測驗](https://hackmd.io/@sysprog/linux2021-summer-quiz)
> 
## alpha
AAA = v >> (bits - c)
BBB= v << (bits - c)

我智商一開始只想到這樣的做法，後來看分析文沒想到有 UDB 要顧

## beta
MMM = (sz + mask) & (~mask)

## gamma
NNN = 12

## delta
AAA = queue->last->next = node
BBB = node
CCC = queue->first = new_header

## epsiloon
XXX = x

## eta
### 1. 探討 gamma 中迴圈 fork 並 print 至數量 49152 個 -

總共會產生 $2^{12} - 1 = 4095$ 個 child process
本身 parent 也是一個 process ，因此共有 4096 個 process 會進行
而原本應該每個 process 輸出都應該從 i 開始遞減輸出。
但是 buffered io 在遇到 fflush 或 newline 時才會被輸出至螢幕上。並參考以下 fork 之說明，會共享 parent 的 file descriptor 。因此每個 process 裏面都含有 i 個 "-" 於 buffer 中。

因此共會印出 4096 process * 12 = 49152 個

[man 2 fork](https://man7.org/linux/man-pages/man2/fork.2.html)


> The child inherits copies of the parent's set of open file
>           descriptors.  Each file descriptor in the child refers to the
>           same open file description (see open(2)) as the corresponding
>           file descriptor in the parent.  This means that the two file
>           descriptors share open file status flags, file offset, and
>           signal-driven I/O attributes (see the description of F_SETOWN
>           and F_SETSIG in fcntl(2)).

### 2. Beta 中 Bitwise 進行記憶體位置向上對齊

題目中的數字，是希望用來做記憶體位置的表示。CPU 取資料不會是一個 byte 一個 byte 抓，而是以 word 為單位。如果將資料擺放位置都剛好符合 CPU 一次抓取的大小，拿到資料後就不用額外花時間喬正資料。

因此假設記憶體位置對齊 aligment 位，表示位置的數值需剛好為 aligment 的倍數。

如果剛好就是 aligment 的倍數，則表示本身就已經對齊，無需對該數值操作。

以題目中的例子，希望對齊 4 ，因此 120 ~ 123 中， 120 剛好為 4 的倍數，表示已經對其無需改動，121~123 按題目需求向上對齊至下一個倍數。


作答如下，如果剛好 alignment 為 2 的冪，可以用簡單的 bitwise 特性，直接用 bitwise 運算取值
```cpp
#include <stdint.h>

static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
    uintptr_t mask = alignment - 1;
    if ((alignment & mask) == 0) {  /* power of two? */
        return (sz + mask) & (~mask);       
    }
    return (((sz + mask) / alignment) * alignment);
}
```

以下分為 2 的冪和非 2 的冪進行討論

#### alignment 為 2 的冪

如果剛好為 2 的冪，一個對齊的位置其右半邊皆剛好是 bit 0。
舉例如下

* align_up(4, 4) => 0b0100
* align_up(8, 4) => 0b1000

多少個最右邊 bit 需要變成零，剛好是最大還不會二位元進位的 bit，例如 aligiment 為 4 = 0b0100 ，右邊兩個 bit 只要剛好是 0，皆是對齊的數字。

因此 aligment - 1 可以拿來作為 mask，其特性剛好是右邊位數皆為 1。

以對齊 4 為例，alignment - 1 = 3 = 0b0011

核心想法為:

1. 想辦法辨識右邊非 0 為未對齊
2. 右半邊最終都必須剛好是 0

##### 後半部 右半邊清零

上述作法單獨將 `& (~mask)` 拿出來看。bitwise and 的特性是

* 任何數字 and 1 = 原數字
* 任何數字 and 0 = 0

剛好 mask 最右邊的數字皆為 1 ，只要全部翻轉再做 & 運算就能保留右半邊。

##### 前半部 利用進位特性加法

除了右半邊清 0 之外，還必須辨別是剛好對齊的數字還是非對齊的數字。

剛好所有非對齊的數字 + mask 皆能造成二位元進位。只有對齊的數字 00 不會進位，最終被後半部清零

eg.

* 4 (0b0100) + 3 (mask 0b0011) = 7 (0b0111)
* 6 (0b0101) + 3 (mask 0b0011) = 9 (0b1001)

:::warning
TODO: 列出其他可能的實作
:::

其他思路，以核心想法找出非對齊的作法，只要快速 !!(sz & mask) 便能得知是否非 0。只要再 + mask 也能觸發進位和不進位

#### alignment 非 2 的冪

```
(((sz + mask) / alignment) * alignment)
```
上述則是改為利用 int 的除法為捨去小數點之後的整數，加上 mask 則是為了讓數值向上對齊，而非相下。

eg.

##### align_up(9, 7)

1. 9 + mask = 15
2. 15 / 7 = 2
3. 2 * 7 = 14

若改為向下對齊則不用加 mask

##### align_down(9, 7)

1. 9 / 7 = 1
2. 1 * 7 = 7


### linux 用於 sign_up 的做法

linux 的紅黑樹的 parent pointer 同時也用做顏色的判斷，
其方法為沒對齊多出來的部分作為紅黑判斷。因此每次取得 parent 的位置時都透過 macro，將位置對齊

```
#define rb_parent(r)   ((struct rb_node *)((r)->__rb_parent_color & ~3))
```

參考如下
[rbtree.h](https://github.com/torvalds/linux/blob/master/include/linux/rbtree.h#L35)
