# 2025q1 Homework5 (assessment) contributed by < `JimmyChongz` > ## 閱讀〈[因為自動飲料機而延畢的那一年](https://blog.opasschang.com/the-story-of-auto-beverage-machine-1)〉的啟發 看完作者製作自動飲料機的心路歷程後,我深刻體會到「不輕易放棄」有多難。當困難接踵而至時,選擇放棄很簡單,但我會因為放棄而失去解決問題與累積經驗的機會,若未來有人問我:「在你的求學或工作經驗中,遇過最大的挑戰是什麼?你是如何克服的?」我可能會因為當初的退縮而無話可說。 ## <s>我覺得這堂課的作業內容對我來說難度頗高</s> ,目前完成度最高的作業是[作業一](https://hackmd.io/@Jimmy-Xu/linux2025-homework1),在寫作業的過程中,我意識到我還有太多東西要補,搞不清楚自己到底要先補哪一個,且我使用 LLM 的機率很高,當我遇到問題時,都習慣先問 LLM,因為它能夠馬上給我「符合邏輯」的解釋,讓我能夠免強跟上作業繳交進度,但這就是為了交作業而交作業,並沒有真的學到東西,這部分我需要改變心態,應先把一件事做好再做另一件事。 學期初受指導教授指派擔任物聯網課程 Network Programming 的助教,需說明 socket interface 概念以及帶領學生透過樹莓派做 client & server 相關的實驗,有花費一些時間在看 Socket Interface 相關的內容,且 server 要能夠服務多個 client,需要對 server 做多執行緒(I/O)的設計,因此內容就應涉及 pthread, select 或 epoll 的概念。所以,針對期末專題我希望是**完成第六次作業 khttpd** 或參與類似 [以 eBPF 建構 TCP 伺服器](https://hackmd.io/@sysprog/linux2023-projects?stext=6195%3A17%3A0%3A1746010931%3AXHjs9l) 的專案。 :::info 對 socket() 回傳的 `File Descriptor` 有些疑惑,已知 Everything in Unix is a file 的概念,且 `File Descriptor` 是一個非負整數,代表 File Descriptor Table 中的一個 entry 的索引。File Descriptor Table 是儲存在 `PCB` (即 Linux 中的 `task_struct`)中的一個資料結構,用來記錄程序所開啟檔案的相關資訊。每個 entry 會指向一個 Open File Table 的 entry,該表中包含與檔案操作有關的詳細資料,例如檔案的偏移量、開啟模式等。Open File Table 中的每個項目則會對應到一個 i-node(Linux 檔案系統中的資料結構),而 i-node 中則紀錄了檔案的路徑、大小、權限等。不過,這是針對像是 open() 這種 system call,即 open() 回傳的 fd 最終會指向硬碟中的某個檔案,但像是 socket() 回傳的 fd 最終會指向什麼?有查過是 Linux 實作 TCP/IP 的網路<s>協議</s>協定堆疊,但有點<s>抽象</s>難以理解,還是<s>不太懂</s>不懂。 ::: :::danger 就是困難才要特別教,其他教授對你沒期待,但我不是! 在此紀錄你的問題,隨後討論。 > 謝謝老師 ::: :::spoiler 會議紀錄 - [Homework5](https://hackmd.io/@Jimmy-Xu/linux2025-homework5) - bitwise operations - Q2: Assume x is a single-precision floating point (IEEE 754) and always >= 1, calculate square root. No FPU is allowed. [https://hackmd.io/@sysprog/linux2025-kxo](https://hackmd.io/@sysprog/linux2025-kxo) ⇒ Q1: 何以 Linux 核心不傾向使用 FPU float my_sqrtf(float x) { } Q3: 為何有 accpet 系統呼叫?為何不能直接 read socket fd? Tell me why? 紀錄問題! [https://hackmd.io/@sysprog/c-linked-list](https://hackmd.io/@sysprog/c-linked-list) Q4: 比照 remove_list_node 撰寫 find_node / reverse_nodes,利用 indirect pointer (no external allocated memory. i.e., constant space complexity) ```c bool find_node(List *list, Node *target) { Node **indirect = &list->head; while (*indirect != NULL) { if (*indirect == target) { return true; } indirect = &(*indirect)->next; } return false; } ``` // in-place: [In-place_algorithm](https://en.wikipedia.org/wiki/In-place_algorithm) ```c void reverse_nodes(List *head) { if (!head || !head->next) { // singular return; } Node **cur = &head->next; // current Node *tmp = NULL; Node *next = head->next; while (cur) { *cur = tmp; tmp = next; next = next->next; cur = &(*cur)->next; } } ``` 如何測試? head -> 1 -> 2 -> 3 ->           ^ shorter and faster 3 -> 2 -> 1 Q5:複雜度 大 O、小 o 的定義 return type? 重視細節! [https://hackmd.io/@sysprog/it-vocabulary](https://hackmd.io/@sysprog/it-vocabulary) 誠實面對自己 待辦事項 - TODO: 回答上述 Q1 到 Q5 ::: ## [回覆會議問答記錄 - 完整版](https://hackmd.io/@Jimmy-Xu/0514qa) ### Q4: 比照 remove_list_node 撰寫 find_node / reverse_nodes,利用 indirect pointer (no external allocated memory. i.e., constant space complexity),如何驗證? ```c bool find_node(List *list, Node *target) { Node **indirect = &list->head; while (*indirect != NULL) { if (*indirect == target) { return true; } indirect = &(*indirect)->next; } return false; } ``` :::danger 以下程式碼的缺陷: 1. 參數 `List *list` 指定單向鏈結串列的開頭,當完成反轉所有節點後,鏈結串列的開頭會發生改變,但呼叫該函式的人卻無從知悉新的開頭是什麼 > 是,當完成反轉所有節點後 `list->head` 所指向的節點會不一樣,不過呼叫該函式的人是傳入 `list` 的位址,也就是說==這函式是更改的 `list->head` 與原先(於 main() 中的) `list->head` 是同一塊記憶體空間==,所以照理講呼叫該函式的人應該能夠取得更新後的 `list->head` (開頭)? ```c typedef struct Node { int data; struct Node* next; } Node; typedef struct { struct Node* head; } List; ``` 2. 當鏈結串列僅有一個節點時 (即 singular),不用繼續操作 > 新增 `!list->head->next` 的判斷。 3. 未依循指定的程式碼縮排風格 (類似 Linux 核心) > 已更正 ::: ```c void reverse_node(List *list) { if (!list || !list->head || !list->head->next) return; Node **cur = &list->head; Node *prev = NULL; Node *next = NULL; while (*cur) { next = (*cur)->next; (*cur)->next = prev; prev = *cur; *cur = next; } *cur = prev; } ``` ### 驗證方法一:畫圖追蹤程式碼 將必要指標初始化: ![截圖 2025-05-14 晚上9.10.25](https://hackmd.io/_uploads/H1aBLffZee.png =70%x) 使用 while 迴圈將每個 Node 之 next 指標往前一個 Node 指:![截圖 2025-05-14 晚上9.14.53](https://hackmd.io/_uploads/Hk38Pff-eg.png =70%x) 最後,將 head 指到最末端的節點: ![截圖 2025-05-14 晚上9.16.05](https://hackmd.io/_uploads/ryWivGzZlg.png =70%x) ### 驗證方法二:寫出完整程式碼來跑,並印出結果 :::spoiler 完整測試程式碼 ```c #include <stdio.h> #include <stdbool.h> typedef struct Node { int data; struct Node* next; } Node; typedef struct { struct Node* head; } List; bool find_node(List *list, Node *target) { Node **indirect = &list->head; while (*indirect != NULL) { if (*indirect == target) { return true; } indirect = &(*indirect)->next; } return false; } void reverse_node(List *list) { if (!list || !list->head) { return; } Node **cur = &list->head; Node *prev = NULL; Node *next = NULL; while (*cur) { next = (*cur)->next; (*cur)->next = prev; prev = *cur; *cur = next; } *cur = prev; } void print_list(List *list) { Node *cur = list->head; while (cur != NULL) { printf("%d -> ", cur->data); cur = cur->next; } printf("NULL\n"); } int main() { Node node4 = {4, NULL}; Node node3 = {3, NULL}; Node node2 = {2, &node3}; Node node1 = {1, &node2}; List list = {.head = &node1}; printf("list: "); print_list(&list); printf("Find 2: %s\n", find_node(&list, &node2) ? "Found" : "Not Found"); printf("Find 4: %s\n", find_node(&list, &node4) ? "Found" : "Not Found"); reverse_node(&list); printf("Reverse list: "); print_list(&list); return 0; } ``` ::: 預期輸出結果: ```bash jimmy@NEAT:~/linux2025$ ./test_list list: 1 -> 2 -> 3 -> NULL Find 2: Found Find 4: Not Found Reverse list: 3 -> 2 -> 1 -> NULL ```