从文件到字符设备
zzj.1125 2012-07-14 23:33
1. 在__dentry_open中(/fs/open.c):
825 f->f_op = fops_get(inode->i_fop); //在这里进行赋值,f->f_op = &def_chr_fops,注意上文inode->i_fop中的赋值
832 if(!open && f->f_op) //在调用__dentry_open时open赋值为空,所以!open为真
833 open = f->f_op->open; //在这里将open赋为chrdev_open
835 error = open(inode,f); //这里调用chrdev_open
2. 在chrdev_open中(/fs/char_dev.v):
371 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); //执行kobj_lookup函数,在cdev_map里寻找相应的inode->i_rdev设备
cdev_map是一个256个probe结构组成的数组,用于查找具有相应设备号的设备,如何查找这个结构,将在后面的文章中详细介绍
inode->i_rdev为设备号 在前面已经提示了
374 new = container_of(kobj, struct cdev, kobj); //从kobj的位置倒算出cdev的内存地址.获得包含相应kobj的cdev
378 inode->i_cdev = p = new; //到这里p已经为我们要的设备cdev了
390 filp->f_op = fops_get(p->ops); //终于拿到了梦寐以求的cdev操作集,至此~ 以后read,write操作都通过file->f_op直接与我们要的设备操作集挂钩了
现在详细讲述cdev_map这个重要的结构
cdev_map是管理linux中字符设备的数组,它的描述为static struct kobj_map *cdev_map;
而kobj_map的相关代码在/drivers/base/map.c里
struct kobj_map {
struct probe {
struct probe *next; //同一大设备号的下一个设备
dev_t dev; //设备的设备号
unsigned long range; //设备的范围,例如某设备下有255个设备
struct module *owner;
kobj_probe_t *get; //取得函数,用于取得私有结构中的kobj,将在kobj_map解释
int (*lock)(dev_t, void *); //互斥锁
void *data; //私有结构
} *probes[255];
struct mutex *lock;
};
这就是kobj_map的结构
下面来看一下操作函数,首先是初始化函数kobj_map_init,这个函数在chrdev_init中调用(/fs/char_dev.c)
cdev_map = kobj_map_init(base_probe, &chrdevs_lock); //初始化cdev_map
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
{
struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); //从内存中取得kobj_map
struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL); //从内存中取得一个probe
int i;
if ((p == NULL) || (base == NULL)) {
kfree(p);
kfree(base);
return NULL;
} //检测分配是否成功
base->dev = 1;
base->range = ~0; //~0为4294967295
base->get = base_probe; //以上三项初始化probe
for (i = 0; i < 255; i++)
p->probes = base; //将kobj_map中的256项probe均指向base
p->lock = lock;
return p;
}
初始完后的数组如下图
![](http://imgsize.ph.126.net/?imgurl=http://hi.csdn.net/attachment/201108/3/0_13123504538OPP.gif_168x168x0.jpg)
查看大图
然后是分配函数kobj_map,该函数负责插入一个包含私有数据的probe到数组里
在添加字符设备的时候cdev_add负责调用此函数
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
其中比较重要的是exact_match这个函数,他用于提取私有数据中的kobj
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; //计算需要多少个主设备号,range超过MIN则增加主设备号需要的数目
unsigned index = MAJOR(dev); //取得主设备号
unsigned i;
struct probe *p;
if (n > 255)
n = 255; //主设备号大于255则限定在255
p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL); //分配主设备号数目需要的probe结构空间
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
} //初始化取得的probe结构
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) { //历遍主设备号需要的数目
struct probe **s = &domain->probes[index % 255]; //取得主设备号所在数组中的位置
while (*s && (*s)->range < range)
s = &(*s)->next; //寻找range适合的位置,算法为大的range在后面
p->next = *s;
*s = p; //找到合适的位置则插入
}
mutex_unlock(domain->lock);
return 0;
}
插入一个probe后的数组如下图
关于kobj_map的题外话,在插入中我们可以看到,代码并不对设备号进行检测,也就说有可能有两个完全一样的设备号设备,如下图
LINUX并没有这样的检测机制~ 只能全凭人工分配设备号的时候进行仔细的检查,也可能是我没发现其中的检测机制,望大家指出,万分感谢
现在到了大家最关心的,如何使用设备号查找对应的设备呢?
这个函数是kobj_lookup,它负责进行查询
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
{
struct kobject *kobj;
struct probe *p;
unsigned long best = ~0UL;
retry:
mutex_lock(domain->lock);
for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) { //计算主设备号所对应的数组位置
struct kobject *(*probe)(dev_t, int *, void *);
struct module *owner;
void *data;
if (p->dev > dev || p->dev + p->range - 1 < dev)
continue; //跳过不在 p->dev ---- p->dev+p->range 中的设备
if (p->range - 1 >= best)
break;
if (!try_module_get(p->owner))
continue;
owner = p->owner;
data = p->data; //取得私有数据
probe = p->get; //取得probe函数,用于提取私有数据中的kobj
best = p->range - 1;
*index = dev - p->dev;
if (p->lock && p->lock(dev, data) < 0) {
module_put(owner);
continue;
}
mutex_unlock(domain->lock);
kobj = probe(dev, index, data); //提取私有数据中的kobj
/* Currently ->owner protects _only_ ->probe() itself. */
module_put(owner);
if (kobj)
return kobj; //返回kobj
goto retry;
}
mutex_unlock(domain->lock);
return NULL;
}
关于kobj_unmap我就不说先了~