1、背景
压缩卡驱动提供给文件系统KAPI,供文件系统对文件数据进行压缩和解压。在测试中,最初采用的方法是通过文件系统提供的系统调用,利用文件系统在处理系统调用时,会调用到驱动的KAPI,来完成对压缩卡KAPI及其更下层(包含硬件)正确性的测试。考虑到这种方法,可能会由于文件系统对KAPI的具体使用方式而屏蔽一些问题的发现,因此展开了对KAPI的直接测试。由于KAPI是内核态的接口,无法在用户态直接调用,因此要最终完成对KAPI的更直接测试,需要借助编写内核模块(Kernel Module),来实现用户进程对KAPI的访问;此外,还要解决用户态和内核态两者的交互。下图中表示了引入基于内核模块的测试方法,可以使我们的测试程序在调用关系上更加接近被测模块,从而在测试效率和覆盖性上得到改进。
在本文中,将主要介绍实现基于内核模块的测试的相关知识与代码编写方式。
2、用户态和内核态
操作系统中,虚拟内存被分为内核空间和用户空间。内核空间被用于内核,内核扩展功能和一些设备驱动的运行;用户空间是用户进程在用户态下运行时所使用。在 linux系统中,内核拥有地址为3G到4G的内存空间,并且它是被共享的,而用户进程拥有0到3G的用户空间,每个用户进程拥有独立的空间。
CPU根据PSW寄存器中的模式bit,可以工作在用户态 (Ring 3)或内核态(Ring 0),用户态能执行有限的非特权的CPU指令。在内核态可以执行所有指令。工作在用户态的进程无法直接访问硬件和内核的内存空间,这是出于运行安全的考虑。当然,操作系统也会也提供系统调用或中断等方法,让用户进程可以切换到内核态,以访问内核空间或硬件。系统调用或中断,可以看作是有限度的开放给用户进程对内核和硬件的访问,从而保证了系统安全。
在驱动项目中,驱动程序工作在内核空间中,它直接提供KAPI给内核,这样我们就无法通过用户进程完成对该KAPI的测试。当然,通过执行内核提供的系统调用,当发生磁盘读写时可以间接的调用到该KAPI,但是内核对其有限或具体的调用方式往往屏蔽了底层驱动的部分缺陷,这种测试方法也带来发现问题后追查定位的困难。要改变这些不足,最直接的思路就是跨越对内核特殊逻辑间接调用的依赖,通过直接对KAPI进行调用的方法完成测试。
为了实现对工作在内核态的KAPI的访问,必须向内核置入内核态的代码以发起调用,这将通过kernel module来完成,会在第三部分具体介绍。而为了实现对测试数据输入,执行发起,结果输出采集等的灵活控制,还必须实现用户态和内核态的交互,具体方法将在第四部分介绍。
3. Kernel Module
要达成对KAPI的直接调用和测试,需要添加工作在内核态的代码实现对该函数的访问,这里我们借助kernel module来完成。kernel module提供了不需要重新编译kernel image,就向内核引入新代码和逻辑的支持。Kernel Module通常是以ko为扩展名的文件,通过lsmod命令可以查看已经加载的Kernel Module,通过insmod/rmmod命令完成指定的的模块的加载和卸载。Kernel Module最基本的编程模式是编程者通过宏定义module_init和module_exit,指定在模块加载和卸载时进行的初始化或清理等工作。一个基本的hello world程序如下。向init中就可以加入对KAPI进行调用的函数,当模块被insmod时就会在内核态被执行。但是这与灵活的完成各种测试任务还有差距,为了能在用户态完成测试数据的准备和输入,测试结果的获取和对执行的控制,还需要引入用户态和内核态的交互。关于Kernel Module更详细的编译方式和编程模式介绍,可以参考 http://www.tldp.org/LDP/lkmpg/2.6/html/
#include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/init.h> /* Needed for the macros */ static int __init hello_init(void){ printk(KERN_INFO "Hello, world 2\n"); return 0; } static void __exit hello_exit(void){ printk(KERN_INFO "Goodbye, world 2\n");} module_init(hello_init); module_exit(hello_exit); |
4. 用户空间和内核空间的交互
原文转自:http://www.uml.org.cn/Test/201210242.asp