Linux驱动编写(块设备驱动代码)
按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。char设备是比较简单的,
只要分配了major、minor号,就可以进行读写处理了。相对而言,block和net要稍微复杂些。net设备
姑且按下不谈,我们在以后的博文中会有涉及。今天,我们可以看看一个简单的block是怎么设计的。
为了将block和fs分开,kernel的设计者定义了request queue这一种形式。换一句话说,所有
fs对block设备的请求,最终都会转变为request的形式。所以,对于block设备驱动开发的朋友来说,
处理好了request queue就掌握了block设备的一半。当然,block设备很多,hd、floppy、ram都可
以这么来定义,有兴趣的朋友可以在drivers/block寻找相关的代码来阅读。兴趣没有那么强的同学,可
以看看我们这篇博文,基本上也能学个大概。有个基本的概念,再加上一个简单浅显的范例,对于一般的朋
友来说,已经足够了。
硬盘基本知识(磁道、扇区、柱面、磁头数、簇、MBR、
再讨论Ramdisk代码驱动前先了解一下Ramdisk:
1 什么是Ramdisk
Ramdisk是一种模拟磁盘,其数据实际上是存储在RAM中,它使用一部分内存空间来模拟出一个磁
盘设备,并以块设备的方式来组织和访问这片内存。对于用户来说可以把Ramdisk与通常的硬盘分区同等对
待来使用。那些经常被访问、并且不会被更改的文件,可以通过Ramdisk被存放在内存中,这样能够明显地
提高系统的响应性能。
2 Ramdisk的产生过程
近几年来,计算机的CPU、内存和显卡等主要配件的性能都提升得很快,而与之相对应的磁盘系统
性能正越来越严重地成为整个电脑系统性能提升的瓶颈。虽然磁盘外部接口也从以前的ATA33发展到今天的
SATA 6Gbit/s。但是,这还是不能彻底解决磁盘瓶颈的问题,特别是在运行一些对数据存取速度要求很高
的程序,如数字影像处理或玩3D游戏装入纹理数据时,受磁盘存取速度的影响,屏幕画面时常会出现延迟和停
顿。于是,虚拟磁盘技术(Ramdisk)应运而生,它可解上述问题的“燃眉之急”。
3 Ramdisk的特点
Ramdisk是基于内存的块设备,以内存作为实际的存储介质,但以块设备的方式组织,所以它具有
比实际磁盘更快的存取速度。但这也带来了另一个问题,当系统重启,内存掉电后,Ramdisk中存储的数据也
将会随之消失。所以Ramdisk不适合作为长期保存文件的介质。
3 Ramdisk的特点
Ramdisk是基于内存的块设备,以内存作为实际的存储介质,但以块设备的方式组织,所以它具有
比实际磁盘更快的存取速度。但这也带来了另一个问题,当系统重启,内存掉电后,Ramdisk中存储的数据也
将会随之消失。所以Ramdisk不适合作为长期保存文件的介质。
4 Ramdisk的作用
Ramdisk磁盘对于保存加密数据来说是一个福音,因为我们如果将加密的文件解密到普通的磁盘的话,即使
我们随后删除了解密文件,数据仍然会留在磁盘上。这样是非常不安全的。而对于Ramdiak来说,就不存在这
样的问题。另外,假设有几个文件要频繁的使用,你如果将它们加到内存当中,程序运行速度会大幅提高,这
是由存储介质的特性决定的(因为内存的读写速度远高于硬盘)。像Web服务器这样的计算机,需要大量的读
取和交换特定的文件,因此,在Web服务器上建立Ramdisk会大大提高网络读取的速度。
闲话不多说,我们看看一个ramdisk代码驱动是怎么写的,代码来自《深入linux 设备驱动程序内核机制》,
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
-
#include <linux/fs.h>
-
#include <linux/types.h>
-
#include <linux/fcntl.h>
-
#include <linux/vmalloc.h>
-
#include <linux/blkdev.h>
-
#include <linux/hdreg.h>
-
-
#define RAMHD_NAME "ramhd"
-
#define RAMHD_MAX_DEVICE 2
-
#define RAMHD_MAX_PARTITIONS 4
-
-
#define RAMHD_SECTOR_SIZE 512
-
#define RAMHD_SECTORS 16
-
#define RAMHD_HEADS 4
-
#define RAMHD_CYLINDERS 256
-
-
#define RAMHD_SECTOR_TOTAL (RAMHD_SECTORS * RAMHD_HEADS *RAMHD_CYLINDERS)
-
#define RAMHD_SIZE (RAMHD_SECTOR_SIZE * RAMHD_SECTOR_TOTAL) //8mb
-
-
typedef struct {
-
unsigned char* data;
-
struct request_queue* queue;
-
struct gendisk* gd;
-
}RAMHD_DEV;
-
-
static char* sdisk[RAMHD_MAX_DEVICE] = {NULL};
-
static RAMHD_DEV* rdev[RAMHD_MAX_DEVICE] = {NULL};
-
-
static dev_t ramhd_major;
-
-
static int ramhd_space_init(void)
-
{
-
int i;
-
int err = 0;
-
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
-
sdisk[i] = vmalloc(RAMHD_SIZE);
-
if(!sdisk[i]){
-
err = -ENOMEM;
-
return err;
-
}
-
-
memset(sdisk[i], 0, RAMHD_SIZE);
-
}
-
-
return err;
-
}
-
-
static void ramhd_space_clean(void)
-
{
-
int i;
-
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
-
vfree(sdisk[i]);
-
}
-
}
-
-
static int ramhd_open(struct block_device* bdev, fmode_t mode)
-
{
-
return 0;
-
}
-
-
static int ramhd_release(struct gendisk*gd, fmode_t mode)
-
{
-
return 0;
-
}
-
-
static int ramhd_ioctl(struct block_device* bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
-
{
-
int err;
-
struct hd_geometry geo;
-
-
switch(cmd)
-
{
-
case HDIO_GETGEO:
-
err = !access_ok(VERIFY_WRITE, arg, sizeof(geo));
-
if(err)
-
return -EFAULT;
-
-
geo.cylinders = RAMHD_CYLINDERS;
-
geo.heads = RAMHD_HEADS;
-
geo.sectors = RAMHD_SECTORS;
-
geo.start = get_start_sect(bdev);
-
-
if(copy_to_user((void*)arg, &geo, sizeof(geo)))
-
return -EFAULT;
-
-
return 0;
-
}
-
-
return -ENOTTY;
-
}
-
-
static struct block_device_operations ramhd_fops = {
-
.owner = THIS_MODULE,
-
.open = ramhd_open,
-
.release = ramhd_release,
-
.ioctl = ramhd_ioctl,
-
};
-
-
static int ramhd_make_request(struct request_queue* q, struct bio* bio)
-
{
-
char* pRHdata;
-
char* pBuffer;
-
struct bio_vec* bvec;
-
int i;
-
int err = 0;
-
-
struct block_device* bdev = bio->bi_bdev;
-
RAMHD_DEV* pdev = bdev->bd_disk->private_data;
-
-
if(((bio->bi_sector * RAMHD_SECTOR_SIZE) + bio->bi_size) > RAMHD_SIZE){
-
err = -EIO;
-
return err;
-
}
-
-
pRHdata = pdev->data + (bio->bi_sector * RAMHD_SECTOR_SIZE);
-
bio_for_each_segment(bvec, bio, i){
-
pBuffer = kmap(bvec->bv_page) + bvec->bv_offset;
-
switch(bio_data_dir(bio)){
-
case READ:
-
memcpy(pBuffer, pRHdata, bvec->bv_len);
-
flush_dcache_page(bvec->bv_page);
-
break;
-
-
case WRITE:
-
flush_dcache_page(bvec->bv_page);
-
memcpy(pRHdata, pBuffer, bvec->bv_len);
-
break;
-
-
default:
-
kunmap(bvec->bv_page);
-
goto out;
-
}
-
-
kunmap(bvec->bv_page);
-
pRHdata += bvec->bv_len;
-
}
-
-
out:
-
bio_endio(bio, err);
-
return 0;
-
}
-
-
static int alloc_ramdev(void)
-
{
-
int i;
-
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
-
rdev[i] = kzalloc(sizeof(RAMHD_DEV), GFP_KERNEL);
-
if(!rdev[i]){
-
return -ENOMEM;
-
}
-
}
-
-
return 0;
-
}
-
-
static void clean_ramdev(void)
-
{
-
int i;
-
-
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
-
if(rdev[i])
-
kfree(rdev[i]);
-
}
-
}
-
-
static int __init ramhd_init(void)
-
{
-
int i;
-
-
ramhd_space_init();
-
alloc_ramdev();
-
-
ramhd_major = register_blkdev(0, RAMHD_NAME);
-
-
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
-
rdev[i]->data = sdisk[i];
-
rdev[i]->queue = blk_alloc_queue(GFP_KERNEL);
-
blk_queue_make_request(rdev[i]->queue, ramhd_make_request);
-
-
rdev[i]->gd = alloc_disk(RAMHD_MAX_PARTITIONS);
-
rdev[i]->gd->major = ramhd_major;
-
rdev[i]->gd->first_minor = i * RAMHD_MAX_PARTITIONS;
-
rdev[i]->gd->fops = &ramhd_fops;
-
rdev[i]->gd->queue = rdev[i]->queue;
-
rdev[i]->gd->private_data = rdev[i];
-
sprintf(rdev[i]->gd->disk_name, "ramhd%c", 'a' +i);
-
rdev[i]->gd->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
-
set_capacity(rdev[i]->gd, RAMHD_SECTOR_TOTAL);
-
add_disk(rdev[i]->gd);
-
}
-
-
return 0;
-
}
-
-
static void __exit ramhd_exit(void)
-
{
-
int i;
-
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
-
del_gendisk(rdev[i]->gd);
-
put_disk(rdev[i]->gd);
-
blk_cleanup_queue(rdev[i]->queue);
-
}
-
-
clean_ramdev();
-
ramhd_space_clean();
-
unregister_blkdev(ramhd_major, RAMHD_NAME);
-
}
-
-
module_init(ramhd_init);
-
module_exit(ramhd_exit);
-
-
MODULE_AUTHOR("laizibi1220@gmail.com");
-
MODULE_DESCRIPTION("The ramdisk implementation with request function");
-
MODULE_LICENSE("GPL");
为了大家方便,顺便也把Makefile放出来,这其实很简单。
-
ifneq ($(KERNELRELEASE),)
-
obj-m := ramdisk.o # 产生ramdisk 模块的目标文件
-
else
-
PWD := $(shell pwd) #模块所在的当前路径
-
LINUX_KERNEL := $(shell uname -r) #Linux内核源代码的当前版本
-
LINUX_KERNEL_PATH := /usr/src/kernels/$(LINUX_KERNEL)/ #Linux内核源代码的绝对路径
-
all:
-
$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(PWD) modules
-
clean:
-
rm -rf *.o *.mod.c *.ko *.ko.* modules.* Module.*
-
endif
这段代码究竟有没有用呢?可以按照下面的步骤来做,
a)make 一下,生成ramdisk.ko;
b)编译好了之后,就可以安装驱动了,在linux下是这么做的,sudo insmod ramdisk.ko;
c)安装好了,利用ls /dev/ramhd*, 就会发现在/dev下新增两个结点,即/dev/ramhda和/dev/ramhdb;
d)不妨选择其中一个节点进行分区处理, sudo fdisk /dev/ramhda,简单处理的话就建立一个分区, 生成/dev/ramhda1;
e)创建文件系统,sudo mkfs.ext3 /dev/ramhda1;
f)有了上面的文件系统,就可以进行mount处理,不妨sudo mount /dev/ramhda1 /mnt;
g)上面都弄好了,大家就可以copy、delete文件试试了,是不是很简单。
具体操作步骤如下:
-
[root@localhost Desktop]# make
-
make -C /usr/src/kernels/2.6.32-279.el6.i686 / M=/root/Desktop modules
-
make[1]: Entering directory `/usr/src/kernels/2.6.32-279.el6.i686'
-
LD /root/Desktop/built-in.o
-
CC [M] /root/Desktop/ramdisk.o
-
Building modules, stage 2.
-
MODPOST 1 modules
-
CC /root/Desktop/ramdisk.mod.o
-
LD [M] /root/Desktop/ramdisk.ko.unsigned
-
NO SIGN [M] /root/Desktop/ramdisk.ko
-
make[1]: Leaving directory `/usr/src/kernels/2.6.32-279.el6.i686'
-
[root@localhost Desktop]# insmod ramdisk.ko
-
[root@localhost Desktop]# ls /dev/ramhd*
-
/dev/ramhda /dev/ramhdb
-
[root@localhost Desktop]# fdisk /dev/ramhda
-
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
-
Building a new DOS disklabel with disk identifier 0x73569d74.
-
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)
-
-
WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
-
switch off the mode (command 'c') and change display units to
-
sectors (command 'u').
-
-
Command (m for help): m
-
Command action
-
a toggle a bootable flag
-
b edit bsd disklabel
-
c toggle the dos compatibility flag
-
d delete a partition
-
l list known partition types
-
m print this menu
-
n add a new partition
-
o create a new empty DOS partition table
-
p print the partition table
-
q quit without saving changes
-
s create a new empty Sun disklabel
-
t change a partition's system id
-
u change display/entry units
-
v verify the partition table
-
w write table to disk and exit
-
x extra functionality (experts only)
-
-
Command (m for help): p
-
-
Disk /dev/ramhda: 8 MB, 8388608 bytes
-
255 heads, 63 sectors/track, 1 cylinders
-
Units = cylinders of 16065 * 512 = 8225280 bytes
-
Sector size (logical/physical): 512 bytes / 512 bytes
-
I/O size (minimum/optimal): 512 bytes / 512 bytes
-
Disk identifier: 0x73569d74
-
-
Device Boot Start End Blocks Id System
-
-
Command (m for help): n
-
Command action
-
e extended
-
p primary partition (1-4)
-
p
-
Partition number (1-4): 1
-
First cylinder (1-1, default 1): 1
-
-
Command (m for help): p
-
-
Disk /dev/ramhda: 8 MB, 8388608 bytes
-
255 heads, 63 sectors/track, 1 cylinders
-
Units = cylinders of 16065 * 512 = 8225280 bytes
-
Sector size (logical/physical): 512 bytes / 512 bytes
-
I/O size (minimum/optimal): 512 bytes / 512 bytes
-
Disk identifier: 0x73569d74
-
-
Device Boot Start End Blocks Id System
-
/dev/ramhda1 1 1 8001 83 Linux
-
-
Command (m for help): w
-
The partition table has been altered!
-
-
Calling ioctl() to re-read partition table.
-
Syncing disks.
-
[root@localhost Desktop]# mkfs.ext3 /dev/ramhda1
-
mke2fs 1.41.12 (17-May-2010)
-
文件系统标签=
-
操作系统:Linux
-
块大小=1024 (log=0)
-
分块大小=1024 (log=0)
-
Stride=0 blocks, Stripe width=0 blocks
-
2000 inodes, 8000 blocks
-
400 blocks (5.00%) reserved for the super user
-
第一个数据块=1
-
Maximum filesystem blocks=8388608
-
1 block group
-
8192 blocks per group, 8192 fragments per group
-
2000 inodes per group
-
-
正在写入inode表: 完成
-
Creating journal (1024 blocks): 完成
-
Writing superblocks and filesystem accounting information: 完成
-
-
This filesystem will be automatically checked every 33 mounts or
-
180 days, whichever comes first. Use tune2fs -c or -i to override.
-
[root@localhost Desktop]# mount /dev/ramhda1 /mnt
-
[root@localhost Desktop]# cd /mnt
-
[root@localhost mnt]# ll
-
总用量 12
-
drwx------. 2 root root 12288 5月 30 15:29 lost+found
-
[root@localhost mnt]# gedit hello.c
-
[root@localhost mnt]# gcc -o hello hello.c
-
[root@localhost mnt]# ./hello
-
Hello World!
-
[root@localhost mnt]#
阅读(1023) | 评论(0) | 转发(1) |