# Reading [The Linux Kernel Module Programming Guide](https://sysprog21.github.io/lkmpg/) (7)
## Blocking Processes and threads
### sleep (但我被搞到沒法sleep)
編譯完sleep.c和cat_nonblock.c後,利用tail -f持續read /proc/sleep 使得cat_nonblock被
```
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
```
抓住,因此拋出異常。
然而,使用`tail -f /proc/sleep &`,tail會一直read /proc/sleep造成占用,因此會看到一直print出來`Last input:`,並且這個時候使用`cat`或`echo "something" > /proc/sleep`就會被卡住,直到`tail -f /proc/sleep &`的process被kill,一切乎也沒什麼,但是我再跑範例的時候出了大trouble。`tail -f /proc/sleep &`這一條指令並沒有直接show `Last input:`出來,反而是一直在後台run,困擾了我好久,測幾次之後發現如果開了兩個`tail -f /proc/sleep &`,接著將執行中(running time比較長)的那個process kill之後,另一個process才會正常print `Last input:`,起初還以為是我的電腦中邪了,仔細去觀察busybox 1.36.1版本的coreutils/tail.c,做了一些實驗後才發現問題。
沒做改動的結果:
```
~ # insmod ./workspace/sleep.ko
[ 2.534513] sleep: loading out-of-tree module taints kernel.
[ 2.540987] /proc/sleep created
~ # tail -f /proc/sleep &
~ # /workspace/cat_nonblock /proc/sleep
Open would block
~ # /workspace/cat_nonblock /proc/sleep
Open would block
~ # /workspace/cat_nonblock /proc/sleep
Open would block
~ # /workspace/cat_nonblock /proc/sleep
Open would block
~ #
```
並沒有正確顯示 `Last input:`
#### 結論:
[tail.c](https://elixir.bootlin.com/busybox/1.37.0/source/coreutils/tail.c#L91)這邊使用的full_read會一次將module_output的message一次吃完,使[這一行](https://elixir.bootlin.com/busybox/1.37.0/source/coreutils/tail.c#L266)的while loop收到的nread一直大於0。然而這邊的loop是一次吃完所有的message才print buffer。所以loop裡面也不會print東西出來,導致這個怪怪的情況。
#### 解法:
將full_read直接換成safe_read就可以解決了
流程如下:
1. 第一次進while loop : safe_read get message "Last input:\n" from module_output, nread > 0 is true.
2. 第二次進while loop : safe_read get message "" from module_output, nread == 0. <---- module_output return 0
3. 繼續執行後面的loop
改完後tail.c的tail_read會長這樣(其他都不動)
```
static ssize_t tail_read(int fd, char *buf, size_t count)
{
ssize_t r;
r = safe_read(fd, buf, count);
if (r < 0) {
bb_simple_perror_msg(bb_msg_read_error);
G.exitcode = EXIT_FAILURE;
}
return r;
}
```
輸出會長這樣,這邊的tailv2是我改寫busybox的tail.c成tailv2.c後重新編譯的結果,詳細的過程之後再更新
```
[ 13.763443] sleep: loading out-of-tree module taints kernel.
[ 13.768161] /proc/sleep created
~ # /workspace/cat_nonblock /proc/sleep
Last input:
~ # tailv2 -f /proc/sleep &
~ # Last input:
Last input:
Last input:
Last input:
Last input:
Last input:
Last input:
Last input:
/workspace/cat_nonblock /proc/sleep
Open would block
~ # Last input:
Last input:
Last input:
QEMU: Terminated
```
#### 再一個結論
這邊是為了要補充前面提到`為何執行兩次然後kill正在跑的process之後,第二個就會變得正常`,我們已經知道infinit loop的原因了,那麼執行兩次然後kill正在跑的process之後,有解決inifint loop嗎?
顯然是沒有,但為何他可以逃過那個whill loop?
答案是它高機率性的逃過。因為我們sleep.c的module_output的finished變數如果process被kill的當下是1,那麼下次執行時mdolue_output就會直接return 0,誤打誤撞得停下那個while loop,反之就一樣會被卡在那裏QQ,但是finished是1的時候要複製字串會比較花時間,在這個過程被中斷的機會比較大。
## 更新一些修改busybox測試時遇到的大小trouble
首先,如果你問chatgpt或上網google怎麼新增applet到busybox裡面會出現很多舊的方法,但其實只要[參考](https://elixir.bootlin.com/busybox/1.37.0/source/docs/new-applet-HOWTO.txt)在你寫的C Program前面加上適當的註解就可以了,makefile 會自動去抓所有.c檔然後生成config和kbuild。下面是我改寫tail.c的註解
```
/* vi: set sw=4 ts=4: */
/*
* Mini tailv2 implementation for busybox
*
* Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
* Bugs fixed (although I may have forgotten one or two... it was pretty bad)
* 1) mixing printf/write without fflush()ing stdout
* 2) no check that any open files are present
* 3) optstring had -q taking an arg
* 4) no error checking on write in some cases, and a warning even then
* 5) q and s interaction bug
* 6) no check for lseek error
* 7) lseek attempted when count==0 even if arg was +0 (from top)
*/
//config:config TAILV2
//config: bool "tailv2 (6.8 kb)"
//config: default y
//config: help
//config: tailv2 is used to print the last specified number of lines
//config: from files.
//config:
//config:config FEATURE_FANCY_TAILV2
//config: bool "Enable -q, -s, -v, and -F options"
//config: default y
//config: depends on TAILV2
//config: help
//config: These options are provided by GNU tailv2, but
//config: are not specified in the SUSv3 standard:
//config: -q Never output headers giving file names
//config: -s SEC Wait SEC seconds between reads with -f
//config: -v Always output headers giving file names
//config: -F Same as -f, but keep retrying
//applet:IF_TAILV2(APPLET(tailv2, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_TAILV2) += tailv2.o
/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/tailv2.html */
//usage:#define tailv2_trivial_usage
//usage: "[OPTIONS] [FILE]..."
//usage:#define tailv2_full_usage "\n\n"
//usage: "Print last 10 lines of FILEs (or stdin) to.\n"
//usage: "With more than one FILE, precede each with a filename header.\n"
//usage: "\n -c [+]N[bkm] Print last N bytes"
//usage: "\n -n N[bkm] Print last N lines"
//usage: "\n -n +N[bkm] Start on Nth line and print the rest"
//usage: "\n (b:*512 k:*1024 m:*1024^2)"
//usage: IF_FEATURE_FANCY_TAILV2(
//usage: "\n -q Never print headers"
//usage: "\n -v Always print headers"
//usage: )
//usage: "\n -f Print data as file grows"
//usage: IF_FEATURE_FANCY_TAILV2(
//usage: "\n -F Same as -f, but keep retrying"
//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
//usage: )
//usage:
//usage:#define tailv2_example_usage
//usage: "$ tailv2 -n 1 /etc/resolv.conf\n"
//usage: "nameserver 10.0.0.1\n"
```
main function要定義成 : `yourapplet_main`
最後要加`MAIN_EXTERNALLY_VISIBLE`
```
int tailv2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
```
# 這些註解很重要要要要要要要要要要
# //config:config TAILV2 這樣是對的
# // config:config TAILV2 不要亂加空白,會讀不到
# auto format 會發生悲劇