Linux字符设备驱动基础(五)

Linux字符设备驱动基础(五)

8 文件操作集

8.1 文件操作集概述

struct file_operations {
	struct module *owner;	//拥有该结构的模块指针,一般有THIS_MODULES
	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);//执行设备的I/O控制命令
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);//64bit系统上,32bit的ioctl调用将使用此函数指针代替
	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);	//通知设备fasync标志发生变化
	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);
} __randomize_layout;

下面是各成员解析:

  • 1、struct module *owner
    第一个 file_operations 成员根本不是一个操作,它是一个指向拥有这个结构的模块的指针。
    这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 <linux/module.h> 中定义的宏.这个宏比较复杂,在进行简单学习操作的时候,一般初始化为THIS_MODULE。

  • 2、loff_t (*llseek) (struct file * filp , loff_t p, int orig);
    (指针参数filp为进行读取信息的目标文件结构体指针;参数 p 为文件定位的目标偏移量;参数orig为对文件定位的起始地址,这个值可以为文件开头(SEEK_SET,0,当前位置(SEEK_CUR,1),文件末尾(SEEK_END,2))
    llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值.
    loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示;如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述).

  • 3、ssize_t (*read) (struct file * filp, char __user * buffer, size_t size , loff_t * p);
    (指针参数 filp 为进行读取信息的目标文件,指针参数buffer 为对应放置信息的缓冲区(即用户空间内存地址),参数size为要读取的信息长度,参数 p 为读的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动,移动的值为要读取信息的长度值)
    这个函数用来从设备中获取数据。在这个位置的一个空指针导致 read 系统调用以 -EINVAL(“Invalid argument”) 失败。一个非负返回值代表了成功读取的字节数( 返回值是一个 “signed size” 类型, 常常是目标平台本地的整数类型).

  • 4、ssize_t (*aio_read)(struct kiocb * , char __user * buffer, size_t size , loff_t p);
    可以看出,这个函数的第一、三个参数和本结构体中的read()函数的第一、三个参数是不同 的,异步读写的第三个参数直接传递值,而同步读写的第三个参数传递的是指针,因为AIO从来不需要改变文件的位置。异步读写的第一个参数为指向kiocb结构体的指针,而同步读写的第一参数为指向file结构体的指针,每一个I/O请求都对应一个kiocb结构体);初始化一个异步读 – 可能在函数返回前不结束的读操作.如果这个方法是 NULL, 所有的操作会由 read 代替进行(同步地).(有关linux异步I/O,可以参考有关的资料,《linux设备驱动开发详解》中给出了详细的解答)

  • 5、ssize_t (*write) (struct file * filp, const char __user * buffer, size_t count, loff_t * ppos);
    (参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界)
    发送数据给设备.。如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数。
    (注:这个操作和上面的对文件进行读的操作均为阻塞操作)

  • 6、unsigned int (*poll) (struct file *, struct poll_table_struct *);
    (这是一个设备驱动中的轮询函数,第一个参数为file结构指针,第二个为轮询表指针)
    这个函数返回设备资源的可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位“或”结果。每个宏都表明设备的一种状态,如:POLLIN(定义为0x0001)意味着设备可以无阻塞的读,POLLOUT(定义为0x0004)意味着设备可以无阻塞的写。
    (poll 方法是 3 个系统调用的后端: poll, epoll, 和 select, 都用作查询对一个或多个文件描述符的读或写是否会阻塞.poll 方法应当返回一个位掩码指示是否非阻塞的读或写是可能的, 并且, 可能地, 提供给内核信息用来使调用进程睡眠直到 I/O 变为可能. 如果一个驱动的 poll 方法为 NULL, 设备假定为不阻塞地可读可写。
    (这里通常将设备看作一个文件进行相关的操作,而轮询操作的取值直接关系到设备的响应情况,可以是阻塞操作结果,同时也可以是非阻塞操作结果)。

  • 7、int (*unlocked_ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
    (inode 和 filp 指针是对应应用程序传递的文件描述符 fd 的值, 和传递给 open 方法的相同参数.cmd 参数从用户那里不改变地传下来, 并且可选的参数 arg 参数以一个 unsigned long 的形式传递, 不管它是否由用户给定为一个整数或一个指针.如果调用程序不传递第 3 个参数, 被驱动操作收到的 arg 值是无定义的.因为类型检查在这个额外参数上被关闭, 编译器不能警告你如果一个无效的参数被传递给 ioctl, 并且任何关联的错误将难以查找.)。
    unlocked_ioctl系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道, 这不是读也不是写). 另外, 几个 ioctl 命令被内核识别而不必引用 fops 表.如果设备不提供 ioctl 方法, 对于任何未事先定义的请求(-ENOTTY, “设备无这样的 ioctl”), 系统调用返回一个错误。

  • 8、int (*mmap) (struct file *, struct vm_area_struct *);
    mmap 用来请求将设备内存映射到进程的地址空间。 如果这个方法是 NULL, mmap 系统调用返回 –ENODEV。
    (如果想对这个函数有个彻底的了解,那么请看有关“进程地址空间”介绍的书籍)

  • 9、int (*open) (struct inode * inode , struct file * filp ) ;
    (inode 为文件节点,这个节点只有一个,无论用户打开多少个文件,都只是对应着一个inode结构;但是filp就不同,只要打开一个文件,就对应着一个file结构体,file结构体通常用来追踪文件在运行时的状态信息)
    尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知.与open()函数对应的是release()函数。

  • 10、int (*flush) (struct file *);
    flush 操作在进程关闭它的设备文件描述符的拷贝时调用;
    它应当执行(并且等待)设备的任何未完成的操作.这个必须不要和用户查询请求的 fsync 操作混淆了. 当前, flush 在很少驱动中使用;SCSI 磁带驱动使用它, 例如, 为确保所有写的数据在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的请求。

  • 11、int (*release) (struct inode *, struct file *);
    release ()函数当最后一个打开设备的用户进程执行close()系统调用的时候,内核将调用驱动程序release()函数:
    void release(struct inode inode,struct file *file),release函数的主要任务是清理未结束的输入输出操作,释放资源,用户自定义排他标志的复位等。在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL。

  • 12、int (*fasync) (int, struct file *, int);
    这个函数是系统支持异步通知的设备驱动,下面是这个函数的模板:

      static int ***_fasync(int fd,struct file *filp,int mode)  
      {  
      struct ***_dev * dev=filp->private_data;  
      return fasync_helper(fd,filp,mode,&dev->async_queue);
      //第四个参数为 fasync_struct结构体指针的指针。  
      //这个函数是用来处理FASYNC标志的函数。(FASYNC:表示兼容BSD的fcntl同步操作)//当这个标志改变时,驱动程序中的fasync()函数将得到执行。  
      //(注:感觉这个‘标志'词用的并不恰当)  
      }
    

8.2 文件操作集示例

static int chardev_test_open(struct inode *inode, struct file *filp)
{
    printk(KERN_DEBUG "%s()-%d open chardev_test okay\r\n", __func__, __LINE);

    return 0;
}

static ssize_t chardev_test_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
{
    printk(KERN_DEBUG "%s()-%d read chardev_test okay\r\n", __func__, __LINE__);

    return len;
}

static ssize_t chardev_test_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
{
    printk(KERN_DEBUG "%s()-%d write chardev_test okay\r\n", __func__, __LINE__);

    return len;
}

static long chardev_test_unlocker_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    printk(KERN_DEBUG "%s()-%d ioctls chardev_test okay\r\n", __func__, __LINE__);

    return 0;
}

#ifdef CONFIG_COMPAT
static long chardev_test_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    printk(KERN_DEBUG "%s()-%d ioctls chardev_test okay\r\n", __func__, __LINE__);

    return 0;
}
#else
#define chardev_test_compat_ioctl NULL
#endif

static const struct file_operations chardev_test_fops = {
	.open	= chardev_test_open,
	.read    = chardev_test_read,
	.write   = chardev_test_write,
	.unlocked_ioctl = chardev_test_unlocker_ioctl,
	#ifdef CONFIG_COMPAT
	.compat_ioctl  = chardev_test_compat_ioctl,
	#endif
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值