Monday, June 29, 2009

Linux 设备驱动程序调试环境

为了建立一个调试环境,首先要有一个Linux源代码目录。我使用了linux-2.6.19.2作为工作目录。

其次,最方便的调试环境是kvm。只要键入
kvm -kernel <内核映像> -initrd 即可。

在源码目录下输入
make mrproper
清除杂物。
然后
make allnoconfig
把所有选项设为no
make menuconfig
增加:initramfs支持,可加载模块支持,内核ELF支持
然后make即可得到一个适合于kvm的内核。

为了生成initrd, 首先创建一个image目录,在里面创建dev目录,然后在image目录下运行
sudo mknod -m 600 dev/console c 5 1
然后编写init.c,静态编译后放到image目录。

在image目录下执行如下命令
find . | cpio -o -H newc | gzip > ../image.cpio.gz
即可生成一个可以用于kvm加载的initrd映像。

现在,我们要编写一个最简单的模块。
#include
#include

static int __init mymodule_init(void) {
printk(KERN_ALERT "Hello, World Module\n");
return 0;
}

static void __exit mymodule_exit(void) {
printk(KERN_ALERT "Goodbye, cruel world\n");
}

module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
将该文件保存为mymodule.c, 再创建一个Makefile包含如下内容:
obj-m:mymodule.o

然后在Makefile所在目录下用如下命令编译
make -C M=`pwd` modules
这样就创建了一个mymodule.ko文件。把它放到创建initrd所用的image目录下。

现在,我们要编写init.c使它能加载我们的模块。

#include
#include

在头文件部分,我们只需要以上2个。stdio.h提供了标准IO,而stdlib.h提供了malloc等重要函数。

extern long init_module(void *,unsigned long, const char *);
以上函数实际上在glibc中定义,但没有查到它在哪个头文件中。这是模仿insmod命令源代码写出来的。该函数的第一个参数指向一个内存块,第二个参数是它的大小,最后一个参数是给模块初始化传递的参数。

为了加载模块,首先我们要把模块文件读入内存,然后才能把第一个参数传递给init_module函数。所以这里需要一个函数来做这件事情。函数里需要获取文件大小,这也可以专门写个简单函数来完成:
static long
get_filesize(FILE *file) {
long curpos, length;

curpos = ftell(file);
fseek(file, 0L, SEEK_END);
length = ftell(file);
fseek(file, curpos,SEEK_SET);
return length;
}

static void *
grab_file(const char *name, size_t *size) {
FILE *file = fopen(name,"rb");
*size = get_filesize(file);
printf("file size = %ld\n", *size);

char *buffer=malloc(*size+1);
buffer[*size] = '\0';

size_t ret = fread(buffer, 1, *size, file);
printf("read size = %ld\n", ret);

fclose(file);

return buffer;
}

因为这只是一个最简易的环境,所以插入模块之后我们要确保模块已经加载了。为此,我们需要一些命令来显示加载的模块。当然我们可以模仿lsmod来写,但是为了今后开发方便起见,最好能把有关信息显示得全一些。为此我们需要把sys和proc文件系统安装到initrd环境里。这只需要在initrd的根目录下创建sys和proc两个目录,然后在init.c里面编写mountsysfs函数:
static void
mountsysfs() {
int i=mount("sysfs","/sys","sysfs",0,NULL);
if(i!=0)printf("Mount sysfs failue - %d", errno);
i=mount("proc","/proc","proc",0,NULL);
if(i!=0)printf("Mount procfs failue - %d", errno);
}

最后,我们需要两个函数分别实现Linux命令ls和cat的功能。
static void
listdir(const char *dirname) {
DIR *pdir = opendir(dirname);
if(!pdir)return;

for(;;) {
struct dirent *ent = readdir(pdir);
if(!ent)break;
printf("%s\n", ent->d_name);
}

closedir(pdir);
}

static void
cat(const char *filename) {
char buffer[1024];
FILE *file = fopen(filename, "rt");

while(fgets(buffer, sizeof(buffer), file)!=NULL) {
printf("%s",buffer);
}
printf("\n");
}

好了,最后编写一个main函数,就可以进行测试了。
int
main(int argc, char *argv[]) {
printf("---Entering INIT.exe Test------\n");

size_t len;
void *file = grab_file("mymodule.ko", &len);

printf("init_module returns %ld\n", init_module(file, len, ""));

mountsysfs();

listdir("/sys/module");
}

编译后,按照前面所说的方法建立initrd,启动kvm即可看到模块目录。


Thursday, January 01, 2009

2009.1.1

今天是2009年的第一天,为了应付这大好的日子,天气也出其得好,一大早爸爸妈妈就带着宝宝出街来感受这喜庆的节日。太阳晒在人身上感觉懒洋洋地,很是舒服。首先我们来到了金山公园,可惜若大的一个公园人影也不见一个,好郁闷!接着就到了邻近的金山度假村, 这里也不比公园强多少,有点失望。

不过,马上就被丰盛的午餐给赶走了,中午一家人在海鲜酒楼美美的大吃了一餐,味道好极了!我想在以后的日子里,也会值得慢慢回味。

宝宝的湿疹没有好多少,脸上还是红彤彤得。心情比较急燥,烦!!

在新的一年里,妈妈先祝:宝宝健健康康的长大!再祝:外公外婆,爷爷奶奶身体健康,爸爸,舅舅,叔叔心想事成,财源广进!全家人平平安安!