写点什么

Linux 设备驱动系列(六)——文件操作

  • 2024-04-27
    浙江
  • 本文字数:3846 字

    阅读完需:约 13 分钟

Linux设备驱动系列(六)——文件操作

关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/JGThRju13r57iQM-BjeebQ

1 cdev 结构和文件操作

当需要对设备执行打开/关闭和读写操作时,我们需要在设备驱动程序中注册一些结构,主要是 struct cdev 和文件操作 struct file_operations。

1.1 cdev 结构

Linux 内核中,struct inode 用来表示具体的文件,它包含了文件的大量信息;而 struct file 表示一个打开的文件描述符。一个文件可能有多个 struc file 表示打开的文件描述符,但它们都指向一个相同的 struct inode 结构。


struct cdev 是 inode 结构其中的一个元素,它是 Linux 内核的一个内部结构体,用于表示字符设备。


struct cdev {   struct kobject kobj;   struct module *owner;   const struct file_operations *ops;   struct list_head list;   dev_t dev;   unsigned int count; };
复制代码


设备驱动程序需要设置 file_operations 和 owner(通常是 THIS_MODULE 宏)两个字段。


有两种分配和初始化 cdev 结构的方式:runtime allocation 和 own allocation。

1.1.1 Runtime Allocation

可以通过下面的方式在运行时获取一个单独的 cdev 结构:


struct cdev *my_cdev = cdev_alloc( );my_cdev->ops = &my_fops;
复制代码

1.1.2 Own Allocation

可以将 cdev 结构体嵌入到一个设备特定的结构中,然后通过下面的方法进行初始化:


void cdev_init(struct cdev *cdev, struct file_operations *fops);
复制代码


当 cdev 结构体设置好 file_operations 和 owner 后,可以通过下面的方法来通知给内核:


int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
复制代码


一旦 cdev_add()成功调用,相应的设备就能立即可用,并且 file_operations 中定义的所有函数都能够被调用。


最后可以通过下面方法释放 cdev 结构体:


void cdev_del(struct cdev *dev);
复制代码

1.2 文件操作

"linux/fs.h"头文件中定义的 file_operations 结构体,它是注册到设备驱动中字符设备的打开/关闭和读写等操作的回调函数。


它包含了一系列的函数指针,每个打开的文件都会通过 file_operations 关联到对应的操作处理函数集合。这些操作通常是作为系统调用的实现,例如 open、read、write 等等。


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 中包含了 struct module *owner 字段,它不是函数指针,而是”拥有“该结构体的模块指针,通常设置为"linux/module.h"中定义宏 THIS_MODULE。它用于防止 file_operations 还在使用的时候对应的内核模块被意外卸载。


一般情况下不需要设置所有的函数指针:


static struct file_operations fops ={  .owner    = THIS_MODULE,  .read      = my_read,  .write    = my_write,  .open      = my_open,  .release  = my_release,};
复制代码

1.2.1 read

ssize_t (*read) (struct file *, char _ _user *, size_t, loff_t *);
复制代码


用于从设备中获取数据,非负数返回值表示成功读取的字节数。

1.2.2 write

ssize_t (*write) (struct file *, const char _ _user *, size_t, loff_t *);
复制代码


用于往设备发送数据,非负数返回值表示成功写入的字节数。

1.2.3 ioctl

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
复制代码


ioctl 系统调用提供了一种发送设备特定命令的方式,例如格式化磁盘,它既不是读也不是写操作。

1.2.4 open

int (*open) (struct inode *, struct file *);
复制代码


打开设备文件的时候会调用 open 函数。

1.2.5 release (close)

int (*release) (struct inode *, struct file *);
复制代码


struct file 被释放的时候会调用 release 函数。

2 文件操作示例

下面是一个简单的设备文件操作的示例:


file_ops.c


#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/err.h>#include <linux/cdev.h>#include <linux/device.h>dev_t dev = 0;static struct class *dev_class;static struct cdev my_cdev;
static int __init my_init(void);static void __exit my_exit(void);static int my_open(struct inode *inode, struct file *file);static int my_release(struct inode *inode, struct file *file);static ssize_t my_read(struct file *filp, char __user *buf, size_t len,loff_t * off);static ssize_t my_write(struct file *filp, const char *buf, size_t len, loff_t * off);static struct file_operations fops ={ .owner = THIS_MODULE, .read = my_read, .write = my_write, .open = my_open, .release = my_release,};
static int my_open(struct inode *inode, struct file *file){ pr_info("Driver Open Function Called\n"); return 0;}
static int my_release(struct inode *inode, struct file *file){ pr_info("Driver Release Function Called\n"); return 0;}
static ssize_t my_read(struct file *filp, char __user *buf, size_t len, loff_t *off){ pr_info("Driver Read Function Called\n"); return 0;}
static ssize_t my_write(struct file *filp, const char __user *buf, size_t len, loff_t *off){ pr_info("Driver Write Function Called\n"); return len;}
static int __init my_init(void){ if((alloc_chrdev_region(&dev, 0, 1, "my_dev")) <0){ pr_err("Cannot allocate major number\n"); return -1; } pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
cdev_init(&my_cdev,&fops); if((cdev_add(&my_cdev, dev, 1)) < 0){ pr_err("Cannot add the device to the system\n"); goto r_class; }
if(IS_ERR(dev_class = class_create(THIS_MODULE,"my_class"))){ pr_err("Cannot create the struct class\n"); goto r_class; }
if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"my_device"))){ pr_err("Cannot create the Device\n"); goto r_device; } pr_info("Device Driver Inserted\n"); return 0; r_device: class_destroy(dev_class); r_class: unregister_chrdev_region(dev,1); return -1;}
static void __exit my_exit(void){ device_destroy(dev_class,dev); class_destroy(dev_class); cdev_del(&my_cdev); unregister_chrdev_region(dev, 1); pr_info("Device Driver Removed\n");}
module_init(my_init);module_exit(my_exit);
MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("Simple Linux device driver (File Operations)");MODULE_VERSION("1.3");
复制代码


编译运行如下:



关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/JGThRju13r57iQM-BjeebQ


发布于: 刚刚阅读数: 6
用户头像

聚沙成塔 2023-01-12 加入

分享Linux内核开发相关的编程语言、开发调试工具链、计算机组成及操作系统内核知识、Linux社区最新资讯等

评论

发布
暂无评论
Linux设备驱动系列(六)——文件操作_Linux内核_Linux内核拾遗_InfoQ写作社区