# 2023q1 Homework3 (fibdrv)
contributed by < [p96114175](https://github.com/p96114175/fibdrv) >
> [題目](https://hackmd.io/@sysprog/linux2023-quiz3)
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
$ lscpu
架構: x86_64
CPU 作業模式: 32-bit, 64-bit
Byte Order: Little Endian
Address sizes: 39 bits physical, 48 bits virtual
CPU(s): 24
On-line CPU(s) list: 0-23
每核心執行緒數: 1
每通訊端核心數: 16
Socket(s): 1
NUMA 節點: 1
供應商識別號: GenuineIntel
CPU 家族: 6
型號: 151
Model name: 12th Gen Intel(R) Core(TM) i9-12900F
製程: 2
CPU MHz: 2400.000
CPU max MHz: 5100.0000
CPU min MHz: 800.0000
BogoMIPS: 4838.40
虛擬: VT-x
L1d 快取: 384 KiB
L1i 快取: 256 KiB
L2 快取: 10 MiB
```
## 開發紀錄
在執行 `make check` 遇到以下問題
```
$ make check
Git hooks are installed successfully.
cc -o client client.c
make -C /lib/modules/5.15.0-69-generic/build M=/media/huaxin/D磁碟區/NUKU/fibdrv modules
make[1]: 進入目錄「/usr/src/linux-headers-5.15.0-69-generic」
CC [M] /media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.o
/media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.c: In function ‘fib_sequence’:
/media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.c:30:5: warning: ISO C90 forbids variable length array ‘f’ [-Wvla]
30 | long long f[k + 2];
| ^~~~
MODPOST /media/huaxin/D磁碟區/NUKU/fibdrv/Module.symvers
CC [M] /media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.mod.o
LD [M] /media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.ko
BTF [M] /media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.ko
Skipping BTF generation for /media/huaxin/D磁碟區/NUKU/fibdrv/fibdrv.ko due to unavailability of vmlinux
make[1]: 離開目錄「/usr/src/linux-headers-5.15.0-69-generic」
make unload
make[1]: 進入目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo rmmod fibdrv || true >/dev/null
rmmod: ERROR: Module fibdrv is not currently loaded
make[1]: 離開目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
make load
make[1]: 進入目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo insmod fibdrv.ko
make[1]: 離開目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo ./client > out
make unload
make[1]: 進入目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo rmmod fibdrv || true >/dev/null
make[1]: 離開目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
```
我們可以關閉 Secure Boot,該部分參考至 [willwillhi1](https://hackmd.io/@willwillhi/linux2023q1-fibdrv)
```
$ sudo mokutil --disable-validation
```
後續我再次執行
```
$ make check
make -C /lib/modules/5.15.0-69-generic/build M=/media/huaxin/D磁碟區/NUKU/fibdrv modules
make[1]: 進入目錄「/usr/src/linux-headers-5.15.0-69-generic」
make[1]: 離開目錄「/usr/src/linux-headers-5.15.0-69-generic」
make unload
make[1]: 進入目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo rmmod fibdrv || true >/dev/null
rmmod: ERROR: Module fibdrv is not currently loaded
make[1]: 離開目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
make load
make[1]: 進入目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo insmod fibdrv.ko
make[1]: 離開目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo ./client > out
make unload
make[1]: 進入目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
sudo rmmod fibdrv || true >/dev/null
make[1]: 離開目錄「/media/huaxin/D磁碟區/NUKU/fibdrv」
Passed [-]
f(93) fail
input: 7540113804746346429
expected: 12200160415121876738
```
## 限定 CPU 給特定的程式使用
```
$ sudo nano /etc/default/grub
```
將 `GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=1"` 加入
![](https://i.imgur.com/AuCnt7N.png)
## fast-doubling 加速 Fibonacci 運算
### 原先 fib 的寫法
```c
static long long fib_sequence(long long k)
{
/* FIXME: C99 variable-length array (VLA) is not allowed in Linux kernel. */
long long f[k + 2];
f[0] = 0;
f[1] = 1;
for (int i = 2; i <= k; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[k];
}
```
參考了 [Matrix Difference Equation for Fibonacci Sequence](https://chunminchang.github.io/blog/post/matrix-difference-equation-for-fibonacci-sequence) 和 [Calculating Fibonacci Numbers by Fast Doubling](https://chunminchang.github.io/blog/post/calculating-fibonacci-numbers-by-fast-doubling) 如何進行加速運算,下方為參考公式
\begin{align*}
F_{2n+1}&=F^2_{n+1} + F^2_n \\
F_{2n}&=F_{n}\cdot(F_{n+1}+F_{n-1}) \\
&=F_{n}\cdot(F_{n+1}+(F_{n+1}-F_{n}) \\
&=F_{n}\cdot(2\cdot F_{n+1}-F_{n}) \\
\end{align*}
### Fast Doubling - recursive version
改良結果如下
```c
static long long fib_doubling(long long n)
{
if(n <= 2) return n ? 1 : 0;
long long k = n >> 1;
long long a = fib_doubling(k);
long long b = fib_doubling(k+1);
if (n % 2) return a*a+b*b;
return a * ((b<<1) - a);
}
```
### Fast Doubling - iterative version
改良結果如下
```c
static long long fib_interative(long long n)
{
uint8_t bits = 0;
for (uint64_t i = n ; i ; ++bits, i >>= 1);
long long a = 0; // F(0) = 0
long long b = 1; // F(1) = 1
for (uint64_t mask = 1 << (bits - 1) ; mask ; mask >>= 1) {
long long c = a * (2 * b - a); // F(2k) = F(k) * [ 2 * F(k+1) – F(k) ]
long long d = a * a + b * b; // F(2k+1) = F(k)^2 + F(k+1)^2
if (mask & n) { // n_j is odd: k = (n_j-1)/2 => n_j = 2k + 1
a = d; // F(n_j) = F(2k + 1)
b = c + d; // F(n_j + 1) = F(2k + 2) = F(2k) + F(2k + 1)
} else { // n_j is even: k = n_j/2 => n_j = 2k
a = c; // F(n_j) = F(2k)
b = d; // F(n_j + 1) = F(2k + 1)
}
}
return a;
}
```
### Fast Doubling with clz
首先,可以使用 clz / ctz 指令來計算 n 的二進制表示中最高位之前 0 的個數,再透過 `64 - __builtin_clzll(n)` 得到 n 的最高位數
在 Fibonacci 數列的計算過程中,我們需要從最高位開始遍歷 n 的二進制表示,因此需要先得到最高位需要的位數,然後用它來初始化 mask 變量:
```c
static long long fib_with_clz(long long n)
{
long long a = 0;
long long b = 1;
for (uint64_t mask = 1ull << 64 - __builtin_clzll(n) ; mask ; mask >>= 1) {
long long c = a * (2 * b - a);
long long d = a * a + b * b;
if (mask & n) {
a = d;
b = c + d;
} else {
a = c;
b = d;
}
}
return a;
}
```
## 核心模式的時間測量
在[教材](https://hackmd.io/@sysprog/linux2022-fibdrv#%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%BC%8F%E7%9A%84%E6%99%82%E9%96%93%E6%B8%AC%E9%87%8F)中提到,一個新的機制是跳脫 `jiffies` 而將計時機制建立在一個新的資料結構 `ktime_t` 上面。這個結構體的定義會隨架構有所不同。
教材也提供了[ktime 相關的 API](https://www.kernel.org/doc/html/latest/core-api/timekeeping.html) 讓我們可以來測量時間。並參考教材範例,更改我的 `fibdrv.c`
透過 fib_time_proxy() 函數來計算 Fibonacci 數列函數 fib_doubling() 的運行時間,並在 fib_read() 中調用此函數來返回計算出的 Fibonacci 數,同時在 fib_write() 中返回上次呼叫 fib_read() 的運行時間。
```c
static ktime_t kt;
static long long fib_time_proxy(long long k)
{
kt = ktime_get();
long long result = fib_interative_clz(k);
kt = ktime_sub(ktime_get(), kt);
return result;
}
static ssize_t fib_read(struct file *file,
char *buf,
size_t size,
loff_t *offset)
{
return (ssize_t) fib_time_proxy(*offset);
}
static ssize_t fib_write(struct file *file,
const char *buf,
size_t size,
loff_t *offset)
{
return ktime_to_ns(kt);
}
```
在 [C語言:使用clock_gettime計算程式碼的時間需求](https://starforcefield.wordpress.com/2012/08/06/c%E8%AA%9E%E8%A8%80%EF%BC%9A%E4%BD%BF%E7%94%A8clock_gettime%E8%A8%88%E7%AE%97%E7%A8%8B%E5%BC%8F%E7%A2%BC%E7%9A%84%E6%99%82%E9%96%93%E9%9C%80%E6%B1%82/)中有提到
> 一般來說,C語言的標準程式庫裡面提供的time或clock兩個函數就可以滿足大部分的計時需求了。但在某些場合下可能會需要更精確的計時函數,例如說,比較兩個跑超快函數的時間消耗差異,或者是比較兩個時間消耗差異不大的函數。如果真有這個需求,而且你使用GNU/Linux,那就必須請到clock_gettime函式了。
首先,設定函式庫在 `test.c`
```
#include <time.h>
```
`time.h` 已經定義出 `timespec`
* tv_sec 表示為秒(s)
* tv_nsec 表示為奈秒(ns)
```c
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
```
`time.h` 也涵蓋了 `clock_gettime` 函式的原型,在[clock_gettime(3) - Linux man page](https://linux.die.net/man/3/clock_gettime)有提到
> The clk_id argument is the identifier of the particular clock on which to act. A clock may be system-wide and hence visible for all processes, or per-process if it measures time only within a single process.
* 第二個參數則是放存時間的 `struct timespec`
```
int clock_gettime(clockid_t clk_id, struct timespec *tp);
```
我們再設計一個函式用於存取當前時間
```c
static inline long long get_ns()
{
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return t.tv_nsec + t.tv_sec;
}
```
以下為設計結果,
```c
int main()
{
long long sz, start, real_time, k_time;
char buf[1];
char write_buf[] = "testing writing";
int offset = 90;
int fd = open(FIB_DEV, O_RDWR);
if (fd < 0) {
perror("Failed to open character device");
exit(1);
}
write(fd, write_buf, strlen(write_buf));
for (int i = 0; i <= offset; i++) {
lseek(fd, i, SEEK_SET);
start = get_ns();
sz = read(fd, buf, 1);
real_time = get_ns() - start;
k_time = write(fd, write_buf, strlen(write_buf));
printf("%d %lld %lld %lld\n",i, real_time, k_time, real_time-k_time);
}
close(fd);
return 0;
}
```
![](https://i.imgur.com/iGaRYkR.png)
在該圖可以發現,依照花費的時間由小到大,如下
1. fib sequence
2. iterative
3. recursive
4. iterative clz
### 接著可以進行以下設定排除干擾效能分析的因素
抑制 `address space layout randomization (ASLR)`
```
sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
```
撰寫` perfomance.sh`,並執行
```
for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
do
echo performance > ${i}
done
```
便可以得到下面的結果
![](https://i.imgur.com/5cCpte7.png)
可以從上下圖比較發現,在我們排除干擾效能分析的因素後,依據花費時間少到多,依序為
1. iterative
2. iterative_clz
3. fib_sequence
4. recursive
我們可以發現 `iterative` 加上 `clz` 後並無明顯減少運算時間,此外,`recursive` 比原先的做法 `fib_sequence` 還需耗費更多時間進行計算。
## 學習指出針對大數運算的加速運算和縮減記憶體操作成本的舉措,紀錄你的認知和疑惑
### _bn
針對大數運算,宣告以下結構體
* `number` 為一個指標指向著儲存的數值,其型別為 `unsigned int*`
* `size` 表示該數的記憶體大小
* `sign` 表示該數的正或負
```c
/* number[size - 1] = msb, number[0] = lsb */
typedef struct _bn {
unsigned int *number;
unsigned int size;
int sign;
} bn;
```
### bn_to_string
* 首先計算出 `len`,其中 `(8 * sizeof(int) * src.size)` 在計算 `src` 結構體中 `number` 的總 `Byte` 數目,再來使用 `/3` 將 `number` 轉換成十進制 `string` 所需要的 `characters` 數目,`2 + src.sign` 的 2 是為了預留 `\0` 的空間,`src.sign` 則是考慮到,若為負數,在轉換成 `string` 時,需要加上一個負號。
* 定義一個 `char` 型指標 s ,使用 `kmalloc` 分配記憶體空間,其中 `kmalloc` 函數是一個在 `kernel` 模塊中使用的動態內存分配函數
* 使用 `memset` 將 s 指標所指向的記憶體空間的前 `len-1` 個 byte 都填為 '0'
```c
/*
* output bn to decimal string
* Note: the returned string should be freed with kfree()
*/
char *bn_to_string(bn src)
{
// log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322
size_t len = (8 * sizeof(int) * src.size) / 3 + 2 + src.sign;
char *s = kmalloc(len, GFP_KERNEL);
char *p = s;
memset(s, '0', len - 1);
s[len - 1] = '\0';
for (int i = src.size - 1; i >= 0; i--) {
for (unsigned int d = 1U << 31; d; d >>= 1) {
/* binary -> decimal string */
int carry = !!(d & src.number[i]);
for (int j = len - 2; j >= 0; j--) {
s[j] += s[j] - '0' + carry; // double it
carry = (s[j] > '9');
if (carry)
s[j] -= 10;
}
}
}
// skip leading zero
while (p[0] == '0' && p[1] != '\0') {
p++;
}
if (src.sign)
*(--p) = '-';
memmove(s, p, strlen(p) + 1);
return s;
}
```
## 研讀 `Linux 效能分析的提示` ,在自己的電腦上運作GNU/Linux,並做好必要的設定和準備工作,從中理解為何不要在虛擬機器上進行實驗。
根據 [查看行程的 CPU affinity](https://hackmd.io/@sysprog/linux2023-fibdrv/%2F%40sysprog%2Flinux2023-fibdrv-c)
### 若想要查看指定行程的處理器親和性,可使用 `taskset` 加上 `-p` 參數再加上該行程的 PID(process ID):
```
$ taskset -p PID
```
於是我開啟了一個 `chrome` 的分頁,透過以下方式獲得 process 的 PID
```
$ pidof chrome
```
然後得到以下結果,由高到低列出,換句話說就是最近到最舊
```
8638 8602 8504 8503 8487 8480 8466 8465 8442 8438 8437 8420
```
接著我使用了最近的 PID `8638` ,並執行以下指令
```
$ taskset -p 8638
```
得到以下輸出,就是 `8638` 的`處理器親和性`
![](https://i.imgur.com/zgLaj8O.png)
接著我們加上 `-c` 參數,讓 taskset 直接輸出 `CPU 核的列表`:
```
$ taskset -cp 8638
```
得到以下輸出
![](https://i.imgur.com/5YbLCeQ.png)
### 將行程固定在特定的 CPU 中執行
在教材中有提到,可使用以下兩個方法完成,兩種方式效果一致
第一種,COREMASK 就是指定的十六進位 core mask,PID 則為行程的 ID。
```
$ taskset -p COREMASK PID
```
第二種,使用 `-c` 參數以 CPU 的核心列表來指定
```
$ taskset -cp CORELIST PID
```
後來我選用了第二個方法來實現,我將行程的PID `8638` 固定在第一個和第三個 CPU 核,以下為我的輸入
```
$ taskset -cp 1,3 8638
```
輸出為:
![](https://i.imgur.com/TZ6WTFe.png)
### 限定 CPU 給特定的程式使用
流程可分為
* 第一步,讓特定的 CPU 核在開機時就被保留下來。
下方為教材提到的範例,讓在開機時,第0個和第1個 `CPU` 保留下來
```
isolcpus=0,1
```
* 第二步,使用 `taskset` 命令將這兩個 CPU 核指定給要執行的程式使用即可。
### 排除干擾效能分析的因素
該部分主要以環境設定為主,將可能對效能產生影響的因素盡可能避免掉,分別為
* 抑制 address space layout randomization (ASLR)
* 設定 scaling_governor 為 performance。
* 針對 Intel 處理器,關閉 turbo mode
* 關閉 [SMT](https://en.wikipedia.org/wiki/Simultaneous_multithreading) (Intel 稱它為 [Hyper-Threading](https://en.wikipedia.org/wiki/Hyper-threading))
## 注意到 fibdrv.c 存在著 DEFINE_MUTEX, mutex_trylock, mutex_init, mutex_unlock, mutex_destroy 等字樣,什麼場景中會需要呢?
**DEFINE_MUTEX**
在`fibdrv.c` 這一文件中,我觀察到
```c
static DEFINE_MUTEX(fib_mutex);
```
使用 `DEFINE_MUTEX` 可用於定義出我們的 mutex
**mutex_trylock**
從字面上可以理解該字樣是常是獲取 lock ,於是我觀看教材 [POSIX Threads](https://hackmd.io/@sysprog/concurrency/https%3A%2F%2Fhackmd.io%2F%40sysprog%2Fposix-threads) 裡面提到
> If `pthread_mutex_trylock()` returns `EBUSY`, the lock is already locked. Otherwise, the calling thread becomes the owner of this lock.
接著再觀察 `fibdrv.c` 這一文件中這樣撰寫
```c
static int fib_open(struct inode *inode, struct file *file)
{
if (!mutex_trylock(&fib_mutex)) {
printk(KERN_ALERT "fibdrv is in use");
return -EBUSY;
}
return 0;
}
```
我們可以清楚的知道,當返回 `EBUSY` 時意味著,你的 lock 仍然被鎖著,你的 thread 並未取得 lock ,相反的,你的 thread 便會獲得 lock 並成為 lock 的擁有者。
**mutex_init**
在 [mutex_init](https://archive.kernel.org/oldlinux/htmldocs/kernel-locking/API-mutex-init.html) 有提到
> Initialize the mutex to unlocked state.
> It is not allowed to initialize an already locked mutex.
那接著再觀察 `fibdrv.c` 這一文件中,可知道正在對 mutex 進行初始化到未鎖狀態
```c
static int __init init_fib_dev(void)
{
int rc = 0;
mutex_init(&fib_mutex);
```
**mutex_unlock**
在 [mutex_unlock](https://archive.kernel.org/oldlinux/htmldocs/kernel-locking/API-mutex-unlock.html) 有提到
> Unlock a mutex that has been locked by this task previously.
> This function must not be used in interrupt context. Unlocking of a not locked mutex is not allowed.
接著再觀察 fibdrv.c 這一文件中這樣撰寫
```c
static int fib_release(struct inode *inode, struct file *file)
{
mutex_unlock(&fib_mutex);
return 0;
}
```
它將 `mutex_unlock` 應用在 `fib_release` 這一函式中,目的是在費伯納西釋放時,將 mutex 進行解鎖
**mutex_destroy**
在 fibdrv.c 這一文件中,該處使用了 `mutex_destroy`
```c
static void __exit exit_fib_dev(void)
{
mutex_destroy(&fib_mutex);
device_destroy(fib_class, fib_dev);
class_destroy(fib_class);
cdev_del(fib_cdev);
unregister_chrdev_region(fib_dev, 1);
}
```
從字樣中可判斷出,這是在離開 fib 時所調用的函式,裡頭使用了 `mutex_destroy` 目的在於將 mutex 進行釋放
## 撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。
#### 參考資料
[POSIX Thread](https://en.wikipedia.org/wiki/POSIX_Threads)
搭配閱讀〈[並行和多執行緒程式設計](https://hackmd.io/@sysprog/concurrency)〉
我會做個實驗組和對照組,分別為有用到 mutex 和沒有使用到 mutex 的
### 有用到 mutex(並未註解掉原先程式有用到 mutex 的部分)
我參考了 [POSIX Thread](https://en.wikipedia.org/wiki/POSIX_Threads) 設計一個名為 `thread.c` 的檔案並使用兩個執行序去執行我的函數 `Run_fibonacci`
```c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_THREADS 2
#define FIB_DEV "/dev/fibonacci"
void Run_fibonacci()
{
long long sz;
char buf[128];
char write_buf[] = "testing writing";
int offset = 92;
int fd = open(FIB_DEV, O_RDWR);
if (fd < 0) {
perror("Failed to open character device");
exit(1);
}
write(fd, write_buf, strlen(write_buf));
for (int i = 0; i <= offset; i++) {
lseek(fd, i, SEEK_SET);
sz = read(fd, buf, 1);
printf("reading from " FIB_DEV " at ofset %d, returned the sequence ""%lld.\n", i, sz);
}
close(fd);
}
int main() {
pthread_t threads[NUM_THREADS];
int i;
int result_code;
//create all threads one by one
for (i = 0; i < NUM_THREADS; i++) {
printf("IN MAIN: Creating thread %d.\n", i);
result_code = pthread_create(&threads[i], NULL, (void *(*)(void *))Run_fibonacci, NULL);
assert(!result_code);
}
printf("IN MAIN: All threads are created.\n");
//wait for each thread to complete
for (i = 0; i < NUM_THREADS; i++) {
result_code = pthread_join(threads[i], NULL);
assert(!result_code);
printf("IN MAIN: Thread %d has ended.\n", i);
}
printf("MAIN program has ended.\n");
return 0;
}
```
在我執行 `fib_thread.sh` 後
```
make
sudo rmmod fibdrv
sudo insmod fibdrv.ko
gcc thread.c -pthread -o thread
sudo ./thread > fib_mutex.txt
```
在 `fib_mutex.txt` 得到以下結果
```
IN MAIN: Creating thread 0.
IN MAIN: Creating thread 1.
reading from /dev/fibonacci at ofset 0, returned the sequence 0.
reading from /dev/fibonacci at ofset 1, returned the sequence 1.
reading from /dev/fibonacci at ofset 2, returned the sequence 1.
reading from /dev/fibonacci at ofset 3, returned the sequence 2.
reading from /dev/fibonacci at ofset 4, returned the sequence 3.
reading from /dev/fibonacci at ofset 5, returned the sequence 5.
reading from /dev/fibonacci at ofset 6, returned the sequence 8.
reading from /dev/fibonacci at ofset 7, returned the sequence 13.
reading from /dev/fibonacci at ofset 8, returned the sequence 21.
reading from /dev/fibonacci at ofset 9, returned the sequence 34.
reading from /dev/fibonacci at ofset 10, returned the sequence 55.
reading from /dev/fibonacci at ofset 11, returned the sequence 89.
reading from /dev/fibonacci at ofset 12, returned the sequence 144.
reading from /dev/fibonacci at ofset 13, returned the sequence 233.
reading from /dev/fibonacci at ofset 14, returned the sequence 377.
reading from /dev/fibonacci at ofset 15, returned the sequence 610.
reading from /dev/fibonacci at ofset 16, returned the sequence 987.
reading from /dev/fibonacci at ofset 17, returned the sequence 1597.
reading from /dev/fibonacci at ofset 18, returned the sequence 2584.
reading from /dev/fibonacci at ofset 19, returned the sequence 4181.
reading from /dev/fibonacci at ofset 20, returned the sequence 6765.
reading from /dev/fibonacci at ofset 21, returned the sequence 10946.
reading from /dev/fibonacci at ofset 22, returned the sequence 17711.
IN MAIN: All threads are created.
reading from /dev/fibonacci at ofset 23, returned the sequence 28657.
reading from /dev/fibonacci at ofset 24, returned the sequence 46368.
reading from /dev/fibonacci at ofset 25, returned the sequence 75025.
reading from /dev/fibonacci at ofset 26, returned the sequence 121393.
reading from /dev/fibonacci at ofset 27, returned the sequence 196418.
reading from /dev/fibonacci at ofset 28, returned the sequence 317811.
reading from /dev/fibonacci at ofset 29, returned the sequence 514229.
reading from /dev/fibonacci at ofset 30, returned the sequence 832040.
reading from /dev/fibonacci at ofset 31, returned the sequence 1346269.
reading from /dev/fibonacci at ofset 32, returned the sequence 2178309.
reading from /dev/fibonacci at ofset 33, returned the sequence 3524578.
reading from /dev/fibonacci at ofset 34, returned the sequence 5702887.
reading from /dev/fibonacci at ofset 35, returned the sequence 9227465.
reading from /dev/fibonacci at ofset 36, returned the sequence 14930352.
reading from /dev/fibonacci at ofset 37, returned the sequence 24157817.
reading from /dev/fibonacci at ofset 38, returned the sequence 39088169.
reading from /dev/fibonacci at ofset 39, returned the sequence 63245986.
reading from /dev/fibonacci at ofset 40, returned the sequence 102334155.
reading from /dev/fibonacci at ofset 41, returned the sequence 165580141.
reading from /dev/fibonacci at ofset 42, returned the sequence 267914296.
reading from /dev/fibonacci at ofset 43, returned the sequence 433494437.
reading from /dev/fibonacci at ofset 44, returned the sequence 701408733.
reading from /dev/fibonacci at ofset 45, returned the sequence 1134903170.
reading from /dev/fibonacci at ofset 46, returned the sequence 1836311903.
reading from /dev/fibonacci at ofset 47, returned the sequence 2971215073.
reading from /dev/fibonacci at ofset 48, returned the sequence 4807526976.
reading from /dev/fibonacci at ofset 49, returned the sequence 7778742049.
reading from /dev/fibonacci at ofset 50, returned the sequence 12586269025.
reading from /dev/fibonacci at ofset 51, returned the sequence 20365011074.
reading from /dev/fibonacci at ofset 52, returned the sequence 32951280099.
reading from /dev/fibonacci at ofset 53, returned the sequence 53316291173.
reading from /dev/fibonacci at ofset 54, returned the sequence 86267571272.
reading from /dev/fibonacci at ofset 55, returned the sequence 139583862445.
reading from /dev/fibonacci at ofset 56, returned the sequence 225851433717.
reading from /dev/fibonacci at ofset 57, returned the sequence 365435296162.
reading from /dev/fibonacci at ofset 58, returned the sequence 591286729879.
reading from /dev/fibonacci at ofset 59, returned the sequence 956722026041.
reading from /dev/fibonacci at ofset 60, returned the sequence 1548008755920.
reading from /dev/fibonacci at ofset 61, returned the sequence 2504730781961.
reading from /dev/fibonacci at ofset 62, returned the sequence 4052739537881.
reading from /dev/fibonacci at ofset 63, returned the sequence 6557470319842.
reading from /dev/fibonacci at ofset 64, returned the sequence 10610209857723.
reading from /dev/fibonacci at ofset 65, returned the sequence 17167680177565.
reading from /dev/fibonacci at ofset 66, returned the sequence 27777890035288.
reading from /dev/fibonacci at ofset 67, returned the sequence 44945570212853.
reading from /dev/fibonacci at ofset 68, returned the sequence 72723460248141.
reading from /dev/fibonacci at ofset 69, returned the sequence 117669030460994.
reading from /dev/fibonacci at ofset 70, returned the sequence 190392490709135.
reading from /dev/fibonacci at ofset 71, returned the sequence 308061521170129.
reading from /dev/fibonacci at ofset 72, returned the sequence 498454011879264.
reading from /dev/fibonacci at ofset 73, returned the sequence 806515533049393.
reading from /dev/fibonacci at ofset 74, returned the sequence 1304969544928657.
reading from /dev/fibonacci at ofset 75, returned the sequence 2111485077978050.
reading from /dev/fibonacci at ofset 76, returned the sequence 3416454622906707.
reading from /dev/fibonacci at ofset 77, returned the sequence 5527939700884757.
reading from /dev/fibonacci at ofset 78, returned the sequence 8944394323791464.
reading from /dev/fibonacci at ofset 79, returned the sequence 14472334024676221.
reading from /dev/fibonacci at ofset 80, returned the sequence 23416728348467685.
reading from /dev/fibonacci at ofset 81, returned the sequence 37889062373143906.
reading from /dev/fibonacci at ofset 82, returned the sequence 61305790721611591.
reading from /dev/fibonacci at ofset 83, returned the sequence 99194853094755497.
reading from /dev/fibonacci at ofset 84, returned the sequence 160500643816367088.
reading from /dev/fibonacci at ofset 85, returned the sequence 259695496911122585.
reading from /dev/fibonacci at ofset 86, returned the sequence 420196140727489673.
reading from /dev/fibonacci at ofset 87, returned the sequence 679891637638612258.
reading from /dev/fibonacci at ofset 88, returned the sequence 1100087778366101931.
reading from /dev/fibonacci at ofset 89, returned the sequence 1779979416004714189.
reading from /dev/fibonacci at ofset 90, returned the sequence 2880067194370816120.
reading from /dev/fibonacci at ofset 91, returned the sequence 4660046610375530309.
reading from /dev/fibonacci at ofset 92, returned the sequence 7540113804746346429.
dev/fibonacci at ofset 41, returned the sequence 165580141.
reading from /dev/fibonacci at ofset 42, returned the sequence 267914296.
reading from /dev/fibonacci at ofset 43, returned the sequence 433494437.
reading from /dev/fibonacci at ofset 44, returned the sequence 701408733.
reading from /dev/fibonacci at ofset 45, returned the sequence 1134903170.
reading from /dev/fibonacci at ofset 46, returned the sequence 1836311903.
reading from /dev/fibonacci at ofset 47, returned the sequence 2971215073.
reading from /dev/fibonacci at ofset 48, returned the sequence 4807526976.
reading from /dev/fibonacci at ofset 49, returned the sequence 7778742049.
reading from /dev/fibonacci at ofset 50, returned the sequence 12586269025.
reading from /dev/fibonacci at ofset 51, returned the sequence 20365011074.
reading from /dev/fibonacci at ofset 52, returned the sequence 32951280099.
reading from /dev/fibonacci at ofset 53, returned the sequence 53316291173.
reading from /dev/fibonacci at ofset 54, returned the sequence 86267571272.
reading from /dev/fibonacci at ofset 55, returned the sequence 139583862445.
reading from /dev/fibonacci at ofset 56, ret
```
### 未用到 mutex(註解掉原先程式有用到 mutex 的部分)
我已將 `fibdrv.c` 有關 `mutex` 的關鍵字註解掉
```c
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("National Cheng Kung University, Taiwan");
MODULE_DESCRIPTION("Fibonacci engine driver");
MODULE_VERSION("0.1");
#define DEV_FIBONACCI_NAME "fibonacci"
/* MAX_LENGTH is set to 92 because
* ssize_t can't fit the number > 92
*/
#define MAX_LENGTH 92
static dev_t fib_dev = 0;
static struct cdev *fib_cdev;
static struct class *fib_class;
// static DEFINE_MUTEX(fib_mutex);
static long long fib_sequence(long long k)
{
/* FIXME: C99 variable-length array (VLA) is not allowed in Linux kernel. */
long long f[k + 2];
f[0] = 0;
f[1] = 1;
for (int i = 2; i <= k; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[k];
}
static long long fib_doubling(long long n)
{
if(n <= 2) return n ? 1 : 0;
long long k = n >> 1;
long long a = fib_doubling(k);
long long b = fib_doubling(k+1);
if (n % 2) return a*a+b*b;
return a * ((b<<1) - a);
}
static long long fib_interative(long long n)
{
uint8_t bits = 0;
for (uint64_t i = n ; i ; ++bits, i >>= 1);
long long a = 0; // F(0) = 0
long long b = 1; // F(1) = 1
for (uint64_t mask = 1 << (bits - 1) ; mask ; mask >>= 1) {
long long c = a * (2 * b - a); // F(2k) = F(k) * [ 2 * F(k+1) – F(k) ]
long long d = a * a + b * b; // F(2k+1) = F(k)^2 + F(k+1)^2
if (mask & n) { // n_j is odd: k = (n_j-1)/2 => n_j = 2k + 1
a = d; // F(n_j) = F(2k + 1)
b = c + d; // F(n_j + 1) = F(2k + 2) = F(2k) + F(2k + 1)
} else { // n_j is even: k = n_j/2 => n_j = 2k
a = c; // F(n_j) = F(2k)
b = d; // F(n_j + 1) = F(2k + 1)
}
}
return a;
}
static int fib_open(struct inode *inode, struct file *file)
{
// if (!mutex_trylock(&fib_mutex)) {
// printk(KERN_ALERT "fibdrv is in use");
// return -EBUSY;
// }
return 0;
}
static int fib_release(struct inode *inode, struct file *file)
{
// mutex_unlock(&fib_mutex);
return 0;
}
/* calculate the fibonacci number at given offset */
static ssize_t fib_read(struct file *file,
char *buf,
size_t size,
loff_t *offset)
{
return (ssize_t) fib_sequence(*offset);
}
/* write operation is skipped */
static ssize_t fib_write(struct file *file,
const char *buf,
size_t size,
loff_t *offset)
{
return 1;
}
static loff_t fib_device_lseek(struct file *file, loff_t offset, int orig)
{
loff_t new_pos = 0;
switch (orig) {
case 0: /* SEEK_SET: */
new_pos = offset;
break;
case 1: /* SEEK_CUR: */
new_pos = file->f_pos + offset;
break;
case 2: /* SEEK_END: */
new_pos = MAX_LENGTH - offset;
break;
}
if (new_pos > MAX_LENGTH)
new_pos = MAX_LENGTH; // max case
if (new_pos < 0)
new_pos = 0; // min case
file->f_pos = new_pos; // This is what we'll use now
return new_pos;
}
const struct file_operations fib_fops = {
.owner = THIS_MODULE,
.read = fib_read,
.write = fib_write,
.open = fib_open,
.release = fib_release,
.llseek = fib_device_lseek,
};
static int __init init_fib_dev(void)
{
int rc = 0;
// mutex_init(&fib_mutex);
// Let's register the device
// This will dynamically allocate the major number
rc = alloc_chrdev_region(&fib_dev, 0, 1, DEV_FIBONACCI_NAME);
if (rc < 0) {
printk(KERN_ALERT
"Failed to register the fibonacci char device. rc = %i",
rc);
return rc;
}
fib_cdev = cdev_alloc();
if (fib_cdev == NULL) {
printk(KERN_ALERT "Failed to alloc cdev");
rc = -1;
goto failed_cdev;
}
fib_cdev->ops = &fib_fops;
rc = cdev_add(fib_cdev, fib_dev, 1);
if (rc < 0) {
printk(KERN_ALERT "Failed to add cdev");
rc = -2;
goto failed_cdev;
}
fib_class = class_create(THIS_MODULE, DEV_FIBONACCI_NAME);
if (!fib_class) {
printk(KERN_ALERT "Failed to create device class");
rc = -3;
goto failed_class_create;
}
if (!device_create(fib_class, NULL, fib_dev, NULL, DEV_FIBONACCI_NAME)) {
printk(KERN_ALERT "Failed to create device");
rc = -4;
goto failed_device_create;
}
return rc;
failed_device_create:
class_destroy(fib_class);
failed_class_create:
cdev_del(fib_cdev);
failed_cdev:
unregister_chrdev_region(fib_dev, 1);
return rc;
}
static void __exit exit_fib_dev(void)
{
// mutex_destroy(&fib_mutex);
device_destroy(fib_class, fib_dev);
class_destroy(fib_class);
cdev_del(fib_cdev);
unregister_chrdev_region(fib_dev, 1);
}
module_init(init_fib_dev);
module_exit(exit_fib_dev);
```
執行 `fib_thread.sh`
```
make
sudo rmmod fibdrv
sudo insmod fibdrv.ko
gcc thread.c -pthread -o thread
sudo ./thread > fib_without_mutex.txt
```
在 `fib_without_mutex.txt` 得到以下結果
```
IN MAIN: Creating thread 0.
IN MAIN: Creating thread 1.
reading from /dev/fibonacci at ofset 0, returned the sequence 0.
reading from /dev/fibonacci at ofset 1, returned the sequence 1.
reading from /dev/fibonacci at ofset 2, returned the sequence 1.
reading from /dev/fibonacci at ofset 3, returned the sequence 2.
reading from /dev/fibonacci at ofset 4, returned the sequence 3.
reading from /dev/fibonacci at ofset 5, returned the sequence 5.
reading from /dev/fibonacci at ofset 6, returned the sequence 8.
reading from /dev/fibonacci at ofset 7, returned the sequence 13.
reading from /dev/fibonacci at ofset 8, returned the sequence 21.
reading from /dev/fibonacci at ofset 9, returned the sequence 34.
reading from /dev/fibonacci at ofset 10, returned the sequence 55.
reading from /dev/fibonacci at ofset 11, returned the sequence 89.
reading from /dev/fibonacci at ofset 12, returned the sequence 144.
reading from /dev/fibonacci at ofset 13, returned the sequence 233.
reading from /dev/fibonacci at ofset 14, returned the sequence 377.
reading from /dev/fibonacci at ofset 15, returned the sequence 610.
reading from /dev/fibonacci at ofset 16, returned the sequence 987.
reading from /dev/fibonacci at ofset 17, returned the sequence 1597.
reading from /dev/fibonacci at ofset 18, returned the sequence 2584.
reading from /dev/fibonacci at ofset 19, returned the sequence 4181.
reading from /dev/fibonacci at ofset 20, returned the sequence 6765.
IN MAIN: All threads are created.
reading from /dev/fibonacci at ofset 21, returned the sequence 10946.
reading from /dev/fibonacci at ofset 22, returned the sequence 17711.
reading from /dev/fibonacci at ofset 23, returned the sequence 28657.
reading from /dev/fibonacci at ofset 24, returned the sequence 46368.
reading from /dev/fibonacci at ofset 25, returned the sequence 75025.
reading from /dev/fibonacci at ofset 26, returned the sequence 121393.
reading from /dev/fibonacci at ofset 27, returned the sequence 196418.
reading from /dev/fibonacci at ofset 28, returned the sequence 317811.
reading from /dev/fibonacci at ofset 29, returned the sequence 514229.
reading from /dev/fibonacci at ofset 30, returned the sequence 832040.
reading from /dev/fibonacci at ofset 31, returned the sequence 1346269.
reading from /dev/fibonacci at ofset 32, returned the sequence 2178309.
reading from /dev/fibonacci at ofset 33, returned the sequence 3524578.
reading from /dev/fibonacci at ofset 34, returned the sequence 5702887.
reading from /dev/fibonacci at ofset 35, returned the sequence 9227465.
reading from /dev/fibonacci at ofset 36, returned the sequence 14930352.
reading from /dev/fibonacci at ofset 37, returned the sequence 24157817.
reading from /dev/fibonacci at ofset 0, returned the sequence 0.
reading from /dev/fibonacci at ofset 1, returned the sequence 1.
reading from /dev/fibonacci at ofset 2, returned the sequence 1.
reading from /dev/fibonacci at ofset 3, returned the sequence 2.
reading from /dev/fibonacci at ofset 4, returned the sequence 3.
reading from /dev/fibonacci at ofset 5, returned the sequence 5.
reading from /dev/fibonacci at ofset 38, returned the sequence 39088169.
reading from /dev/fibonacci at ofset 6, returned the sequence 8.
reading from /dev/fibonacci at ofset 39, returned the sequence 63245986.
reading from /dev/fibonacci at ofset 7, returned the sequence 13.
reading from /dev/fibonacci at ofset 40, returned the sequence 102334155.
reading from /dev/fibonacci at ofset 8, returned the sequence 21.
reading from /dev/fibonacci at ofset 41, returned the sequence 165580141.
reading from /dev/fibonacci at ofset 9, returned the sequence 34.
reading from /dev/fibonacci at ofset 42, returned the sequence 267914296.
reading from /dev/fibonacci at ofset 10, returned the sequence 55.
reading from /dev/fibonacci at ofset 43, returned the sequence 433494437.
reading from /dev/fibonacci at ofset 11, returned the sequence 89.
reading from /dev/fibonacci at ofset 44, returned the sequence 701408733.
reading from /dev/fibonacci at ofset 12, returned the sequence 144.
reading from /dev/fibonacci at ofset 45, returned the sequence 1134903170.
reading from /dev/fibonacci at ofset 46, returned the sequence 1836311903.
reading from /dev/fibonacci at ofset 47, returned the sequence 2971215073.
reading from /dev/fibonacci at ofset 48, returned the sequence 4807526976.
reading from /dev/fibonacci at ofset 13, returned the sequence 233.
reading from /dev/fibonacci at ofset 49, returned the sequence 7778742049.
reading from /dev/fibonacci at ofset 50, returned the sequence 12586269025.
reading from /dev/fibonacci at ofset 14, returned the sequence 377.
reading from /dev/fibonacci at ofset 51, returned the sequence 20365011074.
reading from /dev/fibonacci at ofset 15, returned the sequence 610.
reading from /dev/fibonacci at ofset 52, returned the sequence 32951280099.
reading from /dev/fibonacci at ofset 16, returned the sequence 987.
reading from /dev/fibonacci at ofset 53, returned the sequence 53316291173.
reading from /dev/fibonacci at ofset 17, returned the sequence 1597.
reading from /dev/fibonacci at ofset 54, returned the sequence 86267571272.
reading from /dev/fibonacci at ofset 55, returned the sequence 139583862445.
reading from /dev/fibonacci at ofset 56, returned the sequence 225851433717.
reading from /dev/fibonacci at ofset 57, returned the sequence 365435296162.
reading from /dev/fibonacci at ofset 18, returned the sequence 2584.
reading from /dev/fibonacci at ofset 58, returned the sequence 591286729879.
reading from /dev/fibonacci at ofset 19, returned the sequence 4181.
reading from /dev/fibonacci at ofset 59, returned the sequence 956722026041.
reading from /dev/fibonacci at ofset 20, returned the sequence 6765.
reading from /dev/fibonacci at ofset 60, returned the sequence 1548008755920.
reading from /dev/fibonacci at ofset 21, returned the sequence 10946.
reading from /dev/fibonacci at ofset 61, returned the sequence 2504730781961.
reading from /dev/fibonacci at ofset 22, returned the sequence 17711.
reading from /dev/fibonacci at ofset 62, returned the sequence 4052739537881.
reading from /dev/fibonacci at ofset 23, returned the sequence 28657.
reading from /dev/fibonacci at ofset 63, returned the sequence 6557470319842.
reading from /dev/fibonacci at ofset 64, returned the sequence 10610209857723.
reading from /dev/fibonacci at ofset 65, returned the sequence 17167680177565.
reading from /dev/fibonacci at ofset 66, returned the sequence 27777890035288.
reading from /dev/fibonacci at ofset 24, returned the sequence 46368.
reading from /dev/fibonacci at ofset 67, returned the sequence 44945570212853.
reading from /dev/fibonacci at ofset 25, returned the sequence 75025.
reading from /dev/fibonacci at ofset 68, returned the sequence 72723460248141.
reading from /dev/fibonacci at ofset 26, returned the sequence 121393.
reading from /dev/fibonacci at ofset 69, returned the sequence 117669030460994.
reading from /dev/fibonacci at ofset 27, returned the sequence 196418.
reading from /dev/fibonacci at ofset 70, returned the sequence 190392490709135.
reading from /dev/fibonacci at ofset 28, returned the sequence 317811.
reading from /dev/fibonacci at ofset 71, returned the sequence 308061521170129.
reading from /dev/fibonacci at ofset 29, returned the sequence 514229.
reading from /dev/fibonacci at ofset 72, returned the sequence 498454011879264.
reading from /dev/fibonacci at ofset 73, returned the sequence 806515533049393.
reading from /dev/fibonacci at ofset 74, returned the sequence 1304969544928657.
reading from /dev/fibonacci at ofset 75, returned the sequence 2111485077978050.
reading from /dev/fibonacci at ofset 30, returned the sequence 832040.
reading from /dev/fibonacci at ofset 76, returned the sequence 3416454622906707.
reading from /dev/fibonacci at ofset 31, returned the sequence 1346269.
reading from /dev/fibonacci at ofset 77, returned the sequence 5527939700884757.
reading from /dev/fibonacci at ofset 32, returned the sequence 2178309.
reading from /dev/fibonacci at ofset 78, returned the sequence 8944394323791464.
reading from /dev/fibonacci at ofset 33, returned the sequence 3524578.
reading from /dev/fibonacci at ofset 34, returned the sequence 5702887.
reading from /dev/fibonacci at ofset 35, returned the sequence 9227465.
reading from /dev/fibonacci at ofset 36, returned the sequence 14930352.
reading from /dev/fibonacci at ofset 79, returned the sequence 14472334024676221.
reading from /dev/fibonacci at ofset 80, returned the sequence 23416728348467685.
reading from /dev/fibonacci at ofset 81, returned the sequence 37889062373143906.
reading from /dev/fibonacci at ofset 37, returned the sequence 24157817.
reading from /dev/fibonacci at ofset 82, returned the sequence 61305790721611591.
reading from /dev/fibonacci at ofset 38, returned the sequence 39088169.
reading from /dev/fibonacci at ofset 83, returned the sequence 99194853094755497.
reading from /dev/fibonacci at ofset 39, returned the sequence 63245986.
reading from /dev/fibonacci at ofset 84, returned the sequence 160500643816367088.
reading from /dev/fibonacci at ofset 40, returned the sequence 102334155.
reading from /dev/fibonacci at ofset 85, returned the sequence 259695496911122585.
reading from /dev/fibonacci at ofset 86, returned the sequence 420196140727489673.
reading from /dev/fibonacci at ofset 87, returned the sequence 679891637638612258.
reading from /dev/fibonacci at ofset 41, returned the sequence 165580141.
reading from /dev/fibonacci at ofset 42, returned the sequence 267914296.
reading from /dev/fibonacci at ofset 43, returned the sequence 433494437.
reading from /dev/fibonacci at ofset 44, returned the sequence 701408733.
reading from /dev/fibonacci at ofset 88, returned the sequence 1100087778366101931.
reading from /dev/fibonacci at ofset 45, returned the sequence 1134903170.
reading from /dev/fibonacci at ofset 89, returned the sequence 1779979416004714189.
reading from /dev/fibonacci at ofset 46, returned the sequence 1836311903.
reading from /dev/fibonacci at ofset 90, returned the sequence 2880067194370816120.
reading from /dev/fibonacci at ofset 47, returned the sequence 2971215073.
reading from /dev/fibonacci at ofset 91, returned the sequence 4660046610375530309.
reading from /dev/fibonacci at ofset 48, returned the sequence 4807526976.
reading from /dev/fibonacci at ofset 92, returned the sequence 7540113804746346429.
reading from /dev/fibonacci at ofset 49, returned the sequence 7778742049.
reading from /dev/fibonacci at ofset 50, returned the sequence 12586269025.
reading from /dev/fibonacci at ofset 51, returned the sequence 20365011074.
reading from /dev/fibonacci at ofset 52, returned the sequence 32951280099.
reading from /dev/fibonacci at ofset 53, returned the sequence 53316291173.
reading from /dev/fibonacci at ofset 54, returned the sequence 86267571272.
reading from /dev/fibonacci at ofset 55, returned the sequence 139583862445.
reading from /dev/fibonacci at ofset 56, returned the sequence 225851433717.
reading from /dev/fibonacci at ofset 57, returned the sequence 365435296162.
reading from /dev/fibonacci at ofset 58, returned the sequence 591286729879.
reading from /dev/fibonacci at ofset 59, returned the sequence 956722026041.
reading from /dev/fibonacci at ofset 60, returned the sequence 1548008755920.
reading from /dev/fibonacci at ofset 61, returned the sequence 2504730781961.
reading from /dev/fibonacci at ofset 62, returned the sequence 4052739537881.
reading from /dev/fibonacci at ofset 63, returned the sequence 6557470319842.
reading from /dev/fibonacci at ofset 64, returned the sequence 10610209857723.
reading from /dev/fibonacci at ofset 65, returned the sequence 17167680177565.
reading from /dev/fibonacci at ofset 66, returned the sequence 27777890035288.
reading from /dev/fibonacci at ofset 67, returned the sequence 44945570212853.
reading from /dev/fibonacci at ofset 68, returned the sequence 72723460248141.
reading from /dev/fibonacci at ofset 69, returned the sequence 117669030460994.
reading from /dev/fibonacci at ofset 70, returned the sequence 190392490709135.
reading from /dev/fibonacci at ofset 71, returned the sequence 308061521170129.
reading from /dev/fibonacci at ofset 72, returned the sequence 498454011879264.
reading from /dev/fibonacci at ofset 73, returned the sequence 806515533049393.
reading from /dev/fibonacci at ofset 74, returned the sequence 1304969544928657.
reading from /dev/fibonacci at ofset 75, returned the sequence 2111485077978050.
IN MAIN: Thread 0 has ended.
reading from /dev/fibonacci at ofset 76, returned the sequence 3416454622906707.
reading from /dev/fibonacci at ofset 77, returned the sequence 5527939700884757.
reading from /dev/fibonacci at ofset 78, returned the sequence 8944394323791464.
reading from /dev/fibonacci at ofset 79, returned the sequence 14472334024676221.
reading from /dev/fibonacci at ofset 80, returned the sequence 23416728348467685.
reading from /dev/fibonacci at ofset 81, returned the sequence 37889062373143906.
reading from /dev/fibonacci at ofset 82, returned the sequence 61305790721611591.
reading from /dev/fibonacci at ofset 83, returned the sequence 99194853094755497.
reading from /dev/fibonacci at ofset 84, returned the sequence 160500643816367088.
reading from /dev/fibonacci at ofset 85, returned the sequence 259695496911122585.
reading from /dev/fibonacci at ofset 86, returned the sequence 420196140727489673.
reading from /dev/fibonacci at ofset 87, returned the sequence 679891637638612258.
reading from /dev/fibonacci at ofset 88, returned the sequence 1100087778366101931.
reading from /dev/fibonacci at ofset 89, returned the sequence 1779979416004714189.
reading from /dev/fibonacci at ofset 90, returned the sequence 2880067194370816120.
reading from /dev/fibonacci at ofset 91, returned the sequence 4660046610375530309.
reading from /dev/fibonacci at ofset 92, returned the sequence 7540113804746346429.
IN MAIN: Thread 1 has ended.
MAIN program has ended.
```
我們可以在有 `mutex` 和 `without_mutex` 的實驗裡發現,一旦沒有 `mutex` 的控制,在寫入檔案時會發生 race condition , 寫入順序會變成亂序,
## 自我檢查清單
- [x] 研讀上述 Linux 效能分析的提示 描述,在自己的實體電腦運作 GNU/Linux,做好必要的設定和準備工作 → 從中也該理解為何不希望在虛擬機器中進行實驗;
- [x] 研讀上述費氏數列相關材料 (包含論文),摘錄關鍵手法,並思考 clz / ctz 一類的指令對 Fibonacci 數運算的幫助。請列出關鍵程式碼並解說
- [ ] 複習 C 語言 [數值系統](https://hackmd.io/@sysprog/c-numerics) 和 [bitwise operation](https://hackmd.io/@sysprog/c-bitwise),思考 Fibonacci 數快速計算演算法的實作中如何減少乘法運算的成本;
- [ ] 學習指出針對大數運算的加速運算和縮減記憶體操作成本的舉措,紀錄你的認知和疑惑
- [x] 注意到 `fibdrv.c` 存在著 `DEFINE_MUTEX`, `mutex_trylock`, `mutex_init`, `mutex_unlock`, `mutex_destroy` 等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 [POSIX Thread](https://en.wikipedia.org/wiki/POSIX_Threads) 的程式碼來確認。 → 搭配閱讀〈[並行和多執行緒程式設計](https://hackmd.io/@sysprog/concurrency)〉