---
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. 負數整數乘法問題