Kenel Model 测试
本内核模块测试主要是由三个子系统组成:用户态测试,内核态测试和接口测试。内核态测试通过编写内核模块代码,直接对avl原文件进行测试,可以执行所有的指令。出于运行安全的考虑,用户态的进程无法直接访问硬件和内核的内存空间,而是通过字符设备(ioctl)进入内核态调用,用户态测试时可以看到内核态通过printk函数的输出。接口测试用于检测内核态与用户态之间的交互点,重点是要检查数据的交换,传递和控制管理过程,以及相互逻辑依赖关系等。总体结构如下:
avl的测试包括下图几个方面,以add为例,具体说明avl函数接口测试的实现。
一、 内核态
Module中首先需要创建的文件有:avlat-test.h,avlat-add.c,Makefile.in 。其中avlat-test.h是包含所有测试的头文件(仿造spl内核模块编写即可),avlat-add.c是测试文件,Makefile.in是辅助编译生成文件(4.2中已提到)。
增加结点有三种情况:增加单个结点,增加多个结点,增加已存在结点。对应于这三种情况,可以设计三个测试用例。
1.增加单个结点
static int
avlat_add_test_1(struct file *file, void *arg)
{
avl_tree_t *avl_test_tree;
avlat_t *avlat_node, *first_node, *last_node;
uint32_t avl_value=5, rc = 0;
// step1: initial avl
avl_test_tree = kmalloc(sizeof(*avl_test_tree), GFP_KERNEL);
avl_create(avl_test_tree, avl_test_compare, sizeof (avlat_t), offsetof(avlat_t, avl_node));
if (avl_numnodes(avl_test_tree) != 0)
{
printk("Error: avlat_add_test_1-1.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step2: add sigle avl node and check avl_numnodes
avlat_node = kmalloc(sizeof(*avlat_node), GFP_KERNEL);
avlat_node->avl_key=avl_value;
avl_add(avl_test_tree, avlat_node);
if (avl_numnodes(avl_test_tree) != 1)
{
printk("Error: avlat_add_test_1-2.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step3: check avl_first & avl_last
first_node = avl_first(avl_test_tree);
last_node = avl_last(avl_test_tree);
if ((first_node->avl_key != last_node->avl_key) || (first_node->avl_key != avl_value))
{
printk("Error: avlat_add_test_1-3.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step4: remove avl node
avl_remove(avl_test_tree, avlat_node);
if (avl_numnodes(avl_test_tree) != 0)
{
printk("Error: avlat_add_test_1-4.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
kfree(avlat_node);
// step5: destroy avl node
avl_destroy(avl_test_tree);
kfree(avl_test_tree);
return rc;
}
2.增加多个结点
增加结点的测试用例为{10, 99, 178, 106, 7, 4, 44, 55, 1765, 200},如下图:
代码:
static int
avlat_add_test_2(struct file *file, void *arg)
{
avl_tree_t *avl_test_tree;
avlat_t *avlat_node[10], *first_node, *last_node;
uint32_t key_value[10]={10, 99, 178, 106, 7, 4, 44, 55, 1765, 200};
uint32_t i, num=10, rc=0;
// step1: initial avl
avl_test_tree = kmalloc(sizeof(*avl_test_tree), GFP_KERNEL);
avl_create(avl_test_tree, avl_test_compare, sizeof (avlat_t), offsetof(avlat_t, avl_node));
if (avl_numnodes(avl_test_tree) != 0)
{
printk("Error: avlat_add_test_2-1.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step2: add multiple avl node and check avl_numnodes
for (i=0; i<num; i++) {
avlat_node[i] = kmalloc(sizeof(*avlat_node[i]), GFP_KERNEL);
avlat_node[i]->avl_key=key_value[i];
avl_add(avl_test_tree, avlat_node[i]);
}
if (avl_numnodes(avl_test_tree) != num)
{
printk("Error: avlat_add_test_2-2.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step3: check avl_first
first_node = avl_first(avl_test_tree);
if (first_node->avl_key != 4)
{
printk("Error: avlat_add_test_2-3.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step4: check avl_last
last_node = avl_last(avl_test_tree);
if (last_node->avl_key != 1765)
{
printk("Error: avlat_add_test_2-4.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step5: remove first avl node
avl_remove(avl_test_tree, avlat_node[1]);
if (avl_numnodes(avl_test_tree) != (num-1))
{
printk("Error: avlat_add_test_2-5.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
kfree(avlat_node[1]);
// step6: check avl_first
first_node = avl_first(avl_test_tree);
if (first_node->avl_key != 4)
{
printk("Error: avlat_add_test_2-6.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step7: check avl_last
last_node = avl_last(avl_test_tree);
if (last_node->avl_key != 1765)
{
printk("Error: avlat_add_test_2-7.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step8: remove all avl nodes
for (i=0; i<num; i++) {
if (i==1) {
continue;
}
avl_remove(avl_test_tree, avlat_node[i]);
kfree(avlat_node[i]);
}
if (avl_numnodes(avl_test_tree) != 0)
{
printk("Error: avlat_add_test_2-8.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step9: destroy avl node
avl_destroy(avl_test_tree);
kfree(avl_test_tree);
return rc;
}
3.增加已存在结点
增加已存在的结点的,会报错。
代码:
static int
avlat_add_test_3(struct file *file, void *arg)
{
avl_tree_t *avl_test_tree;
avlat_t *avlat_node, *first_node, *last_node;
uint32_t avl_value=12, rc=0;
// step1: initial avl
avl_test_tree = kmalloc(sizeof(*avl_test_tree), GFP_KERNEL);
avl_create(avl_test_tree, avl_test_compare, sizeof (avlat_t), offsetof(avlat_t, avl_node));
if (avl_numnodes(avl_test_tree) != 0)
{
printk("Error: avlat_add_test_2-1.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step2: add sigle avl node and check avl_numnodes
avlat_node = kmalloc(sizeof(*avlat_node), GFP_KERNEL);
avlat_node->avl_key=avl_value;
avl_add(avl_test_tree, avlat_node);
if (avl_numnodes(avl_test_tree) != 1)
{
printk("Error: avlat_add_test_3-2.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
/*// step3: add exists avl node and check avl_numnodes
avl_add(avl_test_tree, avlat_node);
if (avl_numnodes(avl_test_tree) != 1)
{
printk("Error: avlat_add_test_3-3.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}*/
// step4: check avl_first & avl_last
first_node = avl_first(avl_test_tree);
last_node = avl_last(avl_test_tree);
if ((first_node->avl_key != last_node->avl_key) || (first_node->avl_key != avl_value))
{
printk("Error: avlat_add_test_3-4.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
// step5: remove avl node
avl_remove(avl_test_tree, avlat_node);
if (avl_numnodes(avl_test_tree) != 0)
{
printk("Error: avlat_add_test_3-5.\n");
avlat_destroy_avl_tree(avl_test_tree);
rc = ERRNO_INFO_NUM;
return rc;
}
kfree(avlat_node);
// step6: destroy avl node
avl_destroy(avl_test_tree);
kfree(avl_test_tree);
return rc;
}
二、用户态
在cmd文件夹中的avlat文件夹中创建avlat.h和avlat.c,搭建用户态框架。可以直接通过虚拟机执行指令,主要指令用法如下:
三、用户态和内核态的通信
用户态和内核态的通信通过ioctl来实现,用户态通过ioctl接口设备进入内核态调用。在module文件夹中新建文件avlat-ctl.c,并在include中建立头文件avlat-ctl.h,来具体实现ioctl接口通信。
申请设备:
ioctl接口在内核态的主要实现关键代码:
static long
avlat_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0;
/* Ignore tty ioctls */
if ((cmd & 0xffffff00) == ((int)'T') << 8)
return -ENOTTY;
switch (cmd) {
case AVLAT_CFG:
rc = avlat_ioctl_cfg(file, cmd, arg);
break;
case AVLAT_CMD:
rc = avlat_ioctl_cmd(file, cmd, arg);
break;
default:
avlat_print(file, "Bad ioctl command %d\n", cmd);
rc = -EINVAL;
break;
}
return rc;
}
ioctl接口在用户态的调用:
用户态进入内核态调用运行结果:
标注:此篇仅用以说明内核模块测试的用户态和内核态的交互通过代码具体实现的思路,不包含具体代码及代码指导。