linux2022
目的: 檢驗學員對 記憶體管理、對齊及硬體特性, 指標, bitwise, 前置處理器 的認知
作答表單:
1
延伸第 3 週測驗題的測驗 7
,已知輸入必為大於 0
的數值 (即 x > 0
),以下程式碼可計算 ,也就是 ceil 和 log2 的組合並轉型為整數:
請補完,使其行為符合預期。作答規範:
EXP1
應以最簡潔的形式撰寫,且符合作業一排版規範 (近似 Linux 核心程式碼排版風格)EXP1
為表示式,限制使用 ^
, &
, |
, <<
, >
> 這幾個運算子,可能會出現常數EXP1
不該包含小括號 (即 (
和 )
)r
, shift
, x
EXP1
不可包含 ,
和 ;
這樣的分隔符號 (separator)延伸問題:
x = 0
的狀況,並仍是 branchless2
複習〈你所不知道的 C 語言: bitwise 操作〉,改寫第 3 週測驗題的測驗 11
裡頭的 fls 函式 (fls 意謂 "find last set"),使其得以計算 Find first set:
請補完,使其行為符合預期。作答規範:
EXP2
和 EXP3
應以最簡潔的形式撰寫,且符合作業一排版規範 (近似 Linux 核心程式碼排版風格)EXP2
和 EXP3
限制使用 ^, &, |, <<=, >>=, +=, -= 這幾個運算子,可能會出現常數EXP2
和 EXP3
不該包含小括號 (即 (
和 )
)x
, o
, t
, shift
,也就是說,你應該寫 x ^ t
而非 t ^ x
EXP2
和 EXP3
不可包含 ,
和 ;
這樣的分隔符號 (separator)延伸問題:
3
考慮以下改寫自 Linux 核心的程式碼:
請補完,使 consumer_del
行為符合註解規範。作答規範:
EXP4
和 EXP5
應以最簡潔的形式撰寫,且符合作業一排版規範 (近似 Linux 核心程式碼排版風格)EXP4
和 EXP5
都包含指標操作 (如 ->
)延伸問題:
foo
和 foo_consumer
的好處4
以下嘗試用 lab0 提及的 setjmp 和 longjmp,用以實作〈你所不知道的 C 語言: goto 和流程控制篇〉闡述的 coroutine,參考的程式執行輸出如下:
原始程式碼如下: (檔名 jmp.c
)
請補完,使行為符合註解規範。作答規範:
EXP6
, EXP7
, EXP8
, EXP9
應以最簡潔的形式撰寫,且符合作業一排版規範 (近似 Linux 核心程式碼排版風格)EXP6
和 EXP7
都包含 List API 的使用EXP8
和 EXP9
應有函式呼叫延伸問題:
5
〈你所不知道的 C 語言:前置處理器應用篇〉已提過若干 Linux 核心的巨集,不難發現這些巨集經歷多次演變。Linux 核心一度有個巨集 ACCESS_ONCE
,其作用是確實地讀取所指定記憶體位址的內容值,且限這一次,其原始定義如下:
注意
volatile
關鍵字的使用
ACCESS_ONCE
巨集的使用情境,可參照 Linux v4.0 的 kernel/locking/mutex.c
:
然而,如果不使用 ACCESS_ONCE
巨集,程式碼如下:
由於,編譯器偵測到第 8 行的 owner = lock->owner
在迴圈中沒有被更改,所以其最佳化機制可能將第 8 行的程式碼搬出 while 迴圈之外,如此不用每次都實際地讀取 lock->owner
,其程式碼變成:
但問題來了,lock->owner
有可能被其它核心執行緒修改,從而造成資料不一致。因此使用 ACCESS_ONCE
巨集可以防止編譯器做相關最佳化工作,並確保每次都能到實體記憶體位址讀取。其做法便是將某參數暫時性地轉換成具備 volatile
的型態。如此,存取該參數在非引入 ACESS_ONCE
巨集之處 (不具 volatile
特性),仍可享用編譯器最佳化的好處。
在 ACCESS_ONCE() and compiler bugs 則提及 Linux 核心捨棄上述 ACCESS_ONCE
巨集,改為 READ_ONCE
巨集。在 Linux 核心原始程式碼曾存在以下註解:
ACCESS_ONCE
will only work on scalar types. For union types, ACCESS_ONCE on a union member will work as long as the size of the member matches the size of the union and the size is smaller than word size.The major use cases of
ACCESS_ONCE
used to be
- Mediating communication between process-level code and irq/NMI handlers, all running on the same CPU, and
- Ensuring that the compiler does not fold, spindle, or otherwise mutilate accesses that either do not require ordering or that interact with an explicit memory barrier or atomic instruction that provides the required ordering.
以下是可能的實作:
READ_ONCE
巨集會判斷變數的寬度,針對寬度為 1, 2, 4, 8 位元組的變數,將其轉換為對應的純量 (scalar) 型態並增加 volatile 關鍵字,而對於其他資料寬度的類型,改呼叫 memcpy
來避免編譯器對該變數進行最佳化。
請補完程式碼,使其運作符合預期。書寫規範:
DECL0
為變數宣告,不該存在 ;
和 ,
分隔字元延伸問題:
READ_ONCE
/ WRITE_ONCE
巨集的實作和其演化 (使用 git log
觀察)