1
輸入大於 0
的無號整數,計算
同第三周測驗 7
的作法,最後在回傳值 +1
處理 ceil
。
最開頭 x--
目的是處理當 x
為 2 的冪時,取 ceil
不應 +1
。
其公式如下:
此算法在
x = 1
時會輸出 1
,為錯誤答案。
根據第三周測驗 7
的作法,EXP1
必須包含尚未完成的 r | shift
以及剩下最後 2 bits 的判斷與處理,而最後 2 bits 的判斷可以用 x >> 1
來實做,因此
EXP1
= r | shift | x << 1
TODO
2
找到從低位開始的第一個 1
,並回傳其索引值。
其作法也是利用 binary search 找到 1
的位置。
首先,shift
為 BITS_PER_LONG
除以 2,接著將 t
右移 shift
位,使 t
成為高位半邊全為 0
的 unsigned long
。
然後是 while
迴圈的部份,目的是要找到 1
位於高位還是低位,所以利用 x
與 t
作 AND 運算,為 0
代表在低位沒有 1
出現,將 x
右移 shift
位,並計算 o += shift
紀錄位置。
因此:
EXP2
= x & t
EXP3
= o += shift
TODO
3
從結構體 struct foo
中刪除特定的 struct foo_consumer
。
用一個 pointer to pointer con
走訪 foo->consumers
的所有 foo_consumers
,因此每次 con
應該都要址向下一個 foo_consumers
位置,所以:
EXP4
= con = &(*con)->next
當找到目標 fc
後,con
要指向 fc
下一個 foo_consumers
,所以:
EXP5
= fc->next
。TODO
4
首先,宣告一個 *registered_task[]
陣列,包含 task0
與 task1
。
tasks
是一個指標的指標,指向 registered_task
。
ntasks
是負責紀錄 registered_task
中 task
的數量。
接著,執行 schedule()
task
的結構包含以個 list 以及一個 jmp_buf
。
先 setjump
紀錄當下的 jump_buf
使後續初始化還可以跳回 schedule()
。
while loop 的地方負責隨機呼叫 tasks
中的 function pointer 並進行初始化,接著tasklist
會紀錄每一個 task,然後呼叫 task_join()
執行 tasklist
中的 task。
task0
和 task1
的內容結構式是相同的,會先進行 setjump
,然後把 jump_buf
加入 tasklist
中,初始化完成後再跳回 schedule
,所以:
EXP8
= longjmp(sched, 1)
後續藉由 task_join
切回來時,由於 longjmp
的參數設為 1
,所以 setjmp
的回傳值就會都是 1
,因此會接續執行後面的 for loop。
在 for loop 中,會先 setjmp
,並執行 task_switch
切換到下一個 task,當再一次跳回來時,會印出 resume。直到執行 arg
次,跳出迴圈,會印出 complete。最後再 longjmp
到 schedule
。
task_switch
與 task_join
的內容是一樣的,只是 task_join
是在 schedule
被呼叫;而 task_switch
是在 task0
、task1
被呼叫。
這兩個涵式的目的是取出 tasklist
的第一個 entry 的 jump_buf
,並跳到其對應的位置執行。
tasklist
是否為空jump_buf
longjmp
到 env
對應的位置因此:
EXP6
= list_del(&t->list)
EXP7
= list_del(&t->list)
task_add
負責將 env
加入到 tasklist
的尾端。
TODO
5
巨集會判斷變數的寬度,針對寬度為 1, 2, 4, 8 位元組的變數,將其轉換為對應的純量 (scalar) 型態並增加 volatile 關鍵字,而對於其他資料寬度的類型,改呼叫 memcpy 來避免編譯器對該變數進行最佳化。
巨集 __READ_ONCE_SIZE
利用 switch 配別不同 size
,並根據 size
轉換對應的純量型態並加上 volatile
關鍵字。
巨集 READ_ONCE
宣告一個 uion __u
,而 uion 所佔的記憶體大小是根據佔據的最大空間成員的大小來定,因此 __c
必須為佔據空間最小的型態,所以:
DECL0
= char __c[1]
。TODO