⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 1036.html

📁 著名的linux英雄站点的文档打包
💻 HTML
📖 第 1 页 / 共 4 页
字号:
                          <TR>
                            <TD background="images/bgi.gif" tppabs="http://www.linuxhero.com/docs/images/bgi.gif" 
                          height=30></TD></TR></TBODY></TABLE>
                        <TABLE cellSpacing=0 cellPadding=3 width="95%" 
                        align=center border=0>
                          <TBODY>
                          <TR>
                            <TD>
                              <TABLE cellSpacing=0 cellPadding=3 width="100%" 
                              border=0>
                                <TBODY>
                                <TR>
                                      <TD vAlign=top> 
<p><FONT class=normalfont><B><font color=blue>设备驱动</font></B></FONT><BR><FONT class=smallfont color=#ff9900>2004-04-23 15:18 pm</FONT><BR><FONT class=normalfont>作者:作者<br>来自:Linux知识宝库<br>联系方式:无名<br><br>概述<br>
<br>
一. linux设备概述<br>
在概念上一般把设备分为字符设备、块设备。字符设备是指设备发送和接收数据以字符形式的进行;而块设备则以整个数据缓冲区的形式进行。但是,由于网络设备等有其特殊性,实际上系统对它们单独处理。<br>
<br>
系统用主设备号(MAJOR)加次设备(MINOR)号来唯一标识一个设备。相同主设备号表示同一类设备,例如都是硬盘;次设备号标识同类设备的个数。所有设备在适当的目录(通常在/dev目录下)下必须有相应的文件,这样字符设备和块设备都可以通过文件操作的系统调用了完成。不同的是,块设备操作经常要和缓冲区打交道,更加复杂一点。<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
数据结构<br>
<br>
二. 主要数据结构<br>
与设备管理有关的主要数据结构如下:<br>
1、登记设备管理<br>
系统对已登记设备的管理是由chrdevs和blkdevs这两张列表来完成的:<br>
/*srcfsdevices.c*/<br>
struct device_struct {<br>
const char * name; //指向设备名称<br>
struct file_operations * fops; //指向设备的访问操作函数集,file_operati<br>
ons定义在<br>
include/linux/fs.h中<br>
};<br>
static struct device_struct chrdevs[MAX_CHRDEV] = {<br>
{ NULL, NULL },<br>
}; //所有系统登记的字符设备列表<br>
static struct device_struct blkdevs[MAX_BLKDEV] = {<br>
{ NULL, NULL },<br>
} //所有系统登记的块设备列表<br>
<br>
实际上这两张列表的结构是一样的,但在登记时每个结构元素的值会不同(见初始化部分)。linux对设备的进行访问时,访问文件系统中相应的文件,通过文件系统和文件的属性描述块,系统可以找到该文件系统或文件对应设备的设备号。在实际访问列表时,以chrdevs[MAJOR][MINOR]或blkdevs[MAJOR][MINOR]形式访问,相同主设备号(MAJOR)的元素中fops的内容相同。<br>
<br>
文件系统中相关的的数据结构如下:<br>
struct super_block {<br>
kdev_t s_dev; //该文件系统所在设备的设备标志符<br>
…<br>
} //每个文件系统对应一个super_block<br>
struct inode {<br>
kdev_t i_dev; //该文件所在设备的设备标志符通过它可以找到在设备列表中<br>
… 相应设备<br>
} //每个文件对应一个inode<br>
<br>
2、I/O请求管理<br>
<br>
系统会把一部分系统内存作为块设备驱动程序与文件系统接口之间的一层缓冲区,每个缓冲区与某台块设备中的特定区域相联系,文件系统首先试图存在相应的缓冲区,如未找到就向该设备发出I/O读写请求,由设备驱动程序对这些请求进行处理。因此,需要有相应的数据结构进行管理。<br>
<br>
/*srcincludelinuxlkdev.h*/<br>
struct blk_dev_struct {<br>
void (*request_fn)(void); //指向请求处理函数的指针,请求处理函数是写设备驱动程序的重要一环,设备驱动程序在此函数中通过outb向位于I/O空间中的设备命令寄存器发出命令<br>
<br>
struct request * current_request; //指向当前正在处理的请求,它和plug共同维护了该设备的请求队列<br>
struct request plug; //这是LINUX2.0版本与以前版本的一个不同之处,plug主要被用于异步提前读写操作,在这种情况下,由于没有特别的请求,为了提高系统性能,需要等发送完所有的提前读写请求才开始进行请求处理,即unplug_device。<br>
struct tq_struct plug_tq; //设备对应的任务队列<br>
};<br>
/*srcdriverslockll_rw_blk.c*/<br>
struct blk_dev_struct blk_dev[MAX_BLKDEV];<br>
其中每个请求以request的类型的结构进行传递,定义如下:<br>
/*srcincludelinuxlk_dev.h*/<br>
struct request {<br>
volatile int rq_status; //表示请求的状态<br>
kdev_t rq_dev; //是该请求对应的设备号,kdev_t是unsigned s<br>
hort类型,高8位是主设备号,低8位是从设备号,每一请求都针对一个设备发出的;<br>
int cmd; //表示该请求对应的命令,取READ或WRITE;<br>
int errors;<br>
unsigned long sector; //每一扇区的字节数<br>
unsigned long nr_sectors; //每一扇区的扇区数<br>
unsigned long current_nr_sectors; //当前的扇区数;<br>
char * buffer; //存放buffer_head.b_data值,表示发出请求的<br>
数据存取地址;<br>
struct semaphore * sem; //一个信号量,用来保证设备读写的原语操作,<br>
当<br>
sem=0时才能处理该请求;<br>
struct buffer_head * bh; //读写缓冲区的头指针<br>
struct buffer_head * bhtail; //读写缓冲区的尾指针<br>
struct request * next; //指向下一个请求<br>
};<br>
<br>
对不同块设备的所有请求都放在请求数组all_requests中,该数组实际上是一个请求缓冲池,请求的释放与获取都是针对这个缓冲池进行;同时各个设备的请求用next指针联结起来,形成各自的请求队列。定义如下:<br>
<br>
/*srcdriverslokcll_rw_blk.c*/<br>
static struct request all_requests[NR_REQUEST];<br>
<br>
3、中断请求<br>
<br>
设备进行实际的输入/输出操作时,如果时间过长而始终被占用CPU,就会影响系统的效率,必须有一种机制来克服这个问题而又不引起其他问题。中断是最理想的方法。和中断有关的数据结构是;<br>
<br>
struct irqaction {<br>
void (*handler)(int, void *, struct pt_regs *); //指向设备的中断响应函数,它在系统初始化时被置入。当中断发生时,系统自动调用该函数<br>
unsigned long flags; //指示了中断类型,如正常中断、快速中断等<br>
unsigned long mask; //中断的屏蔽字<br>
const char *name; //设备名<br>
void *dev_id; //与设备相关的数据类型,中断响应函数可以根<br>
<br>
据需要将它转化所需的数据指针,从而达到访问系统数据的功能<br>
struct irqaction *next; //指向下一个irqaction<br>
};<br>
<br>
由于中断数目有限,且很少更新,所以系统在初始化时,从系统堆中分配内存给每一个irq_action指针,通过next指针将它们连成一个队列。<br>
<br>
4、高速缓冲区<br>
<br>
为了加速对物理设备的访问速度,linux将块缓冲区放在Cache内,块缓冲区是由buffer_head连成的链表结构。buffer_head的数据结构如下:<br>
/*includelinuxfs.h*/<br>
struct buffer_head {<br>
unsigned long b_blocknr; /* block number */<br>
kdev_t b_dev; /* device (B_FREE = free) */<br>
kdev_t b_rdev; /* Real device */<br>
unsigned long b_rsector; /* Real buffer location on disk */<br>
struct buffer_head * b_next; /* Hash queue list */<br>
struct buffer_head * b_this_page; /* circular list of buffers in one page *<br>
/<br>
unsigned long b_state; /* buffer state bitmap (see above) */<br>
struct buffer_head * b_next_free;<br>
unsigned int b_count; /* users using this block */<br>
unsigned long b_size; /* block size */<br>
char * b_data; /* pointer to data block (1024 bytes) */<br>
unsigned int b_list; /* List that this buffer appears */<br>
unsigned long b_flushtime; /* Time when this (dirty) buffer should be<br>
written */<br>
unsigned long b_lru_time; /* Time when this buffer was last used. */<br>
struct wait_queue * b_wait;<br>
struct buffer_head * b_prev; /* doubly linked list of hash-queue */<br>
struct buffer_head * b_prev_free; /* doubly linked list of buffers */<br>
struct buffer_head * b_reqnext; /* request queue */<br>
};<br>
<br>
块缓冲区主要由链表组成。空闲的buffer_head组成的链表是按块大小的不同分类组成,linux目前支持块大小为512、1024、2048、4096和8192字节;第二部分是正在用的块,块以Hash_table的形式组织,具有相同hash索引的缓冲块连在一起,hash索引根据设备标志符和该数据块的块号得到;同时将同一状态的缓冲区块用LRU算法连在一起。对缓冲区的各个链表定义如下:<br>
/* fsuffer.c*/<br>
static struct buffer_head ** hash_table;<br>
static struct buffer_head * lru_list[NR_LIST] = {NULL, };<br>
static struct buffer_head * free_list[NR_SIZES] = {NULL, };<br>
static struct buffer_head * unused_list = NULL;<br>
static struct buffer_head * reuse_list = NULL;<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
初始化<br>
<br>
三. 设备的初始化<br>
LINUX启动时,完成了实模式下的系统初始化(arch/i386/boot/setup.S)与保护模式下的核心初始化包括初始化寄存器和数据区(arch/i386/boot/compressed/head.S)、核心代码解压缩、页表初始化(arch/i386/kernel/head.S)、初始化idt、gdt和ldt等工作<br>
后,系统转入了核心。调用函数start_kernel启动核心(init/main.c)后,将继续各方面的初始化工作,其中start_kernel最后将调用kernel_thread (init, NULL, 0),创建init进程进行系统配置(其中包括所有设备的初始化工作)。<br>
static int init(void * unused)<br>
{<br>
…………<br>
/* 创建后台进程bdflush,以不断循环写出文件系统缓冲区中"脏"的内容 */<br>
kernel_thread(bdflush, NULL, 0);<br>
/* 创建后台进程kswapd,专门处理页面换出工作 */<br>
kswapd_setup();<br>
kernel_thread(kswapd, NULL, 0);<br>
…………<br>
setup();<br>
…………<br>
在setup函数中,调用系统调用sys_setup()。sys_setup()的定义如下:<br>
//fs/filesystems.c<br>
asmlinkage int sys_setup(void)<br>
{<br>
static int callable = 1;<br>
… …<br>
if (!callable)<br>
return -1;<br>
callable = 0;<br>
… …<br>
device_setup();<br>
… …<br>
在该系统调用中,静态变量callable保证只被调用实际只一次,再次调用时后面的初始化程序不执行。在该调用开始就先进行设备的初始化:device_setup()。<br>
//dirvers/block/genhd.c<br>
void device_setup(void)<br>
{<br>
extern void console_map_init(void);<br>
… …<br>
chr_dev_init();<br>
blk_dev_init();<br>
… …<br>
可以看到device_setup()将依次执行chr_dev_init()、blk_dev_init()等各类设备的初始化程序。每个具体的init函数的内容和具体设备就有关了,但是它们都有一些必须完成的任务:<br>
1、 告诉内核这一驱动程序使用的主设备号,同时提供指向file_operation的指针,以完成对chrdevs和blkdevs的初始化。<br>
2、 对块设备,需要将输入/输出处理程序的入口地址告诉内核。<br>
3、 对块设备,需要告诉缓冲区设备存取的数据块的大小。<br>
<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
管理流程<br>
<br>
四. 设备管理的流程<br>
下面我们介绍一下整个设备管理的流程。我们以块设备为例,字符设备的流程也和块设备类似,只是没有请求队列管理。<br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -