--- tags: Linux2020 --- # I09: kcalc contributed by < `YLowy` > [code explain](https://hackmd.io/@YLowy/S1cmeqBtw) 順便對學習 kernel 時做些額外筆記 ## 前期準備 確認 ubuntu 版本 ``` $ uname -r 5.4.0-52-generic ``` 載入 linux headers (版本要對) ``` $ dpkg -L linux-headers-5.4.0-52-generic | grep "/lib/modules" /lib/modules /lib/modules/5.4.0-52-generic /lib/modules/5.4.0-52-generic/build ``` ## 取得 kcalc-fixed 程式碼並測試 順利 make 之後載入該 .ko 核心檔案 ``` $ sudo insmod calc.ko ``` 在核心所吐出訊息可看到其註冊成功 ``` $ dmesg [ 224.902852] calc: Initializing the module [ 224.902854] calc: registered correctly with major number 237 [ 224.902860] calc: device class registered correctly [ 224.902955] calc: device class created correctly ``` 在 /dev 目錄下可以觀察到存在 cal 檔案,這也是程式碼中所註冊的名稱 ``` /dev$ ls -al | grep calc crw------- 1 root root 237, 0 十一 9 23:46 calc ``` 為了方便測試更改讀寫權限,以方便在 user space 做讀寫 ``` /dev$ sudo chmod 0666 calc ``` ``` dev$ ls -al | grep calc crw-rw-rw- 1 root root 237, 0 十一 9 23:46 calc ``` 對該 calc 做寫入,這也是呼叫 write API 方式之一 ``` echo -ne "3*5\0" > /dev/calc ``` 此時可以對 calc 檔案做讀取,可以發現內已經有表達輸出式 ``` $ cat calc 64424509440 ``` 對應到 kernel 輸出訊息 ``` $ dmesg ``` ``` [ 749.002247] calc: Received 4 -> 3*5 [ 749.002262] calc: Result: 64424509440 [ 749.002270] calc: Device successfully closed [ 796.015508] calc: size: 12 result: 64424509440 [ 796.015548] calc: Device successfully closed ``` 卸載檔案 ``` $ sudo rmmod calc ` [ 1042.867565] kauditd_printk_skb: 3153 callbacks suppressed [ 1042.867568] audit: type=1400 audit(1604937605.718:3247): apparmor="DENIED" operation="capable" profile="/usr/sbin/cups-browsed" pid=4191 comm="cups-browsed" capability=23 capname="sys_nice" [ 1340.545745] calc: Goodbye! ``` --- ## 對於 kcalc-fixed 所做之資測 (benchforKcalc) 為了檢驗數值正確性,故需要對kcalc-fixed 做資測 [benchforKcalc](https://github.com/YLowy/benchforKcalc) 是為了比較 MathEx 以及 kcalc-fixed 所做測驗 ### benchforKcalc 解釋 ::: danger 1. 此程式碼必須在 calc 載入 kernel 情況下執行 2. 必須在其父目錄底下同時存在 MathEx 以及 kcalc-fixed 3. 會對 kcalc-fixed 做極小更動,以下講解 ::: ### 執行資測程式碼 make 之後生成的檔案 bench 會比較原本寫在裡面的資測內容做比對,進而判斷其正確性 ``` ~/linux2020/benchforkalc-fixed$ ./bench ---Start Bench--- BENCH [PASS] 10+1.1 MathEx: 11.10000 kcalc-fixed: 11.10000 BENCH [PASS] 10*1.1 MathEx: 11.00000 kcalc-fixed: 11.00000 BENCH [X] 10/1.1 MathEx: 9.09091 kcalc-fixed: 9.00000 BENCH [PASS] 10-1.1 MathEx: 8.90000 kcalc-fixed: 8.90000 BENCH [X] 1.1*1.1 MathEx: 1.21000 kcalc-fixed: 42949672.00000 ---End Bench--- ``` ### 對於 bench 程式碼解釋 ```c=1 #include "../MathEx/expression.h" #include "../MathEx/expression.c" #include "../kcalc-fixed/fixed-point.h" ``` `test_benchmark` 為接受輸入字串並比較之程式 ```c=37 static void test_benchmark(const char *s){ .. ... //此處計算 MathEx 輸出 for (long i = 0; i < N; i++) { result = expr_eval(e); } ... //此處計算 kcalc-fixed 輸出 kresult.data = write(fp,s,strlen(s)); float kout = eval(kresult); ... } ``` 只需要對 main 裡寫入測試內容即可 ```c=65 int main() { fp = open("/dev/calc",02); if(fp < 0){ exit(1); } printf("---Start Bench---\n"); /*--- write bench test code ---*/ test_benchmark("10+1.1"); test_benchmark("10*1.1"); test_benchmark("10/1.1"); test_benchmark("10-1.1"); test_benchmark("1.1*1.1"); /*------------------------------*/ printf("---End Bench---\n"); close(fp); return 0; } ``` ### 對 calc-fixed 程式碼之修改 原先對該 system call API 的 write 中該函式會回傳 len 也就是該輸入表達式長度,而現在我們目標為對呼叫該 system call 時可以取得該輸出表達式,以方便讓我們在資測時期做 eval 函式 ```c=86 static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) { memset(message, 0, sizeof(char) * BUFF_SIZE); if (len >= BUFF_SIZE) { pr_alert("Expression too long"); return 0; } copy_from_user(message, buffer, len); pr_info("Received %ld -> %s\n", len, message); calc(); return len; } ``` 將 len 更改成為 result,如下 : ```c=86 static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) { memset(message, 0, sizeof(char) * BUFF_SIZE); if (len >= BUFF_SIZE) { pr_alert("Expression too long"); return 0; } copy_from_user(message, buffer, len); pr_info("Received %ld -> %s\n", len, message); calc(); return result; } ``` --- ## 擴充數值運算器功能 新增 sqrt($\sqrt{}{}$), Sigma ($\Sigma$), Pi ($\pi$) ### Sqrt ### Sigma ### Pi --- ## 錯誤修正 ### mul 給定程式碼中對於乘法少了小數與小樹之間的相乘(也就是缺少 +bd ) ```c=248 static uint64_t mult(uint64_t a, uint64_t b) { fixedp fa = {.data = a}, fb = {.data = b}; /* (a + b) * (c + d) = ac + ad + bc + bd */ fixedp result = {.inte = fa.inte * fb.inte}; uint64_t ad = (GET_NUM(a) >> 32) * GET_FRAC(b); uint64_t bc = GET_FRAC(a) * (GET_NUM(b) >> 32); return result.data + ad + bc; } ``` 更改成為 ```c=248 static uint64_t mult(uint64_t a, uint64_t b) { fixedp fa = {.data = a}, fb = {.data = b}; /* (a + b) * (c + d) = ac + ad + bc + bd */ fixedp result = {.inte = fa.inte * fb.inte}; uint64_t ad = (GET_NUM(a) >> 32) * GET_FRAC(b); uint64_t bc = GET_FRAC(a) * (GET_NUM(b) >> 32); uint64_t bd = GET_FRAC(a) * GET_FRAC(b); return result.data + ad + bc + (bd>>32); } ``` 但是這裡還是有錯,錯誤部分為兩點 1. overflow 問題 2. 負數整數乘法問題