# 2025q1 Homework5 (assessment)
contributed by < `tsaiiuo` >
## 因為自動飲料機而延畢的那一年
看完整個文章後擁有的情緒是非常複雜的,首先第一個最直觀的體驗就是,我從來沒有對一件事情擁有如同作者的熱情,我從小到大做的事情都是理性導向,也可以說是做最符合社會期待的事情,無論是就讀的科系又或做的抉擇,雖說是理性導向,假若真的是理性導向我應該此時此刻就已經在科技大廠工作或對開源程式碼有著重大貢獻,但我啥都沒做到,相反的比較像且戰且走,什麼事情都是做到有就好,並沒有做到完美過。
這篇文章的作者頂著交大資工的學歷,花了半年以上去做一件看似會沒有回報的事情,而每個章節的過程都遇到許許多多的困難,應該是說全是困難,但作者雖說有想過要放棄,中間老師也有去跟作者本人交談,並給予鼓勵,並且我認為老師講的這句話十分有意義
>「你該學習的不是看到事情要完蛋了就去避免失敗,而是應該學習如何處理與承受失敗,你才能變得比以前更強大。」
最終作者還是一一克服了中間遇到的種種困難,就算可能解決的方式跟它當初預先設想的有落差(冰塊機),但他最終還是成功做出一個自動飲料機,就算功能可能是最基本的,但也無坊,就像作者說的
>但我經手了這台機器的每一個細節、我知道每一個零件存在的理由、每一個設計背後做出的取捨,我比任何人都清楚這台機器還有多少問題,茶垢可能會卡在接頭的縫隙、冰塊的定量機構絕對還能再改進、管線過脆容易折斷……我可以列出上百個問題,機器現在能做到的每一件事,都是用一次次試誤換來的取捨。
這中間無論是培養解決問題的能力,成就感,與朋友協作的風景,我想不是什麼在業界實習能學到的,就算飲料機本身後續就放在那邊,帶給作者的經驗、培養的能力是不會消失的,更不用說還有滿滿的感動,以我看來這是一個可以驕傲一輩子的事情
最後自我反思,就是我不應該要用符合社會期待的價值觀去約束自己,比如好像該去實習就去實習,碩士該兩年畢業就兩年畢業,能早點工作就早點工作,而是應該想著對自己認真一次,做自己喜歡做的事情,而不是被社會期待綁架,好像別人做了什麼,我就該跟上。除此之外,我認為我的且戰且走的心態也該改進,不該事情遇到了困難就半途而廢,然後催眠自己已經比很多人好了,嘗試著把一件事情做到好,總之這篇文章真得算受益良多,而且也比我想像中的還要令人感動(因為遇到的困難真的太多了),中間從冰塊分割機開始我每看一集,都認為假若換我是作者,我應該就半途而廢了。
# Linux問題
## 問題一
The scheduler performs these task switches under two circumstances. One is
the expiration of a timeslice. The other is when a new task enters the runqueue,
provided that the currently running task has not just recently started running.
One of the advantages of positioning runnable tasks on a timeline of virtual
runtimes is that it naturally prevents waking tasks from starving other tasks
that have remained runnable, as was possible with earlier Linux schedulers. As
time marches on, tasks that wake up get inserted into the timeline at later
and later virtual runtimes. A runnable task that has been patiently waiting
for the CPU, on the other hand, retains a fixed virtual runtime. As such, it
will eventually have the lowest virtual runtime, and hence will be chosen to run
(once a task switch occurs).
<s>
因此,對於僅短暫離開就緒佇列的任務,CFS 允許它們追回應有的執行時間;但若任務非可執行狀態超過某個閾值,當它甦醒時,排程器會將它的 vruntime 提前設定到「僅比目前最小 vruntime 稍微小一點」的位置。這樣做的目的並非嚴格維持比例分享,而是兼顧系統的回應性與整體吞吐量。
排程器在兩種情況下會觸發任務切換:一是當前任務的時間片用盡,二是有新任務進入就緒佇列(只要當前任務並非剛剛才開始執行)。
將可執行任務置於虛擬執行時間的時間軸上,有一個重要優勢:甦醒的任務不會「飢餓」那些已持續等待的任務。隨著時間推進,甦醒任務會被插入到不斷增大的 vruntime 之後;而那些耐心等待的任務,其 vruntime 保持不動,最終必然成為最小,並在下一次切換時被選中執行。
</s>
## 問題二
RCU list 函式與顧及 memory ordering 的影響
跟撰寫核心空間其他程式碼一樣,需要確保順序的正確性,為了防止編譯器最佳化後順序不同,用到 READ_ONCE、 WRITE_ONCE 等函式,而 Linux 核心原始程式碼有好心的提供對應的 API
對於賦予值提供了 rcu_assign_pointer
```c
#define rcu_assign_pointer(p, v) \
do { \
uintptr_t _r_a_p__v = (uintptr_t)(v); \
rcu_check_sparse(p, __rcu); \
\
if (__builtin_constant_p(v) && (_r_a_p__v) == (uintptr_t)NULL) \
WRITE_ONCE((p), (typeof(p))(_r_a_p__v)); \
else \
smp_store_release(&p, RCU_INITIALIZER((typeof(p))_r_a_p__v)); \
} while (0)
```
可以看到這個巨集確實是透過 WRITE_ONCE 去確保寫入的正確性,但這可以發現它是在 NULL 時才呼叫 WRITE_ONCE ,把 p 直接設為 NULL 後,任何 rcu_dereference(p) 的讀者都會得到 NULL,並不會進一步讀取 p->next 或其它成員,因此不需要保證先寫入資料得順序性。反之則是呼叫 smp_store_release
而這裡的觀念就是除了編譯器最佳化外,硬體本身在多核也有順序不同的問題,因此也需要對應的 swp 等函式去做處理,這裡的 smp_store_release 原始程式碼有很多種架構,這裡提出其中一種 linux/include/asm-generic/barrier.h
```c
#define smp_store_release(p, v) \
do { \
barrier(); \
WRITE_ONCE(*p, v); \
} while (0)
```
也就是 barrier 與 WRITE_ONCE 為基礎,保證「在此 store(釋放點)之前的所有 memory 操作(loads/stores)已完成且對其他 CPU 可見」,但也不阻止後續 loads/stores 與前面 loads 的 reorder
同理針對讀取值提供了 rcu_dereference
CONFIG_SMP