Presentation on theme: "实验2:Linux内核重建及内核模块 主讲教师:夏莹杰 xiayingjie@zju.edu.cn."— Presentation transcript:
1
实验2:Linux内核重建及内核模块 主讲教师:夏莹杰
2
重点内容 Linux内核重建 Linux内核模块 查找并且下载一份内核源代码 配置内核 编译内核和模块 配置启动文件 什么是内核模块 内核模块的特点 一个内核模块实例 创建内核模块需要makefile
3
Linux内核重建
4
实验环境 Linux操作系统发行版本为ubuntu16.04.1 LTS Linux内核版本为4.6 编译成功后,配置GNU的启动引导工具为grub2
5
查找并下载一份内核源代码 Linux受GNU通用公共许可证(GPL)保护,内核源代码是完全开发的。官方网站为: 首先通过命令 uname –r 确认你的Linux运行环境采用哪个版本的内核源代码,ubuntu LTS采用的是 generic版本 这次内核重建实验,我们试下较新的版本即Linux4.6 必须下载到工作目录的文件: linux-4.6.tar.xz patch-4.6.xz
6
部署内核源代码 此过程比较机械、枯燥,因而容易出错,请严格按照指导手册的步骤来操作。
7
配置内核 配置是精确控制新内核功能的机会,配置过程也控制哪些需编译到内核的二进制映像中(在启动时被载入),哪些是需要时才装入的内核模块(module) 进行配置时,大部分选项可以使用其缺省值,只有少部分需要根据用户不同的需要选择,例如硬盘分区采用ext2文件系统,则配置项应支持 ext2文件系统等 对每一个配置项,用户有三种选择 “<*>”或“[*]” 将该功能编译进内核 “[]” 不将该功能编译进内核 “[M]” 将该功能编译成可以在需要时动态插入到内核中的模块
8
编译内核和模块 编译内核就用:make bzImage -jN 这里N是主机 CPU的核数乘以2 执行到这一步可能会出现错误:fatal error:openssl/opensslv.h:No such file or directory,这是因为没有安装openssl,安装openssl:apt-get install libssl-dev 也可能会出现 bc:not found,相应的需要安装bc:apt-get install bc 编译内核及内核模块需较长时间,具体与机器的硬件条件及内核的配置因素有关。
9
安装内核 安装操作不可逆,一旦操作中出现哪怕些许的失误,将导致Linux系统无法正常初始化,一切努力付之东流!慎重!慎重! 安装内核过程就比较快,先安装模块: make modules_install 安装内核: make install
10
应用grub配置启动文件 grub就是管理ubuntu系统启动的一个程序。想运行刚刚编译好的内核,就需要修改相应的grub。 mkinitramfs –o /boot/initrd.img-4.6.0 update-grub2 这个命令会自动修改grub 这样,我们已经编译了内核bzImage(vmlinuz-4.6.0),放到了指定位置/boot;initrd.img-4.6.0、System.map-4.6.0等辅助资源,同样放到了/boot,我们也配置了grub,现在重启主机系统,期待编译过的Linux操作系统内核正常运行。
11
Linux内核模块
12
重点内容 Linux内核模块 什么是内核模块 内核模块的特点 一个内核模块实例 创建内核模块需要makefile
13
monolithic kernel vs micro kernel Linux操作系统的内核是单一体系结构(monolithic kernel)的。与monolithic kernel相对的,是微内核体系结构(micro kernel) micro kernel操作系统的核心部分是一个很小的内核,实现一些最基本的服务,如创建和删除进程、内存管理、中断管理等等。而文件系统、网络协议等其它部分都在微内核外的用户空间里运行。 micro kernel操作系统具有很好的可扩展性而且内核非常的小,但这样的操作系统由于不同层次之间的消息传递要花费一定的代价所以效率比较低。 monolithic kernel操作系统,所有的模块都集成在一起,系统的速度和性能都很好,但是可扩展性和维护性就相对比较差。
14
鱼与熊掌能否兼得 ? 内核模块(Loadable Kernel Module)或 LKM,是在内核空间运行的程序。 内核模块视同其它静态连接的内核函数,只不过它是“动态”连接的。 与运行在微内核体系操作系统的外部用户空间的服务进程不同,内核模块在内核态代表当前进程执行。
15
内核模块的优点 比monolithic kernel更加紧凑和灵活。 修改内核模块时,只要编译该模块生成目标代码(.ko) ,然后将.ko插入内核即可。不必全部重新编译整个内核,可节省不少时间,避免人工操作的错误。 模块的目标代码一旦被链接到内核,它的作用和通过静态连接的内核代码完全等价。 所以,当调用模块的函数时,无须显式地消息传递。
16
内核模块的缺点 由于内核所占用的内存不会被换出,所以连接进内核的模块会给整个系统带来性能、安全、内存利用方面的损失。 装入内核的模块就成为内核的一部分,可以修改内核中的其他部分,因此,模块的使用不当会导致系统崩溃。 为了让内核模块能访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改符号表。 模块会要求利用其它模块的功能,所以,内核要维护模块之间的依赖性。
17
编写一个简单的内核模块helloworld.ko #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ int init_module(void) { printk(KERN_INFO “Hello World!\n”); /* 试试printf(), 验证ko不在用户态 */ return 0; } void cleanup_module(void) { printk(KERN_INFO “Goodbye!\n”); MODULE_LICENSE(“GPL”);
18
内核模块必备要素 任何模块程序的编写都需要包含linux/module.h这个头文件。文件包含了对模块的结构定义以及模块的版本控制。 函数init_module()和函数cleanup_module( )是模块编程中最基本的也是必须的两个函数: init_module()向内核注册模块所提供的新功能 cleanup_module()负责注销所有由模块注册的功能 printk()函数不是printf() printk()函数由Linux内核定义,功能与printf相似。 字符串KERN_INFO表示消息的优先级, syslogd针对不同优先级的消息进行不同的处理。
19
内核模块的加载 系统调用 modprobe程序 insmod [path]modulename.ko 从命令行中读入模块,通常是扩展名为“.ko”,elf格式的目标文件 确定模块对象代码所在文件的位置。通常这个文件在lib/modules的某个子目录中。 计算存放模块代码、模块名和数据结构module所需要的内存大小。 在用户空间中分配一个内存区,把module结构、模块名以及为正在运行的内核重定位的模块代码拷贝到这个内存里。其中,module结构中的init域指向这个模块的入口函数重新分配到的地址;exit域指向出口函数所重新分配的地址。 调用init_module(),向它传递上面所创建的用户态的内存区的地址。 释放用户态内存, 整个过程结束。
20
内核模块的卸载 rmmod [path]modulename
21
内核模块实用程序modutils insmod加载内核模块 rmmod卸载内核模块 ksyms显示内核符号和模块符号表的信息,可以读取/proc/kallsyms文件。 lsmod显示当前系统中正在使用的模块信息。 实际上这个程序的功能就是读取/proc文件系统中的文件/proc/modules中的信息。 modprobe连接(与insmod类似)在命令行中指定的一个模块,但它还可以(与insmod不同)递归地连接指定模块所引用到的其他模块。
22
内核模块的make文件 TARGET = helloworld KDIR = /usr/src/linux PWD = $(shell pwd) obj-m += $(TARGET).o default: make -C $(KDIR) M=$(PWD) modules KDIR目录中包含了内核驱动模块所需要的各种头文件及依赖。 KDIR的内核源码版本,必须与runtime系统一致
23
内核模块的make文件 $(KDIR)表示源代码最高层目录的位置。 “obj-m += $(TARGET).o”告诉kbuild,希望将$(TARGET),也就是helloworld,编译成内核模块。 “M=$(PWD)”表示生成的模块文件都将在当前目录下。
24
多文件内核模块的make文件 TARGET = helloworld KDIR = /usr/src/linux PWD = $(shell pwd) obj-m += $(TARGET).o $(TARGET)-y := start.o stop.o default: make -C $(KDIR) M=$(PWD) modules
25
start.c #include <linux/kernel.h> /*我们在做内核的工作 */ #include <linux/module.h> /*初始化模块 */ int init_module() { printk("Hello, world!\n"); /* 如果我们返回一个非零值, 那就意味着 * init_module 初始化失败并且内核模块 * 不能加载 */ return 0; }
26
stop.c #include <linux/kernel.h> /*我们在做内核的工作 */ #define __NO_VERSION__ #include <linux/module.h> #include <linux/version.h> /* 不被module.h包括,因为__NO_VERSION__ */ /* Cleanup - 撤消 init_module 所做的任何事情*/ void cleanup_module() { printk("Bye!\n"); }
27
实验资料 下载地址: 教学与课程 实验2:Linux内核重建和内核模块 实验指导材料1&实验指导材料2 完成时间:10月6日——10月27日 完成要求: 针对两个实验指导材料中的研究内容,按照实验报告模板格式撰写实验报告
28
Q & A
Download ppt "实验2:Linux内核重建及内核模块 主讲教师:夏莹杰 xiayingjie@zju.edu.cn."