Yume 練功地

偷偷練

kernel

Kbuild: the Linux Kernel Build System

Build kernel 相關套件

1
2
sudo apt-get install git-core
sudo apt-get install libncurses5-dev

查看 Linux kernel

可以在 /lib/modules 查看到目前有哪些 kernel。

且可以在 /boot 找到對應版本

1
2
3
4
5
ls /lib/modules
# 3.5.0-23-generic

ls /boot
# vmlinuz-3.5.0-23-generic
1
2
3
4
5
6
7
8
9
10
11
12
/boot/grub

uname -a

tar xf linux-3.12.2.tar.xz

lsmod

make menuconfig
make all
make modules_install
make install
Linux kernel

Kernel
[Linux Kernel] 撰寫 Hello, World module: The init and exit Macros (part 2).

Lesson

Lesson 1: Building and running a new Linux kernel

Video

Linux Boot Process
Kernel Basics

PDF

The Linux Kernel Module Programming Guide
The sysfs Filesystem

Trick

Linux Kernel: BUILD_BUG_ON_ZERO() / BUILD_BUG_ON_NULL()


Kernel Module

零散的 code,當需要的時候可以 載入(load)/卸載(unload) 至 kernel。

擴展 kernel 的功能,且不必 reboot kernel

Module 如何進入到 kernel

1
lsmod # 列出 /proc/modules 的 modules

當需要的功能不在 kernel 時(not resident in the kernel),

kernel module daemon(kmod),會執行 modprobe 將 module 載入

modprobe is passed a string in one of two forms:

  • module 名稱,如 softdog or ppp.
  • A more generic identifier like char−major−10−30.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# kmod -h
kmod - Manage kernel modules: list, load, unload, etc
Usage:
kmod [options] command [command_options]

Options:
-V, --version show version
-h, --help show this help

Commands:
help Show help message
list list currently loaded modules
static-nodes outputs the static-node information installed with the currently running kernel

kmod also handles gracefully if called from following symlinks:
lsmod compat lsmod command
rmmod compat rmmod command
insmod compat insmod command
modinfo compat modinfo command
modprobe compat modprobe command
depmod compat depmod command
1
2
3
# /etc/modprobe.conf
alias char−major−10−30 softdog
# generic identifier refers to the module softdog.ko

接著, modprobe 會瀏覽 /lib/modules/version/modules.dep

觀察這個模組載入前還需要載入哪些模組。

/lib/modules/version/modules.dep 藉由 depmod -a 產生且它包含相依模組。

例如: msdos.ko 須要 fat.ko 事先被載入

requested module 有相依於其他模組 如果有定義 symbols(vars of funcs)

最後,modprobeinsmod 第一次組入任何 prerequisite 模組至 kernel,然後是 requested module。

modprobeinsmod 導至 /lib/modules/version 模組的標準目錄下

確保模組依序載入
1
2
3
4
5
insmod /lib/modules/2.6.11/kernel/fs/fat/fat.ko
insmod /lib/modules/2.6.11/kernel/fs/msdos/msdos.ko
# or
modprobe msdos
# by parsing /lib/modules/version/modules.dep

There’s a bit more to the story if you want to write your own modules which depend on other modules (we calling this `stacking modules’).

Modversioning

CONFIG_MODVERSIONS

模組不能用 printf 但可以 log information and warnings

hello−1.c
1
2
3
4
5
6
7
8
9
10
11
#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void) {
printk(KERN_INFO "Hello world 1.\n");
return 0;
}

void cleanup_module(void) {
printk(KERN_INFO "Goodbye world 1.\n");
}
Makefile
1
2
3
4
5
obj−m += hello−1.o
all:
make −C /lib/modules/$(shell uname −r)/build M=$(PWD) modules
clean:
make −C /lib/modules/$(shell uname −r)/build M=$(PWD) clean

printk

建議使用 <1> and KERN_ALERT,在實作真的模組時,可以讓訊息不止只寫在 log 還能顯示給使用者

要小於 console_loglevel 才會顯示到終端機上。

Compiling Kernel Modules

建議用 kbuild

在 kernel 2.6 之後多了一些命名限制,輸出副檔名為 ko,此外還包含有額外的 modinfo 區塊。

load module & unload module & kernel log

1
2
3
4
5
insmod ./hello−1.ko
rmmod hello−1

# cat /var/log/messages
cat /var/log/kern.log # on ubuntu 1604
1
modinfo hello−1.ko
1
2
3
4
rmmod hello−1 # unload hello-1
cat /var/log/messages # 觀看 kernel log

dmesg | tail

hello-2.c

init_module() and cleanup_module() 寫法過時了。請愛用 linux/init 寫法。

hello-2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* hello−2.c − Demonstrating the module_init() and module_exit() macros.
* This is preferred over using init_module() and cleanup_module().
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
/* Needed by all modules */
/* Needed for KERN_INFO */
/* Needed for the macros */
static int __init hello_2_init(void)
{

printk(KERN_INFO "Hello, world 2\n");
return 0;
}
static void __exit hello_2_exit(void)
{

printk(KERN_INFO "Goodbye, world 2\n");
}
module_init(hello_2_init);
module_exit(hello_2_exit);

linux/drivers/char/Makefile

在這份文件你可以看到 obj−y,但 obj−m 跑去哪了?

你可以看到 obj−$(CONFIG_FOO) 這種東東,而這些變數都存放在 linux/.config

#####

hello−3
1
static int hello3_data __initdata = 3;
hello-4
1
2
3
4
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); /* What does this module do */
MODULE_SUPPORTED_DEVICE("testdevice");
1
2
# 接參數,`linux/moduleparam.h`
./insmod mymodule.ko myvariable=5
start.c stop.c
1
2
3
4
5
startstop−objs := start.o stop.o
all:
make −C /lib/modules/$(shell uname −r)/build M=$(PWD) modules
clean:
make −C /lib/modules/$(shell uname −r)/build M=$(PWD) clean
1
2
3
4
5
6
7
8
version magics
init/vermagic.o

linux/Makefile 開頭四行
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 5
EXTRAVERSION = −1.358custom

3

module init 告訴 kernel,你的 module 能提供什麼功能。並設置好,提供給 kernel 去 call

gcc −Wall −o hello hello.c. Run the exectable with strace ./hello.

man 2 write
man 2 for system call
man 3 for lib

3.14 name space

  • By convention, all kernel prefixes are lowercase.(static variable)
  • declare a symbol table and register it with a kernel.
    • /proc/kallsyms 掌管所有 symbol

Device file

1
2
3
4
ls −l /dev/hda[1−3]
brw−rw−−−− 1 root disk 3, 1 Jul 5 2000 /dev/hda1
brw−rw−−−− 1 root disk 3, 2 Jul 5 2000 /dev/hda2
brw−rw−−−− 1 root disk 3, 3 Jul 5 2000 /dev/hda3

brw−rw−−−− 1 root disk 3, 1 Jul 5 2000 /dev/hda1

major number: 1
每個driver 會對應到一個獨特的 major number

minor number: 1
type: b block device

device type

character device & block device 解釋

ch 4

1
2
3
4
5
6
struct file_operations fops = {
read: device_read,
write: device_write,
open: device_open,
release: device_release
};
C99 syntax
1
2
3
4
5
6
struct file_operations fops = {
.read = device_read, // pointer to function
.write = device_write,
.open = device_open,
.release = device_release
};