Try   HackMD

2022q1 Homework1 (lab0)

contributed by < RoyWFHuang > (網路旁聽)

tags: linux_class

開發環境為:
OS: ubuntu 20.04
kernel ver: 5.4.0-100-generic
CPU arch: x86_64
使用 multipass 開發

$ multipass info dev
Name:           dev
State:          Running
IPv4:           10.195.227.138
Release:        Ubuntu 20.04.3 LTS
Image hash:     db49f99b5162 (Ubuntu 20.04 LTS)
Load:           0.00 0.00 0.00
Disk usage:     4.2G out of 9.5G
Memory usage:   167.2M out of 3.8G
Mounts:         /home/roy/src-code => /home/roy/src-code
                    UID map: 1000:default
                    GID map: 1000:default

In multipass shell
$ uname -a
Linux dev 5.4.0-100-generic #113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
$ gcc -v
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

lab0 作業描述

q_new: 建立新的「空」佇列;
q_free: 釋放佇列所佔用的記憶體;
q_insert_head: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則);
q_insert_tail: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則);
q_remove_head: 在佇列開頭 (head) 移去 (remove) 給定的節點;
q_release_element: 釋放特定節點的記憶體空間;
q_size: 計算目前佇列的節點總量;
q_delete_mid: 移走佇列中間的節點,詳見 LeetCode 2095. Delete the Middle Node of a Linked List
q_delete_dup: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 LeetCode 82. Remove Duplicates from Sorted List II
q_swap: 交換佇列中鄰近的節點,詳見 LeetCode 24. Swap Nodes in Pairs
q_reverse: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點;
q_sort: 以遞增順序來排序鏈結串列的節點,可參閱 Linked List Sort 得知實作手法;

queue.c

q_new

struct list_head *q_new()
{
    struct list_head * head = calloc(1, sizeof(struct list_head));
    if (NULL == head) return NULL;
    INIT_LIST_HEAD(head);
    return head;
}

q_free

void q_free(struct list_head *l)
{
    if (NULL == l)
        return;
    while (!list_empty(l)) {
        struct list_head *del_node = l->next;
        list_del(del_node);
        element_t *e = container_of(del_node, element_t, list);
        free(e->value);
        free(e);
    }
    free(l);
}

q_insert_head

bool q_insert_head(struct list_head *head, char *s)
{
    if (NULL == head)
        return false;
    element_t *e = calloc(1, sizeof(element_t));
    if (NULL == e)
        return false;
    e->value = calloc((strlen(s) + 1), sizeof(char));
    if (NULL == e->value) {
        free(e);
        return false;
    }
    memcpy(e->value, s, sizeof(char) * strlen(s));
    list_add(&e->list, head);
    return true;
}

q_remove_head

element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
    if (NULL == head || NULL == sp)
        return NULL;
    if (list_empty(head))
        return NULL;
    element_t *e = list_first_entry(head, element_t, list);
    if (NULL == e)
        return NULL;
    list_del(&(e->list));

    memset(sp, 0, sizeof(char) * bufsize);
    strncpy(sp, e->value, bufsize - 1);
    return e;
}

calloc / malloc 問題

在寫上面這四個 function 的時候, 可說是吃盡苦頭, 每次都出現這個訊息
PS: 而且是第一個就錯, 信心打擊超大的

# Test of insert_head and remove_head +++ TESTING trace trace-01-ops: # Test of insert_head and remove_head ERROR: Attempted to free unallocated block. Address = 0x555fa713ff00 ERROR: Attempted to free unallocated or corrupted block. Address = 0x555fa713ff00 ERROR: Corruption detected in block with address 0x555fa713ff00 when attempting to free it Segmentation fault occurred. You dereferenced a NULL or invalid pointer --- trace-01-ops 0/5

可是明明就寫法邏輯上都沒錯阿

好吧, 這時候只好把錯誤訊息扔進去找找看, 是怎麼觸發的. 於是乎 find_header -> test_free -> 找到header #define free test_free, 然後往上面看, 只有定義 malloc 阿, calloc 就變成直接使用 glibc 了. 補上 #define calloc test_calloc 後, 在跑一次看看.

+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
---	trace-01-ops	5/5

恩果然成功了

但是, 一切當然都不會這麼順利阿. 果然在跑trace-13-malloc.cmd 這測項, 又 fail 了.

回頭看 test_calloc 實做, 發現到他是透過 test_malloc 一併去管理的, 而 13 測項則是限制 malloc , 所以這邊就會直接回傳 NULL 出來了, 當然 NULL 沒有處理直接 memset 當然會 segmentation fault

所以就把 test_calloc 改成

void *test_calloc(size_t nelem, size_t elsize)
{
    /* Reference: Malloc tutorial
     * https://danluu.com/malloc-tutorial/
     */
    // size_t size = nelem * elsize;  // TODO: check for overflow
    size_t size;
    if (__builtin_mul_overflow(nelem, elsize, &size))
        return NULL;
    void *ptr = test_malloc(size);
    if (NULL == ptr)
        return NULL;
    memset(ptr, 0, size);
    return ptr;
}

這邊也順便處理了 overflow 問題

不過 glibc裡面, 也有類似的處理 malloc.c 這邊上面寫 size_t is unsigned so the behavior on overflow is defined. 似乎說明 size_t 對於overflow有特別處理?(自己寫程式測試過後, 似乎是會轉成負數?) 而

if (elem_size != 0 && bytes / elem_size != n)
{
    __set_errno (ENOMEM);
    return 0;
}

這段也對於 overflow 做了相對應的處理


接下來的都很順順的就直接寫完了 XD

q_insert_tail

bool q_insert_tail(struct list_head *head, char *s)
{
    if (NULL == head)
        return false;
    element_t *e = calloc(1, sizeof(element_t));
    if (NULL == e)
        return false;
    // memset(e, 0, sizeof(element_t));
    e->value = malloc((strlen(s) + 1) * sizeof(char));
    if (NULL == e->value) {
        free(e);
        return false;
    }
    memset(e->value, 0, (strlen(s) + 1) * sizeof(char));
    memcpy(e->value, s, sizeof(char) * strlen(s));
    list_add_tail(&e->list, head);
    return true;
}

q_remove_tail

element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
    if (NULL == head || NULL == sp)
        return NULL;
    if (list_empty(head))
        return NULL;
    element_t *e = list_last_entry(head, element_t, list);

    list_del(&(e->list));
    memset(sp, 0, sizeof(char) * bufsize);
    strncpy(sp, e->value, bufsize - 1);
    return e;
}

q_size

int q_size(struct list_head *head)
{
    if (NULL == head)
        return 0;
    int len = 0;
    struct list_head *node;
    list_for_each (node, head) {
        len++;
    }
    return len;
}

q_delete_mid

bool q_delete_mid(struct list_head *head)
{
    int tar_len = q_size(head) / 2;
    struct list_head *node = NULL;
    list_for_each (node, head) {
        if (0 == tar_len) {
            element_t *e = container_of(node, element_t, list);
            list_del(node);
            q_release_element(e);
            break;
        }
        tar_len--;
    }
    // https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
    return true;
}

q_delete_dup

這題在寫的時候, 其實我本來是直接拿 Leetcode 的程式直接來改, 改完才發現, Leetcode 是 singly linked list, 按照上面寫, prev 就會被搞亂
所以才重改了一個版本, 也順便改成non-recursive 版本

bool q_delete_dup(struct list_head *head)
{
    // https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
    if (!head)
        return false;
    if (list_empty(head))
        return true;
    struct list_head *node = head->next, *chk;
    if ((chk = node->next) == head)
        return true;

    while (chk != head) {
        struct list_head *kep;
        kep = chk = node->next;
        while (chk != head &&
               !strcmp(container_of(node, element_t, list)->value,
                       container_of(chk, element_t, list)->value)) {
            element_t *e = container_of(chk, element_t, list);
            list_del(chk);
            q_release_element(e);
            chk = node->next;
        }
        if (chk != kep) {
            element_t *e = container_of(node, element_t, list);
            list_del(node);
            q_release_element(e);
        }
        node = chk;
    }
    return true;
}

q_swap

這題有點偷懶, 直接用兩個指標, 把兩個 node 的 data 對調, 這樣連 next, prev 指標都不用重新設定.

void q_swap(struct list_head *head)
{
    if (!head || list_empty(head))
        return;
    struct list_head *swp1 = head->next, *swp2;

    while (swp1 != head && swp1->next != head) {
        swp2 = swp1->next;
        char *tmp = container_of(swp1, element_t, list)->value;
        container_of(swp1, element_t, list)->value =
            container_of(swp2, element_t, list)->value;
        container_of(swp2, element_t, list)->value = tmp;
        swp1 = swp2->next;
    }
    // https://leetcode.com/problems/swap-nodes-in-pairs/
}

反轉, 即頭尾對調, 當頭跟尾的指標碰在一起的時候, 就是對調完了

q_reverse

void q_reverse(struct list_head *head)
{
    if (NULL == head || list_empty(head))
        return;

    struct list_head *hd = head->next;
    struct list_head *ta = head->prev;
    while (hd != ta) {
        char *tmp = container_of(hd, element_t, list)->value;
        container_of(hd, element_t, list)->value =
            container_of(ta, element_t, list)->value;
        container_of(ta, element_t, list)->value = tmp;
        if (ta == hd->next)
            break;
        ta = ta->prev;
        hd = hd->next;
    }
}

q_sort

這題參考了 Risheng1128 的寫法, 然後把自己以前 leetcode 的寫法直接搬過來改寫

static struct list_head *mergeTwoLists(struct list_head *l1,
                                       struct list_head *l2)
{
    if (NULL == l2 && NULL == l1)
        return NULL;

    if (NULL == l1)
        return l2;
    else if (NULL == l2)
        return l1;

    struct list_head *ret_list = NULL, *tmp_list;
    if (strcmp(container_of(l1, element_t, list)->value,
               container_of(l2, element_t, list)->value) < 0) {
        ret_list = l1;
        l1 = l1->next;
    } else {
        ret_list = l2;
        l2 = l2->next;
    }
    tmp_list = ret_list;
    while (l1 && l2) {
        if (strcmp(container_of(l1, element_t, list)->value,
                   container_of(l2, element_t, list)->value) < 0) {
            tmp_list->next = l1;
            l1 = l1->next;
        } else {
            tmp_list->next = l2;
            l2 = l2->next;
        }
        tmp_list = tmp_list->next;
    }
    tmp_list->next = (struct list_head *) ((u_int64_t) l1 | (u_int64_t) l2);
    return ret_list;
}

static struct list_head *m_sort(struct list_head *head)
{
    if (!head || !head->next)
        return head;

    struct list_head *fast = head, *slow = head;
    while (fast && fast->next) {
        fast = fast->next->next;
        slow = slow->next;
    }

    slow->prev->next = NULL;
    struct list_head *l1 = m_sort(head);
    struct list_head *l2 = m_sort(slow);
    return mergeTwoLists(l1, l2);
}

/*
 * Sort elements of queue in ascending order
 * No effect if q is NULL or empty. In addition, if q has only one
 * element, do nothing.
 */
void q_sort(struct list_head *head)
{
    if (!head || list_empty(head))
        return;

    head->prev->next = NULL;
    head->next = m_sort(head->next);

    struct list_head *target = head;
    while (target->next) {
        target->next->prev = target;
        target = target->next;
    }

    head->prev = target;
    target->next = head;
}
       

qsort 改版:

改用 pre-define方式, 可以讓使用者自定義要遞增還是遞減排序, 並使用 #define 以便未來可以融入 meunconfig 讓使用者選擇

enum {
    eCompareInc,
    eCompareDec,
    eCompareEq,
};

#define compare_value(func, v1, v2, compare) (func((v1), (v2)) compare 0)
#define compare_inc(func, v1, v2) compare_value(func, v1, v2, <)
#define compare_dec(func, v1, v2) compare_value(func, v1, v2, >)
#define compare_eq(func, v1, v2) compare_value(func, v1, v2, ==)

#ifndef COMPARE_TPYE
#pragma message("COMPARE_TPYE should be defined")
#define COMPARE_TPYE eCompareInc
#endif /* COMPARE_TPYE */

#if COMPARE_TPYE == eCompareInc
#define compare_func(func, v1, v2) compare_inc(func, v1, v2)
#elif COMPARE_TPYE == eCompareDec
#define compare_func(func, v1, v2) compare_dec(func, v1, v2)
#elif COMPARE_TPYE == eCompareEq
#define compare_func(func, v1, v2) compare_eq(func, v1, v2)
#else
#error COMPARE_TPYE use undefine data type
#endif /* COMPARE_TPYE == eCompareInc */

static struct list_head *mergeTwoLists(struct list_head *l1,
                                       struct list_head *l2)
{
    if (NULL == l2 && NULL == l1)
        return NULL;

    if (NULL == l1)
        return l2;
    else if (NULL == l2)
        return l1;

    struct list_head *ret_list = NULL, *tmp_list;
    if (compare_func(strcmp, container_of(l1, element_t, list)->value,
                     container_of(l2, element_t, list)->value)) {
        ret_list = l1;
        l1 = l1->next;
    } else {
        ret_list = l2;
        l2 = l2->next;
    }
    tmp_list = ret_list;
    while (l1 && l2) {
        if (compare_func(strcmp, container_of(l1, element_t, list)->value,
                         container_of(l2, element_t, list)->value)) {
            tmp_list->next = l1;
            l1 = l1->next;
        } else {
            tmp_list->next = l2;
            l2 = l2->next;
        }
        tmp_list = tmp_list->next;
    }
    tmp_list->next = (struct list_head *) ((u_int64_t) l1 | (u_int64_t) l2);
    return ret_list;
}

Valgrind

Valgrind start testing

在跑 valgrind 之前, 直接先跑一次qtest看看

$ ./qtest 
cmd> quit
Freeing queue
Segmentation fault occurred.  You dereferenced a NULL or invalid pointer
Aborted (core dumped)

Segmentation fault 改用gdb 看看哪邊錯了

$ gdb ./qtest core-qtest-6-1000-1000-5657-1645875820
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./qtest...
[New LWP 5657]
Core was generated by `./qtest'.
Program terminated with signal SIGABRT, Aborted.
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.

看來訊息被攔走了用bt看看是從哪邊被攔截

(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007f71beca3859 in __GI_abort () at abort.c:79
#2  0x0000000000402c87 in sigsegvhandler (sig=<optimized out>) at qtest.c:866
#3  <signal handler called>
#4  0x0000000000405337 in run_console (infile_name=<optimized out>) at console.c:654
#5  0x00000000004029b7 in main (argc=1, argv=0x7ffc3e947b88) at qtest.c:1019
(gdb) up 4
#4  0x0000000000405337 in run_console (infile_name=<optimized out>) at console.c:654
654	                while (buf_stack->fd != STDIN_FILENO)

錯誤居然是在這邊?? 難道 buf_stack 會是 NULL ??

改多一個判斷 NULL pointer 看看

            linenoiseFree(cmdline);
            if (NULL != buf_stack) {
                while (buf_stack->fd != STDIN_FILENO)
                    cmd_select(0, NULL, NULL, NULL, NULL);
            }
            has_infile = false;
./qtest cmd> quit Freeing queue

恩過了

接下來開啟 valgrind 測試

$ make;valgrind ./qtest -f traces/trace-14-perf.cmd 
make: Nothing to be done for 'all'.
cmd> # Test performance of insert_tail, reverse, and sort
cmd> option fail 0
cmd> option malloc 0
cmd> new
l = []
cmd> ih dolphin 1000000
ERROR: Time limit exceeded.  Either you are in an infinite loop, or your code is too inefficient
ERROR: Insertion of dolphin failed (1 failures total)
l = [dolphin ... ]
cmd> it gerbil 1000000
ERROR: Time limit exceeded.  Either you are in an infinite loop, or your code is too inefficient
ERROR: Insertion of gerbil failed (2 failures total)
l = [dolphin ... ]
cmd> reverse
l = [gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil gerbil ... ]
cmd> sort
ERROR: Time limit exceeded.  Either you are in an infinite loop, or your code is too inefficient
==5906== Invalid read of size 8
==5906==    at 0x10AC42: do_sort (qtest.c:681)
==5906==    by 0x10D507: interpret_cmda (console.c:185)
==5906==    by 0x10DA53: interpret_cmd (console.c:208)
==5906==    by 0x10E299: cmd_select (console.c:583)
==5906==    by 0x10E5B0: run_console (console.c:659)
==5906==    by 0x10C9FE: main (qtest.c:1019)
==5906==  Address 0xfffffffffffffff8 is not stack'd, malloc'd or (recently) free'd
==5906== 
Segmentation fault occurred.  You dereferenced a NULL or invalid pointer

Error 顯示是 time out 問題, 根據 laneser 的說明是 static int time_limit = 1; 超時造成, 可是我的想法是, 如果是超時正常來說也不應該要出現 "Segmentation fault" 才對, 所以就一樣開 gdb 來看吧

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./qtest...
[New LWP 5913]
Core was generated by `./qtest -f traces/trace-sort-perf.cmd'.
Program terminated with signal SIGABRT, Aborted.
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007f766e664859 in __GI_abort () at abort.c:79
#2  0x0000559be32fbfa2 in sigsegvhandler (sig=<optimized out>) at qtest.c:866
#3  <signal handler called>
#4  do_sort (argc=<optimized out>, argv=<optimized out>) at qtest.c:681
#5  0x0000559be32fe508 in interpret_cmda (argc=argc@entry=1, argv=argv@entry=0x559be3d11e08) at console.c:185
#6  0x0000559be32fe5fc in do_time (argc=2, argv=0x559be3d11e00) at console.c:388
#7  0x0000559be32fe508 in interpret_cmda (argc=argc@entry=2, argv=argv@entry=0x559be3d11e00) at console.c:185
#8  0x0000559be32fea54 in interpret_cmd (cmdline=<optimized out>, cmdline@entry=0x559be33085a0 <linebuf> "time sort\n") at console.c:208
#9  0x0000559be32ff29a in cmd_select (nfds=<optimized out>, nfds@entry=0, readfds=0x7ffd875aabe0, readfds@entry=0x0, writefds=writefds@entry=0x0, 
    exceptfds=exceptfds@entry=0x0, timeout=timeout@entry=0x0) at console.c:583
#10 0x0000559be32ff5b1 in run_console (infile_name=<optimized out>) at console.c:659
#11 0x0000559be32fd9ff in main (argc=3, argv=0x7ffd875ab0d8) at qtest.c:1019
(gdb) 

看來是 sort 問題, 回頭看 sort 演算法, 在 sort 過程中, 會把 list 切斷, 這時候如果觸發timeout

    if (exception_setup(true))
        q_sort(l_meta.l);
    exception_cancel();

這段 sort 就不會完成, 進而引起這段的錯誤

    if (l_meta.size) {
        for (struct list_head *cur_l = l_meta.l->next;
            cur_l != l_meta.l && --cnt; cur_l = cur_l->next) {
            /* Ensure each element in ascending order */
            /* FIXME: add an option to specify sorting order */
            element_t *item, *next_item;
            item = list_entry(cur_l, element_t, list);
            next_item = list_entry(cur_l->next, element_t, list);
            if (strcasecmp(item->value, next_item->value) > 0) {
                report(1, "ERROR: Not sorted in ascending order");
                ok = false;
                break;
            }
        }
    }

所以, 就改良吧

目標: 如果 timeout 就跳出測試

  1. 增加 timeout
bool timer_alarm = false;

bool exception_setup(bool limit_time)
{
    if (sigsetjmp(env, 1)) {
        /* Got here from longjmp */
        jmp_ready = false;
        if (time_limited) {
            alarm(0);
            time_limited = false;
        }

        if (error_message)
            report_event(MSG_ERROR, error_message);
        error_message = "";
        timer_alarm = true;
        return false;
    }

    /* Got here from initial call */
    jmp_ready = true;
    if (limit_time) {
        alarm(time_limit);
        time_limited = true;
        timer_alarm = false;
    }
    return true;
}
  1. 然後在相對應的地方插入檢查
bool do_sort(int argc, char *argv[])
{
    ....
    exception_cancel();
    set_noallocate_mode(false);

    bool ok = true;
    if (!timer_alarm) {
        if (l_meta.size) {
            for (struct list_head *cur_l = l_meta.l->next;
                cur_l != l_meta.l && --cnt; cur_l = cur_l->next) {    
  1. 另外還需要修改 queue_quit部份, 因為qtest在結束前, 會將 queue free
static bool queue_quit(int argc, char *argv[])
{
    report(3, "Freeing queue");
    if (lcnt > big_list_size)
        set_cautious_mode(false);
    if (!timer_alarm) {
        if (exception_setup(true))
            q_free(l_meta.l);
        exception_cancel();
    }
    set_cautious_mode(true);

進行測試

$ make;valgrind ./qtest -f traces/trace-14-perf.cmd 
  CC	qtest.o
  CC	harness.o
  CC	queue.o
queue.c:299:9: note: #pragma message: COMPARE_TPYE should be defined
  299 | #pragma message("COMPARE_TPYE should be defined")
      |         ^~~~~~~
  LD	qtest
cmd> # Test performance of insert_tail, reverse, and sort
cmd> option fail 0
cmd> option malloc 0
cmd> new
l = []
cmd> ih dolphin 1000000
ERROR: Time limit exceeded.  Either you are in an infinite loop, or your code is too inefficient
ERROR: Insertion of dolphin failed (1 failures total)
l = [dolphin ... ]
return falsefalse return
Freeing queue
ERROR: Freed queue, but 2 blocks are still allocated
==6107== 48 bytes in 1 blocks are still reachable in loss record 1 of 2
==6107==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==6107==    by 0x10E68D: test_malloc (harness.c:138)
==6107==    by 0x10E778: test_calloc (harness.c:174)
==6107==    by 0x10EC8B: q_insert_head (queue.c:60)
==6107==    by 0x10C361: do_ih (qtest.c:202)

再來就是要解決 leak 問題了

看測試的案例, 應該是在新增 node 的時候出問題, 觀看test-malloc實做發現,

block_ele_t *new_block =
        malloc(size + sizeof(block_ele_t) + sizeof(size_t));
.....

從這邊開始, 到下面這段程式碼

new_block->next = allocated;
// cppcheck-suppress nullPointerRedundantCheck
new_block->prev = NULL;

若此時收到中斷, 則不會把跟系統取得的記憶體放入 list 中, 也就無法順利歸還了.

以下採用偷懶的解決方式:

sigset_t newmask,oldmask;
sigprocmask(SIG_BLOCK, &newmask, &oldmask);

block_ele_t *new_block =
        malloc(size + sizeof(block_ele_t) + sizeof(size_t));

....
    
if (allocated)
        allocated->prev = new_block;
allocated = new_block;
allocated_count++;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return p;

直接把中間這段的信號中斷關了

問題是解決了但是留下了另一個大問題就是當這段如果發生錯誤, 就沒辦法結束他了

TODO1: 使用另外方式管理, 可能要參考 slab 的實做進行修改.
TODO2: 關於 sort 造成的 Segmentation fault, 是否要改成不需要將 list切斷而可以使用 merge sort合併的方式處理(這邊我有嘗試過, 但似乎都失敗了, 如果時間夠再來進行改寫看看.)

qtest 提供新的命令 shuffle

TODO

qtest 提供新的命令 web

TODO

後記:

每次跟 jserv 老師的課程都超辛苦, 白天要上班, 晚上要帶小孩, 都只能用半夜時間一點一點的慢慢寫, 不知道能不能跟上進度.
每次在寫這個課程的時候都會感覺到, 我之前到底再做啥? 怎麼工作後沒進步就算了, 連之前學校學的都通通還回去了.
每次看到老師上課的跟同學的互動, 越發覺得自己好廢, 真的就像老師說的一樣, 我只是運氣好早出來工作卡了位置Orz

PS: 突然想到之前看到一個問題, 學了 linux kernel 能做啥?, 我是不知道能做啥, 但是我知道如果不學, 只會被學校剛畢業出來的學生壓在地上摩擦

PS2: 最近剛好在整理之前看的記憶相關資料, 剛好去研讀到 glibc的 malloc 跟 calloc 才會有此想法, 用calloc 去寫程式(另外原因是因為在寫quiz 1 的時候, 丟上leetcode跑出現 error 怕這邊也同樣問題, 所以才改成用calloc)