Try   HackMD

2025q1 Homework1 (lab0)

contributed by < gnkuan0712 >

作業書寫規範:

  • 無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利)
  • 文字訊息 (尤其是程式執行結果) 請避免用圖片來表示,否則不好搜尋和分類
  • 共筆書寫請考慮到日後協作,避免過多的個人色彩,用詞儘量中性
  • 不要在筆記內加入 [TOC] : 筆記左上方已有 Table of Contents (TOC) 功能,不需要畫蛇添足
  • 不要變更預設的 CSS 也不要加入任何佈景主題: 這是「開發紀錄」,用於評分和接受同儕的檢閱
  • 在筆記中貼入程式碼時,避免非必要的行號,也就是該手動將 c=cpp= 變更為 ccpp。行號只在後續討論明確需要行號時,才要出現,否則維持精簡的展現。可留意「你所不知道的 C 語言: linked list 和非連續記憶體」裡頭程式碼展現的方式
  • HackMD 不是讓你張貼完整程式碼的地方,GitHub 才是!因此你在開發紀錄只該列出關鍵程式碼 (善用 diff 標示),可附上對應 GitHub commit 的超連結,列出程式碼是為了「檢討」和「便於他人參與討論」
  • 留意科技詞彙的使用,請參見「資訊科技詞彙翻譯」及「詞彙對照表
  • 不要濫用 :::info, :::success, :::warning 等標示,儘量用清晰的文字書寫。:::danger 則僅限授課教師作為批注使用
  • 避免過多的中英文混用,已有明確翻譯詞彙者,例如「鏈結串列」(linked list) 和「佇列」(queue),就使用該中文詞彙,英文則留給變數名稱、人名,或者缺乏通用翻譯詞彙的場景
  • 在中文敘述中,使用全形標點符號,例如該用「,」,而非 ","。注意書名號的使用,即 ,非「小於」和「大於」符號
  • 避免使用不必要的 emoji 字元

開發環境

題目:2025 年 Linux 核心設計課程作業 —— lab0 (A)

前置作業

Ubuntu環境設定

有關在寫作業前的環境設定寫在這裡: Link

我注意到的細節

  • Programming style
    使用工具來調整作業程式要求的風格 $ clang-format -i *.[ch]

  • 用四個空格不要用縮排 Tab

  • 函式的左大括號應該放在行首( if else 那些就放在同行即可)

    ​​​​int function(int x)
    ​​​​{
    ​​​​    body of function
    ​​​​}
    
  • 正確的寫法們, 反正就是重要的字後面在空格就好

    ​​​​s = sizeof(struct file);
    ​​​​unsigned long long memparse(char *ptr, char **retptr);
    
  • Commit 基本規範

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

每次要push前的步驟

  1. sudo git add queue.c 放要更改的檔案名稱
  2. sudo git config global user.name "Carol Chen"
  3. sudo git config global user.email gnkuan0712@gmail.com
  4. sudo clang-format -i queue.c (都用這個先檢查排版?)
  5. sudo git commit -a 用這個 command 直接進到類似 vim 可以修改 commit 的空間(?
    這裡也可以善用 GPT 檢查 commit 寫的是否合規,最後按 ctrl + O 儲存 ctrl + x 離開,然後就會新增了
    ​​​​Improve q_size function to handle null input
    
    ​​​​Update the q_size function so that it first checks for a null head
    ​​​​pointer and returns 0 when no list is provided.
    

執行評分

  1. sudo make
  2. sudo make test
  3. 重整: sudo make clean

Valgrind + 自動測試程式

$ valgrind --tool=<toolname> <program>
toolname 選擇: memcheck

  • man strdup

安裝步驟

$ sudo apt install libc6-dbg

參考前一屆資料

檔案理解

  • .clang-format: 確保團隊 code 風格一致

實作 queue.[ch]

q_new

我總共參考了三位同學的寫法
struct list_head *q_new()
{
    struct list_head *head =
        (struct list_head *) malloc(sizeof(struct list_head));

    if (!head || !head->prev)
        return NULL;

    INIT_LIST_HEAD(head);

    return head;
}
struct list_head *q_new()
{
    struct list_head *new_q = malloc(sizeof(struct list_head));
    if (new_q)
        INIT_LIST_HEAD(new_q);
    return new_q;
}
struct list_head *q_new()
{
    struct list_head *new_qhead = malloc(sizeof(struct list_head));
    if (!new_qhead)
        return NULL;
    INIT_LIST_HEAD(new_qhead);
    return new_qhead;
}

歸納出 q_new() 應該要直接用以下方法

  • malloc() 失敗時,會直接返回 NULL
  • 第二個沒有處理 Null 的狀態
  • 第一個多處理 head->prev 的情況
struct list_head *q_new()
{
    struct list_head *new_qhead = malloc(sizeof(struct list_head));
+    if (!new_qhead)
+        return NULL;
    INIT_LIST_HEAD(new_qhead);
    return new_qhead;
}

q_free

我總共參考了三位同學的寫法
void q_free(struct list_head *l)
{
    if (!l)
        return;
    for (struct list_head *node = l->next, *next; node != l; node = next) {
        next = node->next;
        element_t *e = list_entry(node, element_t, list);
        free(e->value);
        free(e);
    }
    free(l);
    return;
}
void q_free(struct list_head *l)
{
    if (!l)
        return;

    element_t *entry = NULL, *safe = NULL;
    list_for_each_entry_safe (entry, safe, l, list) {
        list_del(&(entry->list));
        free(entry->value);
        free(entry);
    }
    list_del_init(l);
    free(l);
    return;
}
void q_free(struct list_head *l)
{
    if (!l)
        return;
    element_t *entry, *safe;
    list_for_each_entry_safe (entry, safe, l, list) {
        free(entry->value);
        free(entry);
    }
    free(l);
    return;
}

歸納出 q_free() 可以用以下方法
l 是 dummy node 用來管理整個鏈表

  • list_for_each_entry_safe(entry, safe, l, list): linux kernel 提供的一個安全遍歷巨集
    • entry 指向 l 的第一個節點
    • 讓 safe 記住 entry->next
    • entry = safe
  • INIT_LIST_HEAD(l); 是一個用來初始化的巨集 (macro)
    • 把l的指標 next 和 prev 都指向自己(之後 free 就會都刪掉)
    • 確保 l 不再指向已釋放的記憶體
    • 避免 dangling pointer
void q_free(struct list_head *l)
{
    if (!l)
        return; //鏈表不存在 不做後續操作

    element_t *entry, *safe; //entry當前元素 //safe是entry的下一個節點
    list_for_each_entry_safe(entry, safe, l, list) {
        free(entry->value);
        free(entry);
    }

+    INIT_LIST_HEAD(l);  // 清空 list,確保指標不指向已釋放的記憶體
    free(l);  // 釋放 list_head 本身
}

q_insert_head

我總共參考了三位同學的寫法
bool q_insert_head(struct list_head *head, char *s)
{
    if (!s)
        return false;
    element_t *new_e = e_new(s);
    if (!new_e)
        return false;
    list_add(&new_e->list, head);
    return true;
}
bool q_insert_head(struct list_head *head, char *s)
{
    if (!head)
        return false;

    element_t *new_element = create_new_element(s);

    if (!new_element)
        return false;


    list_add(&(new_element->list), head);

    return true;
}
bool q_insert_head(struct list_head *head, char *s)
{
    if (!head)
        return false;
    element_t *new_ele = malloc(sizeof(element_t));
    if (!new_ele)
        return false;
    INIT_LIST_HEAD(&new_ele->list);
    new_ele->value = strdup(s);
    if (!new_ele->value) {
        free(new_ele);
        return false;
    }
    list_add(&new_ele->list, head);
    return true;
}

歸納出 q_insert_head() 和 q_insert_tail 都需要用到
*


q_insert_tail

我總共參考了三位同學的寫法
bool q_insert_tail(struct list_head *head, char *s)
{
    if (!s)
        return false;
    element_t *new_e = e_new(s);
    if (!new_e)
        return false;
    list_add_tail(&new_e->list, head);
    return true;
}
bool q_insert_tail(struct list_head *head, char *s)
{
    if (!head)
        return false;

    element_t *new_element = create_new_element(s);
    if (!new_element)
        return false;

    // list_add_tail(&(new_element->list), head->prev); //another way
    list_add_tail(&(new_element->list), head);
    return true;
}
bool q_insert_tail(struct list_head *head, char *s)
{
    if (!head)
        return false;
    element_t *new_ele = malloc(sizeof(element_t));
    if (!new_ele)
        return false;

    INIT_LIST_HEAD(&new_ele->list);
    new_ele->value = strdup(s);
    if (!new_ele->value) {
        free(new_ele);
        return false;
    }
    list_add_tail(&new_ele->list, head);
    return true;
}

歸納出 q_insert_head() 可以用以下方法
*


研讀 Linux 核心原始程式碼的 lib/list_sort.c