简介
1.总线系统分类
总线名称 | 特点 |
---|---|
PCI | 支持热插拔 |
ISA | 古老的总线,IBM引入 |
SBus | SUN公司设计 |
IEEE1394(FireWare) | |
USB | 通用外部总线 |
SCSI | 用于寻址硬盘 |
并口和串口 | 简单效率低下 |
2.与外设的交互方法
- I/O端口
- I/O内存映射
- 轮讯和中断
3. 访问设备
内核采用主从设备号来标记匹配的驱动程序 ,b为块设备,c为字符设备。/dev中的设备节点在基于磁盘的文件系统中动态创建,由udevd守护进程负责管理(创建和删除)这些设备节点。引入udev机制后,/dev放置到tmpfs中。通过ioctl例程实现设备控制数据和普通文件读写数据分离。
注: 设备号的统一标准列表
3.1 设备的注册
注册机制实质上就是要维护一个数据库,以便内核知道哪些设备可用,哪些不可用。该数据库需留出接
3.1.1 字符设备
- 注册或分配一个设备号范围
驱动程序使用register_chrdev_region()来使用特定范围的设备号
内核使用alloc_chrdev_region()来选择合适范围 - 添加设备到设备数据库
使用cdev_init()初始化cdev实例,然后使用cdev_add()注册设备
3.1.2 块设备
采用add_disk()即可
4. 关联文件系统
除少数情况外,设备文件也采用标准函数处理,由虚拟文件系统管理。 在虚拟文件系统中,每个文件关联一个inode,用于管理文件属性。
struct inode {
dev_t i_rdev; //主从设备号
umode_t i_mode; //设备类型 unsigined short
const struct file_operations *i_fop; //文件操作函数指针集合
....
}
在打开一个设备文件时,文件系统会调用init_special_inode()函数,为设备文件创建一个inode
//mode是设备类型(块or字符), rdev是设备号,这个函数相当于inode的初始化
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops; //字符设备对应的操作函数集
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &pipefifo_fops;
else if (S_ISSOCK(mode))
; /* leave it no_open_fops */
else
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\n", mode, inode->i_sb->s_id,
inode->i_ino);
}
5.字符设备的操作
字符设备由struct cdev表示,内核中维护一个数据库,包括所有活动的cedv实例。
struct cdev {
struct kobject kobj; //内核通用对象
struct module *owner; //提供驱动程序的模块
const struct file_operations *ops; // 一组文件操作
struct list_head list; //所有表示该设备的设备特殊文件的inode
dev_t dev; //主从设备号
unsigned int count;
};
打开字符设备的函数chrdev_open():
static int chrdev_open(struct inode *inode, struct file *filp)
{
const struct file_operations *fops;
struct cdev *p;
struct cdev *new = NULL;
int ret = 0;
spin_lock(&cdev_lock);
p = inode->i_cdev;
if (!p) {
struct kobject *kobj;
int idx;
spin_unlock(&cdev_lock);
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
if (!kobj)
return -ENXIO;
new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev;
if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list);
new = NULL;
} else if (!cdev_get(p))
ret = -ENXIO;
} else if (!cdev_get(p))
ret = -ENXIO;
spin_unlock(&cdev_lock);
cdev_put(new);
if (ret)
return ret;
ret = -ENXIO;
fops = fops_get(p->ops);
if (!fops)
goto out_cdev_put;
replace_fops(filp, fops);
if (filp->f_op->open) {
ret = filp->f_op->open(inode, filp);
if (ret)
goto out_cdev_put;
}
return 0;
out_cdev_put:
cdev_put(p);
return ret;
}
6 块设备操作
块设备的实例为Struct block_device, 与块设备关联的inode结构为bdev_inode, 每个块设备都关联了请求队列由struct request_queue表示。一个块设备会有好几个分区,它的实例为struct hd_struct, 用于描述该分区在设备内的键。块设备的操作函数集合都在结构体block_device_operation中。对于已经分区的硬盘,采用Struct gendisk表示。
struct block_device {
dev_t bd_dev; /* not a kdev_t - it's a search key */
int bd_openers;
struct inode * bd_inode; /* will die */
struct super_block * bd_super;
struct mutex bd_mutex; /* open/close mutex */
struct list_head bd_inodes;
void * bd_claiming;
void * bd_holder;
int bd_holders;
bool bd_write_holder;
#ifdef CONFIG_SYSFS
struct list_head bd_holder_disks;
#endif
struct block_device * bd_contains;
unsigned bd_block_size;
struct hd_struct * bd_part;
/* number of times partitions within this device have been opened. */
unsigned bd_part_count;
int bd_invalidated;
struct gendisk * bd_disk;
struct request_queue * bd_queue;
struct list_head bd_list;
/*
* Private data. You must have bd_claim'ed the block_device
* to use this. NOTE: bd_claim allows an owner to claim
* the same device multiple times, the owner must take special
* care to not mess up bd_private for that case.
*/
unsigned long bd_private;
/* The counter of freeze processes */
int bd_fsfreeze_count;
/* Mutex for freeze */
struct mutex bd_fsfreeze_mutex;
};
为系统添加磁盘和分区,采用add_partition向gendisk添加hd_struct。
添加磁盘采用add_disk