分类 Ubuntu 下的文章

“Ubuntu是以桌面应用为主的Linux发行版,基于Debian。Ubuntu有三个正式版本,包括桌面版、服务器版及用于物联网设备和机器人的Core版。从17.10版本开始,Ubuntu以GNOME为默认桌面环境。 Ubuntu是著名的Linux发行版之一,也是目前最多用户的Linux版本。 ”

inb 从I/O端口读取一个字节(BYTE, HALF-WORD) ;
outb 向I/O端口写入一个字节(BYTE, HALF-WORD) ;
inw 从I/O端口读取一个字(WORD,即两个字节) ;
outw 向I/O端口写入一个字(WORD,即两个字节) ;
byte inb(word port); ;
word inw(word port); ;
void outb(word port, byte value); ;
void outw(word port, word value); ;

内核模块 (kernel module) 的编译环境配置会根据发行版的不同而不同,即使是 Ubuntu 和 Debian 也可能有细微的区别。

0x00 安装软件包
对于 Debian 来说,只需要安装 Linux Header 即可进行 module 的编译。

$ sudo apt-get install linux-headers-$(uname -r)

如果需要内核源码,可以参考链接 2。

0x01 编译 module
进行简单的编译测试。

准备 hello.c

#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void){
    printk(KERN_ALERT "Hello, worldn");

return 0;
}
static void hello_exit(void){
    printk(KERN_ALERT "Exit, module.n");
}
module_init(hello_init);
module_exit(hello_exit);

内核模块的编译重点在 Makefile 上。需要注意的是,make 对应的 makefile 文件的名称必须为Makefile,全部小写的 makefile 是不行的。

# File name: Makefile

obj-m := hello.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) module

clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

除了使用 modules 参数构建模块之外,我们还可以使用 clean 参数方便的配置清理函数。

在 hello.c 的所在目录执行make进行编译即可。

详细信息参考链接 3。《Linux 设备驱动程序》中的 Makefile 更认真一些。

0x02 其他
在更新内核版本后,uname -r会发生变动,导致 Makefile 使用的/lib/modules/$(shell uname -r)/build指向新的内核构建目录.

但是 linux-header 的安装包同样是使用uname -r安装的,并不会随着内核更新同步更新 (事实上你可以使用 apt 安装任意版本的 linux-header).

解决办法是安装新的 linux-header,即重新执行步骤 0x01 即可.
Referenced from:https://www.dazhuanlan.com/xthsky/topics/1197635

Ubuntu 下获取和编译内核源码的方法
update:2021-11-11
如果只想获得当前内核版本的源码可以用 apt-get ,如果想获取当前系统的所有内核源码建议用 git 。

1. apt-get
通常是用 apt-get source 下载:

apt-get source linux-image-$(uname -r)

源码会直接下载到当前目录,并自动解压,所以建议在 /usr/src/ 目录下执行:

root@ubuntu:/usr/src# ls
linux-4.4.0 linux_4.4.0-161.189.dsc linux-headers-4.4.0-142
linux_4.4.0-161.189.diff.gz linux_4.4.0.orig.tar.gz linux-headers-4.4.0-142-generic
linux_4.4.0.orig.tar.gz 是标准内核源码
linux_4.4.0-161.189.diff.gz 是 Ubuntu kernel 的补丁
linux_4.4.0-161.189.dsc 是 Debian Source Control 文档,用于描述源码版本的相关信息
linux-4.4.0 是前两个文件解压打补丁后的源码目录
这样自动下载的小版本号通常是最新的,如果想精确匹配当前的内核版本,可以在 https://launchpad.net/ubuntu/+source/linux/ 搜索所需的版本,也是这样的三个文件,下载后需要手动解压打补丁:

tar xfvz <filname>.orig.tar.gz
gunzip <filename>.diff.gz
cd ./<filename>
patch -p1 -i <file name>.diff 

此外,用 apt-cache search linux-source 和 apt-get install linux-source 也可以搜索并下载当前内核版本的源码,并自动解压到 /usr/src/ 目录下。

root@ubuntu:/usr/src$ ls
linux-headers-4.4.0-116 linux-headers-4.4.0-116-generic linux-source-4.4.0 linux-source-4.4.0.tar.gz
root@ubuntu:/usr/src$ ls linux-source-4.4.0/
debian debian.master linux-source-4.4.0.tar.gz
其中 debian 和 debian.master 目录下存放的是 ubuntu 发行版中针对内核修改的配置、补丁、说明和一些工具,在 /usr/src/ 目录下执行如下命令解压,所有源码就解压在 linux-source-4.4.0 目录下,:

/usr/src$ tar xvf linux-source-4.4.0.tar.gz
2. git
Ubuntu 使用的内核源码都是用 git 维护的,每个版本的源代码都保存在 kernel.ubuntu.com上的 git 存储库中。可以下载你需要的版本:

git clone git://kernel.ubuntu.com/ubuntu/ubuntu-<release codename>.git

例如下载 16.04 LTS Xenial Xerus 的源码:

git clone git://kernel.ubuntu.com/ubuntu/ubuntu-xenial.git

这个版本库包含了当前内核源码的所有 commit ,可以检出所需的具体版本。

3. 编译
编译内核前,需要安装一些依赖,并复制一份系统自带的配置文件:

root@ubuntu:~# apt-get install libncurses5-dev bc libssl-dev
root@ubuntu:~# cp -rf /boot/config-4.4.0-116-generic /usr/src/linux-source-4.4.0/.config

如果只是想修改个别模块,必须先复制当前安装的模块的符号表

root@ubuntu:/usr/src/linux-4.4# cp -rf /lib/modules/4.4.0-142-generic/build/Module.symvers ./Module.symvers
然后执行 make M=<module dir> 命令编译。
Referenced from:https://shaocheng.li/posts/2019/09/20/

本文例子中涉及两个模块hello.ko和world.ko,其中hello导出符号供world使用;

insmod
该命令将模块的代码和数据装入内核,然后使用内核的符号表继续模块中任何未解析的符号。insmod不会修改模块的磁盘文件,而仅仅修改内存中的副本。insmod可以接受一些命令行选项,并且可以再模块链接到内核之前给模块中的整数和字符串变量赋值。

按照顺序加载,通过;

1 [root@localhost export]# insmod hello.ko
2 [root@localhost export]# insmod world.ko

先加载world.ko,不通过,因为world找不到从a中引用的符号;

1 [root@localhost export]# insmod world.ko 
2 insmod: ERROR: could not insert module world.ko: Unknown symbol in module

modprobe
与insmod类型,modprobe也用来将模块装入内核,区别在于,modprobe会考虑要加载的模块是否引用了一些当前内核中不存在的符号,如果有这类引用,modprobe会在当前模块搜索路径中查找定义了这些符号的其他模块,如果找到了这些依赖模块,它会同时将这些模块也装载到内核。这种情况下使用insmod会失败,并在系统日志文件中记录”unresolved symbols”消息;

在使用modprobe时,在配置文件中加入 modules_install行和clean行;

1 ifneq ($(KERNELRELEASE),)
 2         obj-m :=hello.o world.o
 3 #       module-objs := file1.o file2.o
 4 else
 5         KERNELDIR ?=/lib/modules/$(shell uname -r)/build
 6         PWD :=$(shell pwd)
 7 default:
 8         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
 9         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
10 clean:
11         rm -rf *.o *.mod.c *.ko *.symvers *.order *.makers
12         $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
13 endif

加载模块world,成功;

1 [root@localhost export]# modprobe world

lsmod查看,可见依赖的模块hello也被加载进内核;

1 [root@localhost export]# lsmod
2 Module                  Size  Used by
3 world                  16384  0 
4 hello                  16384  1 world

rmmod
rmmod用于从内核中移除模块;如果内核模块仍然在使用状态,或者内核被配置为禁止移除模块,则无法移除该模块;配置内核并支持在模块忙的时候仍能移除模块是可能的;但是,重新引导系统是更合适的办法;

先移除world.ko,再移除hello.ko,成功;

[root@localhost export]# rmmod world.ko 
[root@localhost export]# rmmod hello.ko

先移除模块hello.ko,产生错误,因为hello在被world使用;

1 [root@localhost export]# rmmod hello.ko
2 rmmod: ERROR: Module hello is in use by: world

lsmod
lsmod用于列出当前装载到内核中的所有模块,包括一些其他信息,其通过读取/proc/modules来获取这些信息;

1 [root@localhost export]# lsmod
2 Module                  Size  Used by
3 world                  16384  0 
4 hello                  16384  1 world

Referenced from:https://www.cnblogs.com/wanpengcoder/p/11755850.html

make[1]: 进入目录“/usr/src/linux-headers-5.4.0-89-generic”
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/confdata.o
HOSTCC scripts/kconfig/expr.o
LEX scripts/kconfig/lexer.lex.c
YACC scripts/kconfig/parser.tab.[ch]
HOSTCC scripts/kconfig/lexer.lex.o
HOSTCC scripts/kconfig/parser.tab.o
HOSTCC scripts/kconfig/preprocess.o
HOSTCC scripts/kconfig/symbol.o
HOSTLD scripts/kconfig/conf
scripts/kconfig/conf --syncconfig Kconfig
make[2]: * 没有规则可制作目标“arch/x86/tools/relocs_32.c”,由“arch/x86/tools/relocs_32.o” 需求。 停止。
make[1]: * [arch/x86/Makefile:236:archscripts] 错误 2
make[1]: 离开目录“/usr/src/linux-headers-5.4.0-89-generic”
make: * [Makefile:5:all] 错误 2

解决办法:

In the make file, just change SUBDIRS=$(PWD) into M=$(shell pwd)