Linux中一切皆是文件,驱动我呢见最终通过与文件操作相关的系统调用或者C库还函数被访问,而设备驱动的结构也最终是为了迎合提供应用程序API。
设备节点:
Linux中的设备节点是通过"mknod"命令来创建的,一个设备节点其实就是一个文件,Linux中称为设备文件,有一点必须要说明的是,在Linux中所有的设备访问都是通过文件的方。设备节点就是连接上层应用和底层驱动的桥梁,如下所示:
文件对应的有打开关闭读写等,那么设备节点看作是一个文件,对应的设备节点就有打开关闭读写等功能。
file_operation结构体是访问驱动的函数,它在里边的每个结构体成员都对应这个一个调用,这个结构体里边有很多成员变量,并且结构体中的成员函数是字符设备驱动程序设备的主体内容。这些函数实际会在应用程序进行Linux的open()、write()、read()、close()等系统调用时最终被内核调用。file_operations 文件操作集在定义在 include/linux/fs.h 下面,如下图所示。
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 (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
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 *, loff_t, loff_t, 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 **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
};
下边对file_operations结构体中的主要成分进行分析。
函数 | 功能 |
llseek() | 修改一个文件当前的读写位置,并将新位置返回,在出错的时候,这个函数返回一个负值。 |
read() | 用来从设备中读取数据,成功时候返回读取的字节数,错的时候返回一个负值。
与用户空间应用程序中的 ssize_t read(int fd,void*buf,size_t count)和 size_t fread
(void*ptr,size_t size,size_t nmemb,FILE*stream)对应。
|
write() | 向设备发送数据,成功的时候该函数返回写入的字节数,如果此函数未被实现,当用户进程调用write的时候,将得到-EINVAL 返回值。它与用户空间应用程序中的 ssize_t write(int fd,const void*buf,size_t count)和 size_t fwrite(const void*ptr,size_t size,
size_t nmemb,FILE*stream)对应。
|
unlocked_ioctl() | 提供设备相关控制命令的实现(既不是读操作,也不是写操作),当调用成功的时候返回给调用程序一个非负值。它与用户空间应用程序调用的 int fcntl(int fd,int
cmd,.../*arg*/)和 int ioctl(int d,int request,...)对应。
|
mmap() | 将设备内存映射到进程的虚拟地址空间当中,如果这个设备驱动未实现此函数,用户进行mmap()系统调用时候将返回-ENODEV。这个函数对于帧缓冲等设备特备有意义,帧缓冲被映射到用户用户空间后,应用程序可以直接访问它而无需在内核和应用之间进行内存复制。它与用户空间应用程序中的void*mmap(void*addr,size_t length,int prot,int flags,int fd,off_t offset)函数对
应。
|
poll()函数 | 一般用于询问设备是否可被非阻塞的立即读取。当询问的条件未触发的时候,用户空间进行 select()和 poll()系统调用将引起进程的阻塞。 |
aio_read ( ) 和
aio_write()函数
|
分别对与文件描述符对应的设备进行异步读、写操作。设备实现这
两个函数后,用户空间可以对该设备文件描述符执行 SYS_io_setup、SYS_io_submit、
SYS_io_getevents、SYS_io_destroy 等系统调用进行读写。
|
open() | 当用户空间调用open()函数打开文件设备的时候,设备驱动的open()函数最终被调用。驱动程序可以不实现这个函数,在这种情况下,设备打开操作永远成功。
与 open()函数对应的是 release()函数。
|
Linux的应用层和内核层是不能直接进行数据传输的。我们想要进行数据传输就要借助下边两个函数。
static inline long copy_from_user(void *to, const void __user * from, unsigned long n)
static inline long copy_to_user(void __user *to, const void *from, unsigned long n)
用户空间->内核空间,
函数 | copy_from_user(void *to, const void __user * from, unsigned long n) |
参数 |
目标地址(内核空间) |
参数 |
源地址(用户空间) |
参数 |
将要拷贝数据的字节数 |
返回值 | 成功返回0,失败返回没有拷贝成功的数据字节数 |
功能 | 将用户空间的数据拷贝到内核空间 |
内核空间->用户空间:
函数 | copy_to_user(void __user *to, const void *from, unsigned long n) |
参数 |
目标地址(用户空间) |
参数 |
源地址(内核空间) |
参数 |
将要拷贝数据的字节数 |
返回值 | 成功返回0,失败返回没有拷贝成功的数据字节数 |
功能 | 将内核空间的数据拷贝到用户空间 |