file_operations、file、inode

来源:互联网 发布:破壁机骗局知乎 编辑:程序博客网 时间:2024/05/29 19:53
一、 file_operations文件操作:
fileoperation结构就是用来建立驱动程序操作连接到设备编号的,这个结构定义在<linux/fs.h>,其中包含了一组函数指针。每个打开的文件(在内部由一个file结构表示)和一组函数关联(通过包含指向一个file_operation结构的f_op字段)。这些操作主要用来实现系统调用,命名为open、read。
file_operations结构中每个字段都必须指向驱动程序中实现特定操作的函数,对于不支持的操作,对应的字段可置为NULL。

该结构涉及到的一些常用函数和指针:
struct module *owner         
 指向“拥有”结构体的模块的指针,内核使用这个字段避免在模块的操作正在使用时卸载该模块。几乎所有情况下该成员都会被初始化为THIS_MODULE,她是定义在<linux、module.h>中的一个宏。
loff_t (*llseek) (struct file *,loff_t,nt); 
llseek用来修改该文件的当前读写位置,并将新位置作为(正的)返回值返回。参数loff_t是一个长偏移量,出错时返回一个负的返回值。
ssize_t (*read)(struct file *,char __user *,size_t,loff_t *);         
read用来从设备读取数据,该函数指针被赋为NULL值时,将导致read系统调动出错并返回-EINVAL(“Invalid,非法参数”),返回非负值表示成功读取到的字节数。
ssize_t (*aio_read) (struct kiocb *,char __user *,size_t,loff_t);         
初始化一个异步的读取操作,若为NULL,则所有操作通过read(同步)处理。
ssize_t (write) (struct file*,const cahr __user *,size_t,loff_t);
向设备发送数据。如果没有这个函数,write调用就会向程序返回一个-EINVAL。返回非负值,表示成功写入的字节数。
ssize_t (*aio_write) (struct kiocb *,const char __user *,size_t,loff_t *);
异步写入操作。
int (*readdir) (struct file *,void *,filldir_t); 
对于设备文件来说,这个字段应该为NULL,他仅用于读取目录,只对文件系统有用。
unsigned int (*poll) (struct file *,struct poll_table_struct *); 
poll方法是poll、epoll和select这三个系统调用的后端实现的。这三个系统调用可用来查询某个或多个文件描述符上的读取或写入是否会被堵塞。poll方法应该返回一个位掩码用
来指出非堵塞的读取或写入是否可能,并且也会向内核提供将调用进程休眠状态直到I/O变为可能时的信息。如果驱动程序将poll方法定义为NULL,则设备会被认为既可读也可写,并且不会被堵塞。
int (*ioctl) (struct inode *,struct file *,unsigned int,unsigned long);   
系统调用ioctl提供了一种执行设备特定命令的方法(如格式化软盘的某个磁道,这既不是读操作也不是写操作)。另外,内核还能识别一部分ioctl命令,而不必调用fops表里的ioctl。如果设备不提供ioctl入口点,则对于任何内核未预先定义的请求,ioctl系统调用将返回错误(-ENOTTY))。
int (*mmap) (struct file *,struct vm_area_struct *);
mmap用于请求将设备内存映射到进程地址空间。如果设备没有实现这个方法,那么mmap系统调用将返回-ENODEV。
int (*open) (struct inode *,struct file *); 
尽管是对设备文件执行的第一个操作,然而却并不要求驱动程序一定要声明一个对应的方法。如果这个入口为NULL,设备的打开操作永远成功,但系统不会通知驱动程序。

scull设备驱动程序所实现的只是最重要的设备方法,他的file_operations结构被初始化为如下形式:
struct file_operations scull_fops={
.owner  = THIS_MODULE,
.llseek = scull_llseek,
.read   = scull_raed,
.write  = scull_write,
.ioctl  = scull_ioctl,
.open   = scull_open,
.release= scull_release,
};
PS:该声明采用了标准C的标记化结构初始化语法。

二、file结构:
在<lilnux/fs.h>中定义的struct file是设备驱动程序所使用的第二个最重要的数据结构。注意,file结构与用户空间程序中的FILE没有任何关联。FILE在C库中定义且不会出现在内核代码中;而struct file是一个内核结构,他不会出现在用户程序中。
file结构代表一个打开的文件(它不仅仅限定于设备驱动程序,系统每个打开的文件在内核空间都有一个对应的file结构)。他由内核在open时创建,并传递给在该文件进行操作的所有函数,直到最后的clode()函数。在文件的所有实例都被关闭之后,内核会释放这个数据结构。
在内核源码中,指向struct file的指针通常被称为file或filp(文件指针)。为了不至于和这个结构本身混淆,我们一致将该指针称为filp。这样,file指的是结构本身,filp则是指向该结构的指针。

file结构中的成员
mode_t f_mode; 
文件模式,他通过FMODE_READ和FMODE_WRITE来标示文件是否可读或可写(或可读写)。由于内核在调用驱动程序中的read和write前已经检查了访问权限,所以不必为这两个方法检查权限。在没有获得对应访问权限而打开文件的情况下,对文件的读写操作将被内核拒绝,驱动程序无需为此而作额外的判断。
loff_t f_pos; 
当前读写位置。
unsigned int f_flags; 
文件标志,如O_RDONLY、O_NONBLOCK和O_SYNC。为了检查用户请求的是否是非阻塞式的操作,驱动程序需要检查O_NONBLOCK标志,而其他标志很少用到。PS:检查读写权限应该查看f_mode,所有这些标志的定义都在<linux/fcntl.h>。
struct file_operations *f_op;
与文件相关的操作。内核在执行open操作时会对这个指针赋值,以后需要处理这些操作就读取这个指针。filp->f_op中的值不会为方便引用而保存起来,也就是说我们可以在任何需要的时候修改文件的关联操作,在返回给调用者之后,新的操作方法就会立即生效。例如,对应于主设备号1(/dev/null、/dev/zerp等等)的open代码根据要打开的次设备号替换filp->f_op中的操作。这种技巧允许相同主设备号下的设备实现多操作的行为,而不会增加系统调用的负担。这种替换文件操作的能力在面向对象编程技术中称为“方法重载”。
void *private_data; 
open系统调用在调用驱动程序的open方法之前将这个指针置为NULL。驱动程序可以将这个字段用于任何目的或者忽略这个字段。驱动程序可以将这个字段用于任何目的或者忽略这个字段。驱动程序可以用这个字段指向已分配的数据,但是一定要在内核销毁file结构体前在release方法中释放内存。private_data是跨系统调用时保存状态信息的非常有用的资源。
struct dentry *f_dentry;
文件对应的目录项(dentry)结构。除了用filp->f_debtry->d_inode的方式来访问索引点结构之外,设备驱动程序的开发者无需关心。
还有一些其他字段,但他们对于设备驱动程序并没有多大用处。由于驱动程序从不自己填写file结构,而只是对别处创建的file结构进行访问,所以忽略这些字段是安全的。

三、inode结构:
内核用inode结构在内部表示文件,因此它和file结构不同,后者表示打开的文件描述符。对于单个文件,可能会有许多表示打开的文件描述符的file结构,但他们都指向单个inode结构。

inode结构常用字段
dev_t i_rdev; 
对表示设备文件的inode结构,该字段包含了真正的设备编号。
struct cdev *i_cdev; 
当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针。
0 0
原创粉丝点击