在上一篇Linux驱动(三)字符设备驱动框架中,我们编写函数操作集合ops,并编写了应用层代码。我们现在来看一看,程序如何从应用到达驱动层的。
linux中一切皆文件,内核如何来区别每一个文件,这个叫做inode号,每个文件有一个特定的inode号
用ls -i查看文件inode号,每个文件会对应一个inode结构体,inode结构体的内容非常多,我们省略了一部分
struct inode {
umode_t i_mode; // 打开方式
kuid_t i_uid; // 用户id
kgid_t i_gid; // 组id
unsigned int i_flags; // 权限标志位,阻塞,非阻塞... 标志位信息
const struct inode_operations*i_op; // inode 操作方法集
struct super_block*i_sb;
struct address_space*i_mapping;
unsigned longi_ino; // inode 号码
union {
const unsigned int i_nlink;
unsigned int __i_nlink; // 链接数
};
dev_ti_rdev; // 设备号
struct timespeci_atime; // 访问时间
struct timespeci_mtime; // 修改时间
struct timespeci_ctime; // 创建时间
// 存储信息
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
const struct file_operations*i_fop; /* 文件操作方法集合 */
struct list_headi_devices; // 内核循环双链表节点
union {
struct pipe_inode_info*i_pipe;
struct block_device*i_bdev;
struct cdev *i_cdev; // 字符设备对象指针
};
...
};
inode结构体包含很多信息,如文件的权限信息,文件的时间信息等等。在驱动文件中,我们主要关注标红的信息,inode号,设备号,文件操作集合。使用mknod创建设备节点时,会将inode号和设备号关联起来。若此时内核中已经注册了该设备号的对象,此时会将设备对象的操作方法集合赋值到此处。
文件操作集合i_fop指向了def_chr_fops,这个结构中的open函数指向了chrdev_open。chrdev_open完成的主要工作是:首先根据设备号找到添加在内核中代表字符设备的cdev。找到对应的cedv对象后,用cedv关联的操作方法集和赋值给新建的file结构体中的文件操作集合。并调用操作集合中的open函数,完成真正的打开操作。
一个驱动可能会被多应用打开,那多次打开时是生成多个inode号吗?答案是否定的。inode号是唯一的,那如何表示多次打开一个驱动文件呢?
系统中每个打开的文件在内核空间都有一个对应的file结构体。
struct file {struct path f_path; // 路径名称
struct inode *f_inode; /*inode 指针*/
const struct file_operations *f_op; // 文件操作集合
unsigned int f_flags; // 标志位信息
fmode_t f_mode; // 操作方式
loff_t f_pos;
struct fown_struct f_owner; // 异步通知 ,需要用到此结构体
void *private_data;
...
};
我们来看看struct file_operations中定义的方法原型,只有在open和release时才会有inode参数,其余的操作都用file结构体来操作文件。也就是在open时会将inode和file结构体关联起来,fil结构体中有inode指针f_inode指向了文件的inode结构体。
我们来看一下下面这副图