Chinaunix首页 | 论坛 | 博客
  • 博客访问: 306591
  • 博文数量: 111
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 672
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-05 23:41
文章分类

全部博文(111)

文章存档

2017年(111)

我的朋友

分类: LINUX

2017-06-29 14:10:41

1. 块设备概念:块设备是指只能以块为单位进行访问的设备,块的大小一般是512个字节的整数倍。常见的块设备包括硬件,SD卡,光盘等。

 

.

 

上边是通过一个编写好的块设备驱动,然后安装块设备驱动以及一些相关操作来体会块设备驱动!(此处省略)

2. 块设备驱动的架构

 

.

 

2.1 系统架构---VFS

VFS是对各种具体文件系统的一种封装,用户程序访问文件提供统一的接口。

 

.

 

2.2 系统架构---Cache

当用户发起文件访问请求的时候,首先回到Disk Cache中寻址文件是否被缓存了,如果在Cache,则直接从cache中读取。如果数据不在缓存中,就必须要到具体的文件系统中读取数据了。

2.3 Mapping Layer

2.3.1 首先确定文件系统的block size,然后计算所请求的 数据包含多少个block.

2.3.2 调用具体文件系统的函数来访问文件的inode结构,确定所请求的数据在磁盘上的地址。

2.4 Generic Block Layer

内核把块设备看做是由若干个扇区组成的数据空间,上层的读写请求在通用块层被构造成一个或多个bio结构。

2.5 I/O Scheduler Layer I/O调度层负责采用某种算法(如:电梯调度算法)将I/O操作进行排序。

电梯调度算法的基本原则:如果电梯现在朝上运动,如果当前楼层的上方和下方都有请求,则先响应所有上方的请求,然后才向下响应下方的请求;如果电梯向下运动,则刚好相反。

2.6 块设备驱动

在块系统架构的最底层,由块设备驱动根据排序好的请求,对硬件进行数据访问。

块设备驱动流程分析:

 

.

 

 

.

 

1.6 注册块设备---add_disk

2. 实现读写请求函数

2.1 取出一个要处理的请求---blk_fetch_request

2.2 更具请求里的信息访问硬件,获取数据

2.3 利用__blk_end_request_cur判读请求队列里是否还有剩余的请求要处理,如果有按照1、2来处理

一个最简单的块设备驱动程序:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include
#include
#include
#include
 
#include
#include /* printk() */
#include    /* kmalloc() */
#include    /* everything... */
#include /* error codes */
#include
#include /* size_t */
#include /* O_ACCMODE */
#include /* HDIO_GETGEO */
#include
#include
#include
#include
#include /* invalidate_bdev */
#include
 
static int major = 0;
 
static int sect_size = 512;//定义每个扇区的大小为512字节
static int nsectors = 1024;//扇区数目
 
struct blk_dev{
    int size;
    u8 *data;
    struct request_queue *queue;
    struct gendisk *gd;
};
 
struct blk_dev *dev;
 
static struct block_device_operations blk_ops = {
    .owner = THIS_MODULE,
};
//static void blk_transfer(struct blk_dev *dev, unsigned long sector,unsigned long nsect, char *buffer, int write)
static void blk_transfer(struct blk_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
{
    unsigned long offset = sector * sect_size;
    unsigned long nbytes = nsect * sector;
     
    if(write)//如果是写操作 将用户空间的数据写到磁盘上去
        memcpy(dev->data + offset, buffer, nbytes);
         
    else
        memcpy(buffer, dev->data + offset, nbytes);
     
     
}
 
static void blk_request(struct request_queue *q)
{
    struct request *req;//保存取出的请求
     
    req = blk_fetch_request(q);//从请求队列中取出一个请求
     
    while(req != NULL)
    {
        //处理该请求
         
        //操作的起始扇区 请求操作扇区的数目 数据读出来放到哪里, 或写数据来自哪里
        blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
        //blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
         
        if( !__blk_end_request_cur(req, 0) )//判读如果不是最后一个请求
            req = blk_fetch_request(q);//再去取一个请求
    }
}
 
void setup_device(void)
{
    dev->size = nsectors * sect_size;
     
    dev->data = vmalloc(dev->size);
     
    dev->queue = blk_init_queue(blk_request, NULL);//初始化请求队列
     
    blk_queue_logical_block_size(dev->queue, sect_size);//设置扇区尺寸
     
    dev->gd = alloc_disk(1);//分配块设备结构
     
    dev->gd->major = major;
    dev->gd->first_minor = 0;
    dev->gd->fops = &blk_ops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    sprintf(dev->gd->disk_name, "simp_blk%d", 0);//设备名为simp_blk0
    set_capacity(dev->gd, nsectors);//设置扇区数
     
    add_disk(dev->gd);
     
}
 
int blk_init(void)
{
    //两个参数 第一个参数为0表示为动态分配设备号 返回主设备号
    major = register_blkdev(0, "blk");//注册块设备驱动程序
     
    if( major <= 0 )
    {
        printk("register blk dev fail!\n");
         
        return -EBUSY;
    }
     
    dev = kmalloc(sizeof(struct blk_dev),GFP_KERNEL);
     
    setup_device();
     
    return 0;
}
 
void blk_exit(void)
{
    del_gendisk(dev->gd);
     
    blk_cleanup_queue(dev->queue);
     
    vfree(dev->data);
     
    unregister_blkdev(major, "blk");
     
    kfree(dev);
}
 
module_init(blk_init);
module_exit(blk_exit);

编译安装 格式化成ext3的时候直接重启了!原来驱动的BUG动不动就把系统搞挂!

 

这个是可以运行的:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#include
#include
#include
 
#include
#include /* printk() */
#include    /* kmalloc() */
#include    /* everything... */
#include /* error codes */
#include
#include /* size_t */
#include /* O_ACCMODE */
#include /* HDIO_GETGEO */
#include
#include
#include
#include
#include /* invalidate_bdev */
#include
 
MODULE_LICENSE("Dual BSD/GPL");
 
static int major = 0;
 
static int sect_size = 512;
 
static int nsectors = 1024;
 
/*
* The internal representation of our device.
*/
struct blk_dev{
         int size;                        /* Device size in sectors */
         u8 *data;                        /* The data array */
         struct request_queue *queue;     /* The device request queue */
         struct gendisk *gd;              /* The gendisk structure */
};
 
struct blk_dev *dev;
 
 
/*
* Handle an I/O request, in sectors.
*/
static void blk_transfer(struct blk_dev *dev, unsigned long sector,
   unsigned long nsect, char *buffer, int write)
{
unsigned long offset = sector*sect_size;
unsigned long nbytes = nsect*sect_size;
 
if ((offset + nbytes) > dev->size) {
   printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
   return;
}
if (write)
   memcpy(dev->data + offset, buffer, nbytes);
else
   memcpy(buffer, dev->data + offset, nbytes);
}
 
/*
* The simple form of the request function.
*/
static void blk_request(struct request_queue *q)
{
struct request *req;
 
req = blk_fetch_request(q);
while (req != NULL) {
   struct blk_dev *dev = req->rq_disk->private_data;
 
   blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
    
   if(!__blk_end_request_cur(req, 0))
   {
    req = blk_fetch_request(q);
   }
}
}
 
 
/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
 
/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
   char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
   blk_transfer(dev, sector, bio_cur_bytes(bio)>>9 /* in sectors */,
    buffer, bio_data_dir(bio) == WRITE);
   sector += bio_cur_bytes(bio)>>9; /* in sectors */
   __bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}
 
/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{
struct bio *bio;
int nsect = 0;
      
__rq_for_each_bio(bio, req) {
   blk_xfer_bio(dev, bio);
   nsect += bio->bi_size/sect_size;
}
return nsect;
}
 
 
 
/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = {
.owner            = THIS_MODULE,
};
 
 
/*
* Set up our internal device.
*/
static void setup_device()
{
/*
* Get some memory.
*/
dev->size = nsectors*sect_size;
dev->data = vmalloc(dev->size);
if (dev->data == NULL) {
   printk (KERN_NOTICE "vmalloc failure.\n");
   return;
}
 
   dev->queue = blk_init_queue(blk_request, NULL);
   if (dev->queue == NULL)
    goto out_vfree;
 
blk_queue_logical_block_size(dev->queue, sect_size);
dev->queue->queuedata = dev;
/*
* And the gendisk structure.
*/
dev->gd = alloc_disk(1);
if (! dev->gd) {
   printk (KERN_NOTICE "alloc_disk failure\n");
   goto out_vfree;
}
dev->gd->major = major;
dev->gd->first_minor = 0;
dev->gd->fops = &blk_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
sprintf (dev->gd->disk_name, "simp_blk%d", 0);
set_capacity(dev->gd, nsectors*(sect_size/sect_size));
add_disk(dev->gd);
return;
 
out_vfree:
if (dev->data)
   vfree(dev->data);
}
 
static int __init blk_init(void)
{
/*
* Get registered.
*/
major = register_blkdev(major, "blk");
if (major <= 0) {
   printk(KERN_WARNING "blk: unable to get major number\n");
   return -EBUSY;
}
 
dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
if (dev == NULL)
   goto out_unregister;
 
   setup_device();
      
return 0;
 
out_unregister:
unregister_blkdev(major, "sbd");
return -ENOMEM;
}
 
static void blk_exit(void)
{
 
   if (dev->gd) {
    del_gendisk(dev->gd);
    put_disk(dev->gd);
   }
   if (dev->queue)
    blk_cleanup_queue(dev->queue);
   if (dev->data)
    vfree(dev->data);
 
   unregister_blkdev(major, "blk");
kfree(dev);
}
 
module_init(blk_init);
module_exit(blk_exit);
makefile:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
ifneq ($(KERNELRELEASE),)
 
obj-m := simple-blk.o
 
else
 
KDIR := /lib/modules/2.6.32-279.el6.i686/build
all:
    make -C $(KDIR) M=$(PWD) modules
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers
 
endif
这个块设备驱动的测试上面也有步骤!重点是理解块设备驱动的大体流程!对块设备驱动又个大体印象!

 

下面来介绍一个和下面即将要出场的flash驱动相关的知识!

MTD

 

MTD设备体验:Flash在嵌入式系统中是必不可少,它是bootloader、linux内核和文件系统的最佳载体。在linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

 

块设备驱动系统架构:

.




.

先过一遍流程!额 ,徒手撸驱动代码这难度还真不是一般大!后边边学边提高吧!

阅读(2346) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~