contributed by < r34796Kevin >
完整程式碼
實作Jserv老師的Linux 核心設計 (Linux Kernel Internals)作業2,並且盡量完成作業的額外要求
首先來看head(queue_t)
與node(list_ele_t)
的結構
list_ele_t
queue_t
其實在list_ele_t與queue_t中已經存在list_head可以指向下一個節點,故我們可以做以下的精簡,也可以形成一個雙向鍊結串列。
list_ele_t
queue_t
queue_t直接使用list_head作為鏈結串列的開頭。
在測試程式碼中我們使用cities.txt
取自 dict/cities.txt,內含世界超過 9 萬個都市的名稱,會產生93827個節點。
修改之前
valgrind –tool=memcheck –track-origins=yes ./test
5737 Memcheck, a memory error detector
5737 Copyright © 2002-2017, and GNU GPL'd, by Julian Seward et al.
5737 Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
5737 Command: ./test
5737
5737
5737 HEAP SUMMARY:
5737 in use at exit: 0 bytes in 0 blocks
5737 total heap usage: 187,659 allocs, 187,659 frees, 5,280,126 bytes allocated
5737
5737 All heap blocks were freed – no leaks are possible
5737
5737 For lists of detected and suppressed errors, rerun with: -s
5737 ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
修改之後
7255 Memcheck, a memory error detector
7255 Copyright © 2002-2017, and GNU GPL'd, by Julian Seward et al.
7255 Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
7255 Command: ./lab2
7255
7255
7255 HEAP SUMMARY:
7255 in use at exit: 0 bytes in 0 blocks
7255 total heap usage: 187,659 allocs, 187,659 frees, 4,529,486 bytes allocated
7255
7255 All heap blocks were freed – no leaks are possible
7255
7255 For lists of detected and suppressed errors, rerun with: -s
7255 ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
因為每個節點的結構都精簡了,所以我們的記憶體空間從5,280,126 bytes下降為4,529,486 bytes。
原始的程式碼請參考測驗一,
因為鍊結串列的結構改變了所以以下程式碼也要改寫。
首先來看struct list_head *q_new()
,配置一個list_head *q,用INIT_LIST_HEAD(q);
把*prev跟*next都指向自己。
bool q_insert_head(struct list_head *q, char *s)
傳入鍊結串列的開頭*q,配置一個節點*newh儲存char *s並用list_add_tail(&newh->list, q);
把newh加到鍊結串列的最後。
(函式的名稱是否改為q_insert_tail較為恰當??)
用圖來解釋list_add_tail(struct list_head *node, struct list_head *head)
list_head *get_middle(struct list_head *list)
傳入一個環形鏈結串列的開頭*list,回傳在正中間的節點slow。
因為每次fast走兩步slow走一步,當fast走出鍊結串列時(下一步為節點開頭list或下兩步為節點開頭list),slow會在鍊結串列的正中間。
list_cut_position(&left, q, get_middle(q));
把開頭到中間的節點分給left,剩下的節點分給q。
遞迴呼叫list_merge_sort
直到鍊結串列的每個節點都被分開,再使用list_merge(&left, q, &sorted);
將節點排序好分給sorted。
用圖來解釋list_cut_position
假設原本q後面有四個節點
呼叫list_cut_position(&left, q, get_middle(q));
後鍊結串列將被分為兩個鍊結串列
list_merge(list_head *lhs,list_head *rhs,list_head *head)
可以將兩個鍊結串列lhs跟rhs排序後,接到新的head上
以下是實現的細節
char *lv跟char *rv
永遠指向各自鍊結串列的第一個節點,
list_head *tmp = strcmp(lv, rv) <= 0 ? lhs->next : rhs->next;
用strcmp比大小後,把tmp指向value小的節點上。
list_del(tmp);
實際上是把指定的節點移出鍊結串列中(但該節點仍存在記憶體中)
所以呼叫list_del(tmp);
後會有以下結果
再用
list_add_tail(tmp, head);
把tmp接到head後
執行完第一次迴圈後會有以下結果
跑完while迴圈後就可以得到一個排序過得鍊結串列。
遍歷鍊結串列中的每個節點並釋放記憶體。