# kxo contributed by <`liangchingyun`> :::danger 注意書寫規範! ::: ## 參考資料 - [x] [N04: kxo(A)](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-a) * 井字遊戲 * Linux 核心的浮點數運算 * 強化學習(Reinforcement Learning) * 以定點數實作 MCTS - [x] [N04: kxo(B)](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-b) * `ksort`: 處理並行排序的 Linux 核心模組 * `ksort` 核心模組內部 * 核心模式的時間測量 * Linux 效能分析 - [x] [N04: kxo&#40;C&#41;](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-c) * Linux 核心的並行處理 * Linux 核心的 kfifo - [x] [N04: kxo(D)](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-d) * 對奕的核心模組 * kxo 程式碼分析 - [ ] [N04: kxo(E)](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-e) * Shannon Entropy 與亂度 * 亂數產生器 * xoroshiro 亂數產生器 * 回顧作業一的洗牌過程 * `xoro` 核心模組 - [ ] [N04: kxo(F)](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-f) 作業要求: - [ ] 縮減使用者和核心層級的通訊成本,並允許並行的對奕 - [ ] 引入若干 coroutine - [ ] 在「電腦 vs. 電腦」的對弈模式,比照前述 load average 的計算方式,規範針對下棋 AI 的 load 機制 - [ ] 運用定點數來統計不同 AI 實作機制對系統負載的使用率,整合到畫面輸出,開發過程中應有對應的數學分析和羅列實作考量因素。 - [x] 對弈的過程中,要在螢幕顯示當下的時間 (含秒數),並持續更新。 - [x] 原本 [kxo](https://github.com/sysprog21/kxo) 棋盤繪製在核心模式,你應該修改程式碼,使畫面呈現的部分全部在使用者層級,且善用 bitops,降低核心和使用者層級之間的通訊成本,應給予相關量化 - [ ] 自 [jserv/ttt](https://github.com/jserv/ttt) 移植 reinforcement learning (RL) 到 [kxo](https://github.com/sysprog21/kxo) 核心模組,使其得以動態切換。 - [ ] [LKMPG WriteUp]() ## 對奕的核心模組 :::danger 注意用語! ::: ### 執行<s>指令</s> 編譯 ``` make ``` 載入 kxo module ``` sudo insmod kxo.ko ``` 執行 ``` sudo ./xo-user ``` 卸載模組 ``` sudo rmmod kxo ``` 查看核心訊息 ``` sudo dmesg ``` ### 使用者層級互動 使用了 `raw_mode_enable()` ,則不用另外再執行 `$ stty start '^-' stop '^-'`,即可直接接收 Ctrl-P,Ctrl-Q 等控制字元 #### 使用 Ctrl + Q 使對弈終止時產生亂碼 > Commit [22a2477](https://github.com/sysprog21/kxo/commit/22a24770eb9fae4bc8c9aa3c4544b2af11961de7) ``` �_�Stopping the kernel space tic-tac-toe game... ``` 給 `display_buf` 中的字串正確結尾(`'\0'`)便可解決 ```diff read(device_fd, display_buf, DRAWBUFFER_SIZE); + display_buf[DRAWBUFFER_SIZE - 1] = '\0'; printf("%s", display_buf); } ``` #### 使對弈終止後自動重置 `xo-user` > Commit [db786e8](https://github.com/sysprog21/kxo/commit/db786e83599cfa8ceec5660bf4f2d03e3f720915) 原程式在執行完 Ctrl-Q 後,直接跑 `sudo ./xo-user` 的結果會卡在第一個棋盤,需要重新載入 kxo module 才會正常。 查看核心訊息後發現使用 Ctrl-Q 會使 `attr_obj.end` 被設成 `'1'`(ASCII `49`)。解決辦法是在 `kxo_release()` 程式中將 `attr_obj.end` 重設為 `'0'`(ASCII `48`),就不用一直重新載入 kxo module。 ```diff static int kxo_release(struct inode *inode, struct file *filp) { pr_debug("kxo: %s\n", __func__); if (atomic_dec_and_test(&open_cnt)) { del_timer_sync(&timer); flush_workqueue(kxo_workqueue); fast_buf_clear(); } pr_info("release, current cnt: %d\n", atomic_read(&open_cnt)); + attr_obj.end = 48; return 0; } ``` #### 使畫面呈現在使用者層級 > Commit [43dc5ba](https://github.com/sysprog21/kxo/commit/43dc5babcecb6a9863cca1173681811f3777deaf) 原本 `draw_board()` 函式在核心層級中進行,每次棋盤狀態改變,核心就要透過 `copy_to_user()` 把整個畫面樣板傳給使用者空間。 參考 [a2985ba](https://github.com/sysprog21/kxo/commit/a2985ba98d9440414107cba4bd5093b95e34f86c),將核心層級(`main.c`)中的`draw_board` 改寫成只把 16 格棋盤資料存入 `draw_buffer`,只需傳送字元,無需畫出畫面。 ```diff - static char draw_buffer[DRAWBUFFER_SIZE]; + static char draw_buffer[N_GRIDS]; ``` 另外,使用者層級(`xo-user.c`)中新增 `draw_board()` 函數,將核心傳來的 16 格資料轉換為棋盤畫面(含換行與橫線)。 **效能分析** 在 `kxo_release` 最後加入下面這一行: ```c pr_info("kxo: DRAWBUFFER_SIZE = %d, N_GRIDS = %d\n", DRAWBUFFER_SIZE, N_GRIDS); ``` 可得到: ``` [421143.829037] kxo: DRAWBUFFER_SIZE = 66, N_GRIDS = 16 ``` 原本讀取 `DRAWBUFFER_SIZE`(66 個字元)→ 現在只讀 `N_GRIDS`(16 個字元)。也就是減少了了 66 - 16 = 50 bytes 的傳輸成本。 :::danger 提供更多數學分析 ::: **亂碼問題** 先前所提到[使用 Ctrl + Q 使對弈終止時產生亂碼](https://hackmd.io/PF72FR7lQvKNhSVU3L9jWA?both#%E4%BD%BF%E7%94%A8-Ctrl--Q-%E4%BD%BF%E5%B0%8D%E5%BC%88%E7%B5%82%E6%AD%A2%E6%99%82%E7%94%A2%E7%94%9F%E4%BA%82%E7%A2%BC),是因為 ASCII 畫面含有空格、``|``、``-``、換行等特殊字元,在傳輸中斷或解碼錯誤所造成。因此將畫棋盤的動作改在使用者層級進行也能解決此問題。 #### 顯示當下的時間 > Commit [00a2c58](https://github.com/sysprog21/kxo/commit/00a2c5898c1f52dd9fd218d0f581583c7813afb4) 使用 `time(NULL)` 來取得現在的時間,再用 `localtime_r()` 轉換成當地時間的結構,同時保證 thread-safe。 > thread-safe:當多個執行緒(threads)同時使用同一段程式碼時,不會發生資料衝突或錯誤。 輸出結果如下: ``` Time: 2025-04-16 20:42:40 ```