or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Syncing
xxxxxxxxxx
Lab0
lib/list_sort.c
研讀筆記list_sort.c
src標頭研讀
// SPDX-License-Identifier: GPL-2.0
SPDX(Software Package Data Exchange)是一種標準化的方式,用來標示軟體的授權許可資訊。GPL-2.0
則代表採用 GNU 通用公共授權(GNU General Public License)第 2 版 進行授權。這是一種開源許可證,允許使用者自由地使用、修改和分發代碼,但前提是必須遵守 GPL-2.0 的條款,例如分發修改後的代碼時也必須保持開源並提供原始碼。這行註解清楚地告訴開發者這段代碼的法律授權狀態,避免了冗長的版權聲明文字。#include <linux/compiler.h>
: 它確保代碼在不同環境下都能正確編譯和運行#include <linux/export.h>
: 此定義了用於導出符號的巨集,例如EXPORT_SYMBOL
。在 Linux 核心中,有些函數或變數需要被其他核心模組(module)使用,這時就需要透過這些巨集將它們標記為可導出,如果有函數需要被其他模組調用,就會依賴這個頭檔案提供的功能概述
list_sort
用於對linux kernel中的doubly linekd list進行排序。排序部分採用的是穩定的 merge sort algorithm,並支援自定義比較邏輯。為什麼使用合併排序?
實現
實作方面
list_sort
仰賴兩個輔助函式 :merge
和merge_final
輔函式1
merge
此註解為針對
merge
函式的作用 :此函式回傳的中間格式架構有以下三特點 :
作用 : 合併兩個已排序的子列表 a 和 b,生成一個單向鏈表(以 NULL 結尾)
src :
此是GCC編譯器提供的一個擴展屬性,用於告訴編譯器某些函數參數不應該為 NULL。具體來說,這裡的 (2,3,4) 表示函數的第 2、第 3 和第 4 個參數必須是非空的ptr
引數 :
void *priv
: 為一私有泛型資料指標,目的是讓使用者能設置比較邏輯cmp
的對象list_cmp_func_t cmp
: 是一個自訂的比較函數,用來決定兩個串列節點 a 和 b 的相對順序list_cmp_func_t
定義在<linux/list.h>
中struct list_head *a
: a 是第一個sorted的linked list headstruct list_head *b
: b 是第二個sorted的linked list head邏輯 :
用
*head
作為新的list之head,再用**tail
根據 a、b list中element內容比較去選擇將該 element 串入新的list(若相等則取a保證stable)。若 a、b 誰先取完,就將另一個list串到新list後面輔函式2
merge_final
作用:最終合併兩個子列表,並恢復雙向鏈表的
prev
指針,形成循環結構。src :
邏輯 :
前面部分與
merge
相同,新的部分為多維護一個count
與後面使用 do-while 迴圈處理剩餘節點,同時監控迴圈次數(count
)以避免過長執行。最後閉合形成循環unlikely()
是一個巨集(macro),作為編譯器的提示,告訴編譯器這個條件(!++count)
不太可能發生,優化程式執行效率。count
為 u8 ,值為255時再執行++count
後,會溢出,count
變為 0cmp(priv, b, b)
為一 no-op ,通常包含一些副作用(side effect),觸發某些系統檢查點。這些副作用給予系統一個機會,讓操作系統的調度器(scheduler)介入,檢查是否需要切換到其他任務。主函式
list_sort
:src :
註解研讀
@priv
:私有資料,對 list_sort 來說是不透明的指標,會直接傳遞給比較函數 @cmp。這允許 @cmp 在比較元素時使用額外的上下文資料。@head
:要排序的linked list的頭部,指標指向串列的起始節點。@cmp
:自訂的比較函數,由使用者提供,用來定義兩個節點之間的順序關係。描述比較函數 @cmp 的行為
大於 0:表示 @a 應排在 @b 後面。例如,若要升序排序,則 @a > @b 時返回大於 0。
小於等於 0:表示 @a 應排在 @b 前面,或兩者相等時保持原始順序。
list_sort
是一個穩定排序(stable sort),即相等元素的相對順序不會改變。@cmp 總是以輸入串列中較早出現的元素作為 @a,因此無需明確區分 @a < @b 和 @a == @b 的情況,只需返回 <= 0 即可。描述@cmp所需的數學屬性要求
cmp(@a, @b) 和 cmp(@b, @a) 的返回值符號必須相反。例如,若 cmp(@a, @b) > 0,則 cmp(@b, @a) < 0。這避免了邏輯矛盾(如 @a > @b 且 @b > @a)。
若 cmp(@a, @b) <= 0 且 cmp(@b, @c) <= 0,則 cmp(@a, @c) <= 0 必須成立。這確保排序結果的一致性和正確性。
@cmp的兩種style
返回 < 0(@a < @b)、= 0(@a == @b)、> 0(@a > @b),類似標準 C 的
strcmp
。返回 0(@a <= @b)或 1(@a > @b)。
這種方式更簡潔,可能節省少量 CPU 週期。例如,
block/blk-mq.c
中的plug_ctx_cmp
就採用此@cmp 多欄位比較範例
假設節點有 high、middle、low 三個欄位,依序比較。
先比較 high,若不同,根據 high 的值決定順序;若相同,再比較 middle,最後比較 low。這種方式實現了字典序(lexicographical order),常用於多條件排序。
說明此list_sort的eager體現在條件允許時盡早合併子串列,以免最後很長一條merge一小條不balanced的case。且實施的準則為
2:1 balanced merges
,當有兩個大小為 2^k 的子串列,且後續有 2^k 個元素時,將這兩個子串列合併成一個大小為 2^(k+1) 的串列,旨在先merge小。只要快取能容納 32^k 個元素,就能避免cache thrashing,相較於fully-eager bottom-up
mergesort,這種方法減少了約 0.2n 次比較。
描述
count
的角色為表示待處理串列中的元素總數,用來決定何時進行合併。且描述了運作機制 :位元操作:
每次 count 增加時,設置某個位元(bit k),並清除低於 k 的位元(k-1 至 0)。
合併觸發:
當 count 達到 2^k 的奇數倍(除了第一次達到 2^k),觸發兩個大小為 2^k 的串列合併,生成一個 2^(k+1) 的串列。
描述
count
機制的merge時機 : 當count
為 2^k 的奇數倍時,表示有 2^k 個元素在較小的待處理串列中,此時可以安全合併兩個 2^k 大小的串列。後續合併時,生成兩個 2^(k+1) 的串列,接著它們會被合併成 2^(k+2),確保待處理串列數量不超過兩個。描述pending list的狀態,大小為 2^k 的待處理串列數量取決於:
count
的第 k 位狀態。count
>= 2^(k+1))。描述6種states
x
表示任意位元,y
表示非零高位。0:
00x
- 無 2^k 大小的串列,x 個小於 2^k 的串列。1:
01x
- 無 2^k ,2^(k-1) + x 個小於 2^k。2:
x10x
- 無 2^k ,2^k + x 個小於 2^k。3:
x11x
- 1 個 2^k ,2^(k-1) + x 個小於 2^k。4:
y00x
- 1 個 2^k ,2^k + x 個小於 2^k。5:
y01x
- 2 個 2^k, 2^(k-1) + x 個小於 2^k,觸發合併後回到狀態 2。生成 : 2->3 和 4->5:當 bit k-1 為 1 且高位非零時,生成 2^k 大小的串列(state 5)。
合併 : 5->2:合併兩個 2^k 的串列,生成 2^(k+1),並回到state 2
最終合併:處理完所有輸入後,將所有待處理串列從小到大合併,完成排序。
總結 :
list_sort
透過count
控制合併時機,實現 2:1 balanced merge。它優化了cache使用(需容納 32^k 元素)和比較次數(減少 0.2n 次),特別適合資料能放入 L1 快取的情況。使用者可透過 @cmp 和 @priv 靈活定義排序邏輯函式流程
*pending
(待合併子列表)為 NULL,count
為 0。count
達到某個 2^k 的奇數倍時,觸發大小為 2^k 的子列表合併)count
的二進位表示,找到最低位的 0(記為 bits)。pending
的prev
指針前進bits
步,定位需要合併的子列表。bits
!= 0,取出pending
中的兩個子列表 a 和 b,使用 merge 合併,並更新pending
。list
中取出一個元素,作為大小為 1 的子列表,加入pending
。count
加 1。pending
開始,逐對合併子列表,使用 mergelist_sort
優點特性