第1章
+---------------------------------------------------+
| 写一个块设备驱动 |
+---------------------------------------------------+
同样是读书,读小说可以行云流水,读完后心情舒畅,意犹未尽;读电脑书却举步艰难,读完后目光呆滞,也是意犹未尽,只不过未尽的是痛苦的回忆。
研究证明,痛苦的记忆比快乐的更难忘记,因此电脑书中的内容比小说记得持久。
而这套教程的目的是要打破这种状况,以至于读者在忘记小说内容忘记本文。
在这套教程中,我们通过写一个建立在内存中的块设备驱动,来学习linux内核和相关设备驱动知识。
选择写块设备驱动的原因是:
1:容易上手
2:可以牵连出更多的内核知识
3:像本文这样的块设备驱动教程不多,所以需要一个
好吧,扯淡到此结束,我们开始写了。
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。
所谓的能用,是指我们可以对这个驱动生成的块设备进行mkfs,mount和读写文件。
为了尽可能简单,这个驱动的规模不是1000行,也不是500行,而是100行以内。
这里插一句,我们不打算在这里介绍如何写模块,理由是介绍的文章已经满天飞舞了。
如果你能看得懂、并且成功地编译、运行了这段代码,我们认为你已经达到了本教程的入学资格,
当然,如果你不幸的卡在这段代码中,那么请等到搞定它以后再往下看:
mod.c:
#include <linux/module.h>
static int __init init_base(void)
{
printk("----Hello. World----\n");
return 0;
}
static void __exit exit_base(void)
{
printk("----Bye----\n");
}
module_init(init_base);
module_exit(exit_base);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR("Zhao Lei");
MODULE_DESCRIPTION("For test");
Makefile:
obj-m := mod.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
rm -rf Module.markers modules.order Module.symvers
好了,这里我们假定你已经搞定上面的最简单的模块了,懂得什么是看模块,以及简单模块的编写、编译、加载和卸载。
还有就是,什么是块设备,什么是块设备驱动,这个也请自行google吧,因为我们已经迫不及待要写完程序下课。
为了建立一个可用的块设备,我们需要做......1件事情:
1:用add_disk()函数向系统中添加这个块设备
添加一个全局的
static struct gendisk *simp_blkdev_disk;
然后申明模块的入口和出口:
module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);
然后在入口处添加这个设备、出口处私房这个设备:
static int __init simp_blkdev_init(void)
{
add_disk(simp_blkdev_disk);
return 0;
}
static void __exit simp_blkdev_exit(void)
{
del_gendisk(simp_blkdev_disk);
}
当然,在添加设备之前我们需要申请这个设备的资源,这用到了alloc_disk()函数,因此模块入口函数simp_blkdev_init(void)应该是:
static int __init simp_blkdev_init(void)
{
simp_blkdev_disk = alloc_disk(1);
if (!simp_blkdev_disk) {
ret = -ENOMEM;
goto err_alloc_disk;
}
add_disk(simp_blkdev_disk);
return 0;
err_alloc_disk:
return ret;
}
还有别忘了在卸载模块的代码中也加一个行清理函数:
put_disk(simp_blkdev_disk);
还有就是,设备有关的属性也是需要设置的,因此在alloc_disk()和add_disk()之间我们需要:
strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
simp_blkdev_disk->major = ?1;
simp_blkdev_disk->first_minor = 0;
simp_blkdev_disk->fops = ?2;
simp_blkdev_disk->queue = ?3;
set_capacity(simp_blkdev_disk, ?4);
SIMP_BLKDEV_DISKNAME其实是这个块设备的名称,为了绅士一些,我们把它定义成宏了:
#define SIMP_BLKDEV_DISKNAME "simp_blkdev"
这里又引出了4个问号。(天哪,是不是有种受骗的感觉,像是陪老婆去做头发)
第1个问号:
每个设备需要对应的主、从驱动号。
我们的设备当然也需要,但很明显我不是脑科医生,因此跟写linux的那帮疯子不熟,得不到预先为我保留的设备号。
还有一种方法是使用动态分配的设备号,但在这
+---------------------------------------------------+
| 写一个块设备驱动 |
+---------------------------------------------------+
同样是读书,读小说可以行云流水,读完后心情舒畅,意犹未尽;读电脑书却举步艰难,读完后目光呆滞,也是意犹未尽,只不过未尽的是痛苦的回忆。
研究证明,痛苦的记忆比快乐的更难忘记,因此电脑书中的内容比小说记得持久。
而这套教程的目的是要打破这种状况,以至于读者在忘记小说内容忘记本文。
在这套教程中,我们通过写一个建立在内存中的块设备驱动,来学习linux内核和相关设备驱动知识。
选择写块设备驱动的原因是:
1:容易上手
2:可以牵连出更多的内核知识
3:像本文这样的块设备驱动教程不多,所以需要一个
好吧,扯淡到此结束,我们开始写了。
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。
所谓的能用,是指我们可以对这个驱动生成的块设备进行mkfs,mount和读写文件。
为了尽可能简单,这个驱动的规模不是1000行,也不是500行,而是100行以内。
这里插一句,我们不打算在这里介绍如何写模块,理由是介绍的文章已经满天飞舞了。
如果你能看得懂、并且成功地编译、运行了这段代码,我们认为你已经达到了本教程的入学资格,
当然,如果你不幸的卡在这段代码中,那么请等到搞定它以后再往下看:
mod.c:
#include <linux/module.h>
static int __init init_base(void)
{
printk("----Hello. World----\n");
return 0;
}
static void __exit exit_base(void)
{
printk("----Bye----\n");
}
module_init(init_base);
module_exit(exit_base);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR("Zhao Lei");
MODULE_DESCRIPTION("For test");
Makefile:
obj-m := mod.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
rm -rf Module.markers modules.order Module.symvers
好了,这里我们假定你已经搞定上面的最简单的模块了,懂得什么是看模块,以及简单模块的编写、编译、加载和卸载。
还有就是,什么是块设备,什么是块设备驱动,这个也请自行google吧,因为我们已经迫不及待要写完程序下课。
为了建立一个可用的块设备,我们需要做......1件事情:
1:用add_disk()函数向系统中添加这个块设备
添加一个全局的
static struct gendisk *simp_blkdev_disk;
然后申明模块的入口和出口:
module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);
然后在入口处添加这个设备、出口处私房这个设备:
static int __init simp_blkdev_init(void)
{
add_disk(simp_blkdev_disk);
return 0;
}
static void __exit simp_blkdev_exit(void)
{
del_gendisk(simp_blkdev_disk);
}
当然,在添加设备之前我们需要申请这个设备的资源,这用到了alloc_disk()函数,因此模块入口函数simp_blkdev_init(void)应该是:
static int __init simp_blkdev_init(void)
{
simp_blkdev_disk = alloc_disk(1);
if (!simp_blkdev_disk) {
ret = -ENOMEM;
goto err_alloc_disk;
}
add_disk(simp_blkdev_disk);
return 0;
err_alloc_disk:
return ret;
}
还有别忘了在卸载模块的代码中也加一个行清理函数:
put_disk(simp_blkdev_disk);
还有就是,设备有关的属性也是需要设置的,因此在alloc_disk()和add_disk()之间我们需要:
strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
simp_blkdev_disk->major = ?1;
simp_blkdev_disk->first_minor = 0;
simp_blkdev_disk->fops = ?2;
simp_blkdev_disk->queue = ?3;
set_capacity(simp_blkdev_disk, ?4);
SIMP_BLKDEV_DISKNAME其实是这个块设备的名称,为了绅士一些,我们把它定义成宏了:
#define SIMP_BLKDEV_DISKNAME "simp_blkdev"
这里又引出了4个问号。(天哪,是不是有种受骗的感觉,像是陪老婆去做头发)
第1个问号:
每个设备需要对应的主、从驱动号。
我们的设备当然也需要,但很明显我不是脑科医生,因此跟写linux的那帮疯子不熟,得不到预先为我保留的设备号。
还有一种方法是使用动态分配的设备号,但在这