kernel1- 驱动open 流程
inode --->cdev--->cdev_map--->file_ops
cdev_map: 是要在驱动注册的时候,创建了cdev,然后加入cdev_map , open是由根据inode 中的i_cdev 进行驱动程序的寻找
文件对应唯一一个inode, 不管Open 几次,软连接几次。
struct inode
struct inode {
umode_t i_mode;//文件的访问权限(eg:rwxrwxrwx)
unsigned short i_opflags;
kuid_t i_uid;//inode拥有者id
kgid_t i_gid;//inode拥有者组id
unsigned int i_flags;//inode标志,可以是S_SYNC,S_NOATIME,S_DIRSYNC等
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op;//inode操作
struct super_block *i_sb;//所属的超级快
/*
address_space并不代表某个地址空间,而是用于描述页高速缓存中的页面的一个文件对应一个address_space,一个address_space与一个偏移量能够确定一个一个也高速缓存中的页面。i_mapping通常指向i_data,不过两者是有区别的,i_mapping表示应该向谁请求页面,i_data表示被改inode读写的页面。
*/
struct address_space *i_mapping;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
/* Stat data, not accessed from path walking */
unsigned long i_ino;//inode号
/*
* Filesystems may only read i_nlink directly. They shall use the
* following functions for modification:
*
* (set|clear|inc|drop)_nlink
* inode_(inc|dec)_link_count
*/
union {
const unsigned int i_nlink;//硬链接个数
unsigned int __i_nlink;
};
dev_t i_rdev;//如果inode代表设备,i_rdev表示该设备的设备号
loff_t i_size;//文件大小
struct timespec i_atime;//最近一次访问文件的时间
struct timespec i_mtime;//最近一次修改文件的时间
struct timespec i_ctime;//最近一次修改inode的时间
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;//文件中位于最后一个块的字节数
unsigned int i_blkbits;//以bit为单位的块的大小
blkcnt_t i_blocks;//文件使用块的数目
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;//对i_size进行串行计数
#endif
/* Misc */
unsigned long i_state;//inode状态,可以是I_NEW,I_LOCK,I_FREEING等
struct mutex i_mutex;//保护inode的互斥锁
//inode第一次为脏的时间 以jiffies为单位
unsigned long dirtied_when; /* jiffies of first dirtying */
struct hlist_node i_hash;//散列表
struct list_head i_wb_list; /* backing dev IO list */
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list;//超级块链表
union {
struct hlist_head i_dentry;//所有引用该inode的目录项形成的链表
struct rcu_head i_rcu;
};
u64 i_version;//版本号 inode每次修改后递增
atomic_t i_count;//引用计数
atomic_t i_dio_count;
atomic_t i_writecount;//记录有多少个进程以可写的方式打开此文件
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ 这里对应是node 的
struct file_lock *i_flock;//文件锁链表
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];//inode磁盘限额
#endif
/*
公用同一个驱动的设备形成链表,比如字符设备,在open时,会根据i_rdev字段查找相应的驱动程序,并使i_cdev字段指向找到的cdev,然后inode添加到struct cdev中的list字段形成的链表中
*/
struct list_head i_devices;,
union {
struct pipe_inode_info *i_pipe;//如果文件是一个管道则使用i_pipe
struct block_device *i_bdev;//如果文件是一个块设备则使用i_bdev
struct cdev *i_cdev;//如果文件是一个字符设备这使用i_cdev
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
//目录通知事件掩码
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_marks;
#endif
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
//存储文件系统或者设备的私有信息
void *i_private; /* fs or device private pointer */
};
struct file
struct file {
struct list_head f_list;
struct file_operations *f_op; ---》对应了驱动的写的read ,write..函数
mode_t f_mode;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
};
file_operation就是把系统调用和驱动程序关联起来的关键数据结构
struct file_operation 驱动中
struct file_operations {
struct module *owner;
loff_t(*llseek) (struct file *, loff_t, int);
ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t)
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
linux/include/linux/cdev.h
struct cdev {
struct kobject kobj; // 每个cdev 都是一个 kobject
struct module *owner; // 指向实现驱动的模块
const struct file_operations *ops; // 操纵这个字符设备文件的方法
struct list_head list; // 与cdev 对应的字符设备文件的 inode->i_devices 的链表头
dev_t dev; // 起始设备编号
unsigned int count; // 设备范围号大小
};
int cdev_add(struct cdev *p, dev_t dev, unsignedcount)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
关于 kobj_map() 函数就不展开了,我只是大致讲一下它的原理。内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。 kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。