linux驱动之open
open()系统调用->sys_open()
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
...
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_flags op;
//设置一些标志
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp;
//根据用户空间的filename,设置文件名
//用户空间的指针,在内核不能直接使用
//参考copy_to_user()/copy_form_user()
tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
//获取没有使用的fd,用户进程每打开一个文件,就会占用一个fd,
fd = get_unused_fd_flags(flags);
struct file *f = do_filp_open(dfd, tmp, &op);
...
//关联fd和file结构体,这样就可以通过fd调用file->fops
fd_install(fd, f);
//释放内存
putname(tmp);
return fd;
}
do_filp_open()函数调用get_empty_filp()获取一个file结构体,最终调用
do_dentry_open()
static int do_dentry_open(struct file *f,
struct inode *inode,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
...
//file->f_op赋值为inode->i_fop
//inode->i_fop == def_chr_fops,这个是通用字符设备的fops
f->f_op = fops_get(inode->i_fop);
...
if (!open)
open = f->f_op->open;
if (open) {
error = open(inode, f);
}
...
}
const struct file_operations def_chr_fops = {
.open = chrdev_open,
.llseek = noop_llseek,
};
//每次字符设备打开时,都会调用
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);
//根据设备号查找cdev->kobj
//cdev保存在cdev_map中
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
//container_of是一个宏,通过结构体成员的指针获取结构体本身的指针
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记录cdev
inode->i_cdev = p = new;
//inode挂在cdev链表上
list_add(&inode->i_devices, &p->list);
new = NULL;
}
}
...
// == cdev->fops
fops = fops_get(p->ops);
//替换file->fops为cdev->fops
replace_fops(filp, fops);
if (filp->f_op->open) {
//调用设备的fops
ret = filp->f_op->open(inode, filp);
}
...
}
查找cdev->kobj,cdev_map保存有cdev和对应设备号(在字符设备注册函数
cdev_add()中保存的),kobj_lookup()就是去cdev_map->probes[]这个哈希表中查找
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);
//根据设备号查找cdev
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;
if (p->range - 1 >= best)
break;
...
//data就是cdev
data = p->data;
probe = p->get;
best = p->range - 1;
*index = dev - p->dev;
...
mutex_unlock(domain->lock);
//获取kobj, probe == exact_match
kobj = probe(dev, index, data);
...
}
mutex_unlock(domain->lock);
return NULL;
}
static struct kobject *exact_match(dev_t dev, int *part, void *data)
{
struct cdev *p = data;
return &p->kobj;
}
创建特殊文件时,会调用init_special_inode函数设置inode->i_fop和
inode->i_rdev,也就是通用操作接口和设备号
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
//如果是字符设备文件,操作接口就是def_chr_fops
//在调用open()时会调用
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);
}