第8章
+---------------------------------------------------+
| 写一个块设备驱动 |
+---------------------------------------------------+
| 作者:赵磊 |
| email: [email protected] |
+---------------------------------------------------+
| 文章版权归原作者所有。 |
| 大家可以自由转载这篇文章,但原版权信息必须保留。 |
| 如需用于商业用途,请务必与原作者联系,若因未取得 |
| 授权而收起的版权争议,由侵权者自行负责。 |
+---------------------------------------------------+
本章的目的是让读者继续休息,因此决定仍然搞一些简单的东西。
比如:给我们的驱动程序模块加上模块参数,这样在加载模块时,可以通过参数设定块设备的大小。
给我们模块加参数的工作不难,这牵涉到1个宏:
module_param_named(name, value, type, perm)
name是参数的名称
value是参数在模块中对应的变量
type是参数的类型
perm是参数的权限
如,在模块中添加
int disk_size = 1024;
module_param_named(size, disk_size, int, S_IRUGO);
可以给模块加上名称为"size"的参数,如果在加载模块是使用insmod thismodule size=100,那么在模块代码中disk_size的值就是100。
相反,如果加载模块时没有指定参数,那么模块代码中disk_size的值仍是默认的1024。
S_IRUGO指定了这个参数的值在模块加载以后可以被所有人通过/sys/module/[module_name]/parameters/看到,但无法修改。
好了,有关module_param_named就介绍到这里,细节可以google或者看linux/include/linux/moduleparam.h。
然后我们就要给这个模块加个参数,用来在加载时指定块设备的大小。
参数的名字都已经想好了,就叫size吧,类型嘛,32位无符号整数最大能设定到4G,而我们的野心看起来可能更大一些,
为了让这个模块支持4G以上的虚拟磁盘(当然是内存足够的情况下),我们打算使用64位无符号整型。这样能够设定的最大值为16777216T,应该够了吧。
然后我们试图找出module_param_named的参数中与unsigned long long对应的type来。
结果是:google了,没找到;看linux/include/linux/moduleparam.h了,还是没找到。
结论是:目前的linux(2.6.28)还不支持unsigned long long类型的模块参数。
更新一些的内核中会不会有是将来的事,尽快搞定这一章的功能却是现在面临的问题。
然后我们就开始找解决方案:
1:给内核打个补丁,看样子不错,但至少今天之类完成不了我们的程序了
并且这样一来,我们的程序只能在今后的内核中运行,而失去对旧版linux的兼容性。
2:指定设置磁盘大小的单位为M。这样可设置的最大的数字就成了4G*1M,也就是4096T。
这个主意看似不错。而且看样子10年内机器的内存应该到不了这个容量。
3:用字符串来指定大小
这倒是可以解决所有问题,并且我们可以支持16M、1G之类的设定,让我们的程序看起来比较花哨。
缺点应该是我们需要在程序中自己去解析传入的字符串了,幸运的是,实际的解析代码比想象的容易一些。
因此,我们采用第3个方案,向模块中添加一个名称为size、类型为字符串的参数,并且支持解析以K,M,G,T为单位的设定。
第1步:
向程序中添加以下参数申明。
static char *simp_blkdev_param_size = "16M";
module_param_named(size, simp_blkdev_param_size, charp, S_IRUGO);
char *simp_blkdev_param_size用于存储设定的磁盘大小,我们把磁盘大小的默认值指定为16M。
目前我们不允许用户在模块加载后改变磁盘大小,将来嘛,有可能增加这一功能,看起来很眩。
第2步:
原来的程序使用
#define SIMP_BLKDEV_BYTES (16*1024*1024)
定义磁盘大小,而现在我们不需要这一行了。
同时,我们需要一个unsigned long long变量来存储用户设定的磁盘大小,因此我们增加这个变量:
static unsigned long long simp_blkdev_bytes;
然后把程序中所有使用SIMP_BLKDEV_BYTES的位置换成使用simp_blkdev_bytes变量。
第3步:
在模块加载时对模块参数进行解析,设置simp_blkdev_bytes变量的值。
我们增加一个函数进行解析工作:
int getparam(void)
{
char unit;
char tailc;
if (sscanf(simp_blkdev_param_size, "%llu%c%c", &simp_blkdev_bytes,
&unit, &tailc) != 2) {
return -EINVAL;
}
if (!simp_blkdev_bytes)
return -EINVAL;
switch (unit) {
case 'g':
case 'G':
simp_blkdev_bytes <<= 30;
break;
case 'm':
case 'M':
simp_blkdev_bytes <<= 20;
break;
case 'k':
case 'K':
simp_blkdev_bytes <<= 10;
break;
case 'b':
case 'B':
break;
default:
return -EINVAL;
}
/* make simp_blkdev_bytes fits sector's size */
simp_blkdev_bytes = (simp_blkdev_bytes + (1<<9) - 1) & ~((1ULL<<9) - 1);
return 0;
}
然后在simp_blkdev_init()中调用这个函数:
ret = getparam();
if (IS_ERR_VALUE(ret))
goto err_getparam;
当然,err_getparam的位置读者应该能猜出来了。
这样一来,工作大概就完成了,让我们看看结果:
使用默认值:
# insmod simp_blkdev.ko
# fdisk /dev/simp_blkdev
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that, of course, the previous
content won't be recoverable.
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Command (m for help): p
Disk /dev/simp_blkdev: 16 MB, 16777216 bytes
1 heads, 32 sectors/track, 1024 cylinders
Units = cylinders of 32 * 512 = 16384 bytes
Device Boot Start End Blocks Id System
Command (m for help): q
#
设定成20M:
# rmmod simp_blkdev
# insmod simp_blkdev.ko size=20M
# fdisk /dev/simp_blkdev
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that, of course, the previous
content won't be recoverable.
The number of cylinders for this disk is set to 1280.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
(e.g., DOS FDISK, OS/2 FDISK)
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Command (m for help): p
Disk /dev/simp_blkdev: 20 MB, 20971520 bytes
1 heads, 32 sectors/track, 1280 cylinders
Units = cylinders of 32 * 512 = 16384 bytes
Device Boot Start End Blocks Id System
Command (m for help): q
#
变态一下,还是设定成20M,但用k作单位:
# rmmod simp_blkdev
# insmod simp_blkdev.ko size=20480k
# fdisk /dev/simp_blkdev
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that, of course, the previous
content won't be recoverable.
The number of cylinders for this disk is set to 1280.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
(e.g., DOS FDISK, OS/2 FDISK)
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Command (m for help): p
Disk /dev/simp_blkdev: 20 MB, 20971520 bytes
1 heads, 32 sectors/track, 1280 cylinders
Units = cylinders of 32 * 512 = 16384 bytes
Device Boot Start End Blocks Id System
Command (m for help): q
#
看样子结果不错。
这一章中基本上没有提到什么比较晦涩的知识,而且看样子通过这一章的学习,大家也应该休息好了。
如果读者现在感觉到精神百倍,那么这一章的目的应该就达到了。
<未完,待续>