Linux 用户层和内核层

Linux中一切皆是文件,驱动我呢见最终通过与文件操作相关的系统调用或者C库还函数被访问,而设备驱动的结构也最终是为了迎合提供应用程序API。

回顾概念:

设备节点:

Linux中的设备节点是通过"mknod"命令来创建的,一个设备节点其实就是一个文件,Linux中称为设备文件,有一点必须要说明的是,在Linux中所有的设备访问都是通过文件的方。设备节点就是连接上层应用和底层驱动的桥梁,如下所示:

Linux 用户层和内核层_第1张图片

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)

用户空间->内核空间,

Linux 用户层和内核层_第2张图片

函数 copy_from_user(void *to, const void __user * from, unsigned long n)
参数 目标地址(内核空间)
参数 源地址(用户空间)
参数 将要拷贝数据的字节数
返回值 成功返回0,失败返回没有拷贝成功的数据字节数
功能 将用户空间的数据拷贝到内核空间

内核空间->用户空间

Linux 用户层和内核层_第3张图片

函数 copy_to_user(void __user *to, const void *from, unsigned long n)
参数 目标地址(用户空间)
参数 源地址(内核空间)
参数 将要拷贝数据的字节数
返回值 成功返回0,失败返回没有拷贝成功的数据字节数
功能 将内核空间的数据拷贝到用户空间

你可能感兴趣的:(Linux_study,linux)