对下层用户来说linux是将设备当作一个文件来使用的,cdev中是的conststructfile_operations*ops;成员定义了驱动程序的文件操作表。提到这部份就不得不提其它两个数据结构了:file和inode。大部份的驱动程序操作都涉及到这三个内核数据结构。如今我们就一上去学习一下这几个数据结构。
file_operation
file_operation结构体中的成员都是一些函数表针,其中的一些函数是字符设备驱动程序要实现的,这种函数会在应用程序调用相关的文件操作如read()、write()时被最终调用。下边是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 *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
因为结构体的成员比较多,我们暂时就先剖析一下我们的第一个字符程序即将用到的成员:
structmodule*owner----是一个指向拥有该结构体的模块的表针,通常我们会将其初始化为THIS_MOGULE。
loff_t(*llseek)(structfile*,loff_t,int)----拿来更改文件的当前读写位置linux设备驱动程序pdf,并将新位置作为返回值返回。它对应linux的系统调用lseek。
ssize_t(*read)(structfile*,char__user*,size_t,loff_t*)----从设备中读取数据,假如读取成功将返回读取数据的字节数。它对应linux系统调用的read。
size_t(*write)(structfile*,constchar__user*,size_t,loff_t*)----向设备中发送数据,假如发送成功则返回成功发送的字节数。它对应linux系统调用的write。
int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong)----实现设备的一些控制命令,假如命令未能执行将返回相应的错误码。Ta对应linux系统调用的ioctl。
int(*open)(structinode*,structfile*)----打开一个设备文件,虽然这仍然是对设备文件执行的第一个操作,但是却并不要求驱动程序一定要申明一个相应的函数。假如这个入口为NULL,设备的打开操作永远成功,但系统不会通知驱动程序。它对应linux系统调用open。
int(*release)(structinode*,structfile*)----当file结构被释放时将调用这个操作,也可将release设置为NULL。它对应linux系统调用close[注意:release并不是在进程每次调用close时就会被调用。只要file结构被共享(如在fork或dup调用以后)linux设备驱动程序pdf,release都会等到的所有副本都被关掉以后才能被调用]。
在我们的字符设备驱动程序中目前我们只须要初始化下边的结构体成员:
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.llseek = xxx_llseek,
.read = xxx_read,
.write = xxx_write,
.ioctl = xxx_ioctl,
.open = xxx_open,
.release = xxx_release,
};
file
在Linux中用file结构表示一个打开的文件(不仅仅是设备文件)。该结构在调用open时由内核创建一个文件对象(fileobject),并传递给在该文件上进行调用的所有函数(file_operation结构体中的许多成员函数表针中都包含有file结构体作为参数)。file结构体源码如下:
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u; //
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op; //指向文件操作表
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
loff_t f_pos; //当前文件的位移量(文件指针)
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/*指向特定文件系统或设备驱动程序所需要的数据的指针 */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};
这儿指介绍几个比较重要的成员:
conststructfile_operations*f_op----指向文件操作表。内核在执行open操作时对这个表针形参,之后须要处理这种操作时就读取这个表针。
loff_tf_pos----储存在文件对象中的主要信息是文件表针linux vi 命令,即文件中当前的位置,下一个操作将在该位置发生。我们驱动程序中的read,write、llseek等操作将会用到它。
void*private_data----指向特定文件系统或设备驱动程序所须要的数据的表针。驱动程序可以用这个数组指向已分配的数据,而且一定要在内核销毁file对象前在release方式中释放显存。
structpathf_path.dentry----文件对应的目录项结构。注意file结构代码中的这几行代码:
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
其中path结构体的代码为:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
我们可以用filp->f_dentry->d_inode的方法来访问索引节点。其中filp是指向file对象的表针。我们的驱动程序一般只会用到这一点。
因为驱动程序从不自己填写file结构,而只是对别处创建的file对象进行访问linux系统装win7,所以我们目前只需了解里面的成员就足够了。
inode
文件系统处理文件所须要的所有信息都置于一个名为inode的数据结构中。inode对文件是惟一的其随文件的存在而存在。inode和file不同,前者表示打开的文件描述符。对单个文件,可能会有许多个表示打开的文件描述符的file结构,但她们都指向单个inode结构。
struct inode {
…
dev_t i_rdev; //该字段标识设备号
…
struct cdev *i_cdev; //指向字符设备
…
};
注意,我们不应当直接操作i_rdev而应当通过内核中提供的两个宏来从inode中获取设备的主、次设备号:
unsigned int imajor(struct inode *inode);
unsigned int iminor(struct inode *inode);