contributed by < Charlie-Tsai1123
>
注意細節
在執行 sudo ./xo-user
若按下 ctrl + P 會暫停繪製棋盤的畫面但會出現以下錯誤
我先找到 ctrl + P 的程式碼位子,在 xo-user.c 中
XO_DEVICE_ATTR_FILE 是用來更改遊戲的狀態
從這段程式可以得知,buf[0] 是用來控制程式是否顯示棋盤 read 部份負責看現在的狀態 write 部份則更改現在狀態,從這部份我對修改這個錯誤沒有頭緒,所以轉向觀察輸出棋盤的程式 main.c
發現輸出棋盤的地方好像沒有加上 '\0' 這樣 printf 處理時不知道哪裡終結可能會出現亂碼,所以做了以下修改
main.c
xo-user.c
commit 0e1d9b4
commit 5377a9c
draw_buffer
參考 rota1001
從原本的 DRAWBUFFER_SIZE
可以得知 draw_buffer
紀錄了整個畫面的資訊,也因此從 kernel mode 傳到 user mode 會用很多資源 (66 bytes 以 4x4 為例),那要如何減少?每格狀態只會有三種 ('O', 'X', ' ') 但三不是 2 的冪,所以要用 2 個 bit 表示 (4種狀態),所以傳遞資訊只需要 BOARD_SIZE
BOARD_SIZE
2 bits (32 bits = 1 bytes 以 4x4 為例) 因此我將 draw_buffer
的型別改用 unsigned int
提供更多數學分析
然後從 main.c
draw_board
可以發現 draw_buffer
的更新是根據 table
,它紀錄了每個位子的符號
所以可以思考該如何把 O
, X
,
用三個狀態表示,我先列出他們三個的 Asquii Code:
O |
X |
|
---|---|---|
1001111 | 1011000 | 0100000 |
可以發現黃色標起來的地方剛好可以用 2 bits 代表不同狀態,只需要把那一格的 char
向右移動 4 格再 and 0b11 = 3 ,因此依序把值填入 draw_buffer
中
明明第三跟第四個 bit 就可以表示三個狀態了為什麼要用第五跟第六
O |
X |
|
---|---|---|
1001111 | 1011000 | 0100000 |
因為等等在 user mode 的 draw_board 不想用到 if else
draw_board
到 user mode仔細觀察剛剛用來代表 O
, X
,
的值,剛剛好分別對應 0, 1, 2 ,所以可以創見一個大小為 3 的陣列分別就把 0, 1, 2 對應到 O
, X
,
[to do] 測試是否真的效率較好
首先我看了 <並行程式設計:排程器原理> 中的 coro.c 程式碼,裡面使用了 setjmp 跟 longjmp 處理 coroutine
嘗試在 main.c 使用 setjmp.h 出現以下錯誤
根據 《The Linux Kernel Module Programming Guide》(lkmpg) 5.2
One point to keep in mind is the difference between library functions and system calls. Library functions are higher level, run completely in user space and provide a more convenient interface for the programmer to the functions that do the real work — system calls. System calls run in kernel mode on the user’s behalf and are provided by the kernel itself.
因為 setjmp/longjmp 是 glibc 提供的函式,設計給 user space 使用,依賴 user space 的 stack ,因為 kernel 沒有使用 glibc 所以無法使用
想法:
將不同演算法下棋也可以運作在 user space 並用 coroutine 在 AI1, AI2, draw_board 間輪流執行
Commit 166d6e4
首先要讓 user space 可以使用 mcts 跟 negamax 這兩個函式,先去翻了 lkmpg 並學習了 ioctl 的用法以及第六章提及的 character device driver
Device
device 分成兩種 character devices 跟 block devices
character devices | block devices |
---|---|
input or output many or as few bytest they like | input output through fix-sized buffer |
buffer for request 可以選擇順序(就像選擇更近的 sector 存取資料) |
修改 file_operations
file_operations 定義了各種 device 的函式
define magic number
magic number 可以辨別不同的 ioctl 指令來源,這裡想要讓 mcts
negamax_predict
讓使用者使用,所以需要用到兩組 magic number
完成 kxo_ioctl
進行四個步驟:
copy_from_user
int_to_table
mcts
或 negamax_predict
copy_to_user
修改 user space 程式
用 ioctl
傳入棋盤並回傳最佳下棋位子,接著再更新
空棋盤會是 0b10101010101010101010101010101010
每一格都是 10 因為代表
(可以看上面 mapping 的方法)
00
代表 O
) :01
代表 X
) :結果
上面是 kernel space 執行的,下面是 user space 執行的
[to do] 目前犯了個錯誤因為 user space 沒辦法使用 check_win (因為在 kernel )所以我是讓 mcts 跟 negamax 總共呼叫 16 次再開始新的一局,之後想要寫在 user space 判斷棋盤勝利
上面實做完後,目前是讓兩個 ai 更新完 table 後再執行 draw_board
那現在想要將這兩個函式跟 draw_board 還有 keyboard handler 改成使用 coroutine
MCTS 演算法分成三步(參考Monte Carlo Tree Search 解說)
ni 是右邊的數字代表模擬了幾次, wi 是贏得次數
數學分析呢?
xo-user.c
xo-user.c
用來控制並顯示來自 Linux kernel space 的圈圈叉叉(XO)遊戲,透過 /dev/kxo 與 /sys/class/kxo/kxo/kxo_state 與核心模組互動。
(/dev/kxo 內有棋盤的資訊、/sys/class/kxo/kxo/kxo_state 內有遊玩狀態的資訊)
static bool status_check(void)
main.c