从上一篇我们看到了字符驱动的三个重要结构,那我现在跟大家详细的说说 struct file_operations
 
这个文件操作方法的数据结构。其实这结构中包含了用户空间所需要的大部分的系统调用函数指针,因此如何
 
我们应该如何去实现这些函数的策略呢?这就应该跟用户空间函数所实现的函数功能相对应,去实现这些函数
 
策略。本博客重点描述几个重要的比如 open、read、write、ioctl、lseek ... 至于这个结构里成员,大家
 
自己去看看内核源代码,我也贴出来了。
 
头文件:
#include
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 **);
};
 
   那我就废话少说,直接进入主题了:
  
   1、open 方法提供文件被操作之前状态(struct inode 结构描述)到文件被打开之后要操作状态(struct 
 
file结构描述)的转换,同时我们在系统调用时也指定了文件模式(可读可写等)、读写位置(fopen)等。因此
 
open 方法大部分应做如下操作:
  
   (1)确定打开哪个设备,如第一次打开,应做初始化工作;
  
   (2)open 系统调用以阻塞/非阻塞/可读可写...方式打开的;
 
   (3)涉及到硬件时,应考虑是否检查和初始化;
 
   (4)大部分我们要分配并填充要放进 filp->private_data 的任何数据结构;
 
   (5)打开同一设备文件记数(因为同一时间,不仅仅是单个进程或线程在操作)  MOD_INC_USE_COUNT;
 
【note】
(A)由于内核只知道 struct cdev 而不知道我们自己创建的数据结构,那我们如何去获取我们的数据结构
 
    呢?很幸运的是内核应该帮我们做,用container_of(pointer, container_type, 
 
     container_field);这个宏即可实现。
 
(B)为何要放在 struct file 中的(void *)private_data 中呢?
 
     如何不采用该方法,就应该定义一个全局变量,这样函数的可重入性就没了,更别说其他方面了。 
 
   2、open 方法相对应的应该就是 release,因此我们容易看出 release 应该要干嘛哦。
 
     (1)释放 open 分配在 filp->private_data 中的任何东西;
 
     (2)设备文件减数 MOD_DEC_USE_COUNT
 
     (3)在最后的 close 关闭设备。
 
   3、read/write 应用程序中 read(fd, buf,count)
                           write(fd, buf, count);
     ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
     ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
 
    对于 2 个方法, filp 是文件指针,对应打开的文件描述符 fd, count 是请求的传输数据大小. buff
 
参数指向持有被写入数据的缓存, 或者放入新数据的空缓存,它是用户指针,不能直接被内核解引用,这涉
 
及到内核安全问题. 最后, offp 是一个指针指向一个"longoffset type"对象, 它指出用户正在存取的文件
 
位置. 应用程序没有,是系统调用函数帮忙做好的。返回值是一个"signed size type";用 read 参数传递图
 
来讨论吧。
 
  
   图来自linux设备驱动程序,本博客中大部分参考来自该书本
  
 read 返回值 ret 解释:
 
   (1)ret = count,这种情况是最好不过的啦,说明所需数据全部被传送;
 
   (2)0 < ret < count,说明只有部分数据被传送出去;
 
   (3)ret = 0,说明达到文件末尾了;
 
   (4)ret < 0,表示文件出错,返回值会提示文件出了哪种类型错误;
 
     出错的典型返回值包括 -EINTR( 被打断的系统调用) 或者 -EFAULT( 坏地址 ).
 
  (5)刚开始没有数据可读,可后来会有,这种情况 就会出现阻塞现象,而系统调用在缺省情况下就是有阻
 
塞现象发生
 
   write 返回值 ret 解释:

 
   (1)ret = count,这种情况是最好不过的啦,说明所需数据全部传送完毕;
 
   (2)0 < ret < count,说明只有部分数据被传送,一般应用程序会重新写入;
 
   (3)ret = 0,没有数据可写了,可按具体情况定义;
 
   (4)ret < 0,表示文件出错,返回值会提示文件出了哪种类型错误;
 
     出错的典型返回值包括 -EINTR( 被打断的系统调用) 或者 -EFAULT( 坏地址 ).
 
   (5)刚开始没有内存可写,可后来会有,这种情况也会出现阻塞现象,而系统调用在缺省情况下就是有阻
 
塞现象发生
 
   4、ioctl
   int ioctl(int fd,unsigned long cmd,...)
 
  原型中的点表示这是一个可选的参数,存在与否依赖于控制命令(第2 个参数)是否涉及到与设备的数据交
 
互。
 
   ioctl 驱动方法有和用户空间版本不同的原型:
   int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
 
 cmd参数从用户空间传下来,可选的参数arg 以一个unsigned long 的形式传递,不管它是一个整数或一个指
 
针 。如果cmd命令不涉及数据传输,则第 3 个参数arg的值无任何意义。
 
   我想大家关注的是如何去实现ioctl方法吧。
 
【步骤】
 
(1)定义命令==>类型(魔数、数)、序号、传送方向、参数的大小
 
(2)实现命令==>返回值、参数使用、命令操作
 
【解析】
 
A)定义ioctl 命令的正确方法是使用4 个位段, 这个列表中介绍的符号定义在中:
 
_IO(type,nr)              //没有参数的命令
_IOR(type,nr,datatype)  //从驱动中读数据
_IOW(type,nr,datatype)  //写数据到驱动
_IOWR(type,nr,datatype) //双向传送,type 和number 成员作为参数被传递。 
 
/**type 魔数 一般采用宏定义,表明了哪个设备的命令,具体查看/Docementation/ioctl-number.txt**/
 
/**number 序号 8位宽,可定义255个命令,(nr)不一定从 0 开始定义**/
 
/**Direction 方向,决定了往设备读还是写,甚至可读可写,作用在于具有一定的保护性**/
 
/**size 参数大小(datatype)**/
 
举个例子,让大家更容易理解一些:
 
#define XXX_IOC_MAGIC ‘m’ //定义魔数==>单引号要小心,不要随便使用不确定的魔数
#define XXX_IOCSET   _IOW(XXX_IOC_MAGIC, 0, int)
#define XXX_IOCGQSET _IOR(XXX_IOC_MAGIC, 1, int)
 
B)定义好了命令,下一步就是要实现Ioctl函数了,看个具体例子清楚吧:关键在于命令如何操作、参数要检
 
查、返回值要准确。

  1. /**命令有效性的检查**/
  2. if (_IOC_TYPE(cmd) != XXX_IOC_MAGIC)
  3. {
  4. return -ENOTTY;
  5. }
  6. if (_IOC_NR(cmd) > XXX_IOC_MAXNR)
  7. {
  8. return -ENOTTY;
  9. }
  10. /**参数有效性检查**/
  11. if (_IOC_DIR(cmd) & _IOC_READ)
  12. {                                    /*?为何_IOC_READ对应_IOC_WRITE*/
  13. err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
  14. }
  15. else if (_IOC_DIR(cmd) & _IOC_WRITE)
  16. {
  17. err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
  18. }
  19. if (err)
  20. {
  21. return -EFAULT;
  22. }
  23. /**命令的实现**/
  24. switch(cmd)
  25. {
  26. case XXX_IOCSET:
  27. ...
  28. break;
  29. case XXX_IOCGQSET:
  30. ...
  31. break;
  32. default:
  33. break;
  34. }

字符驱动之二操作方法(struct file_operations)【转】的更多相关文章

  1. linux 字符驱动

    1 结构体说明:     struct cdev {         struct kobject kobj;          // 每一个 cdev 都是一个 kobject         st ...

  2. 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write

    基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read.write 0. 导语 在上一篇博客里面,基于OMAPL138的字符驱动_GPIO驱动AD9833(一)之 ...

  3. 字符设备驱动(二)---key的使用:查询方式

    ---恢复内容开始--- 一.硬件电路 1.1 电路原理图 S1-S5共5个按键,其中,S2-S4为中断按键,S1为复位按键.S1直接为硬件复位电路,并不需要我们写进驱动. 单片机接口如下图: 由图中 ...

  4. 第一个驱动之字符设备驱动(二)mdev

    mdev是busybox提供的一个工具,用在嵌入式系统中,相当于简化版的udev,作用是在系统启动和热插拔或动态加载驱动程序时, 自动创建设备节点.文件系统中的/dev目录下的设备节点都是由mdev创 ...

  5. 如何编写一个简单的Linux驱动(二)——设备操作集file_operations

    前期知识 如何编写一个简单的Linux驱动(一)--驱动的基本框架 前言 在上一篇文章中,我们学习了驱动的基本框架.这一章,我们会在上一章代码的基础上,继续对驱动的框架进行完善.要下载上一篇文章的全部 ...

  6. MPU6050带字符驱动的i2c从设备驱动1

    开干: 1.闲言碎语 这个驱动,越写觉的越简单,入门难,入门之后感觉还好.Linux开发还是比较友好的. 2.编写MPU6050带字符驱动的i2c从设备驱动 要实现的功能就是,将MPU6050作为字符 ...

  7. 旧接口注册LED字符驱动设备(静态映射)

    #include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...

  8. 旧接口注册LED字符驱动设备(动态映射)

    #include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...

  9. 新接口注册LED字符驱动设备

    #include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...

随机推荐

  1. 自学Zabbix5.1 zabbix maintenance维护周期

    自学Zabbix5.1 zabbix maintenance维护周期 1. 概述 你可以定义维护周期在主机或主机组里.这里有2种维护状态: 依旧收集数据   继续对目标的监控数据的收集 暂停收集数据 ...

  2. 洛谷 P2680 运输计划 解题报告

    P2680 运输计划 题目背景 公元2044年,人类进入了宇宙纪元. 题目描述 公元2044年,人类进入了宇宙纪元. \(L\)国有\(n\)个星球,还有\(n-1\)条双向航道,每条航道建立在两个星 ...

  3. virtualenv 包管理

    创建虚拟环境: 1) pip install virtualenv 2) virtualenv  DemoEnv(虚拟环境名) 3) 此处windows和linux不同系统下的文件结构略有不同 lin ...

  4. CF1114D Flood Fill(DP)

    题目链接:CF原网 题目大意:$n$ 个方块排成一排,第 $i$ 个颜色为 $c_i$.定义一个颜色联通块 $[l,r]$ 当且仅当 $l$ 和 $r$ 之间(包括 $l,r$)所有方块的颜色相同.现 ...

  5. Coablt strike官方教程中文译版本

    安装和设置 系统要求 Cobalt Strike的最低系统要求 2 GHz +以上的cpu 2 GB RAM 500MB +可用空间 在Amazon的EC2上,至少使用较高核数的CPU(c1.medi ...

  6. python数据类型(二)

    跟着慕课网练习的,一些简单的知识点如下

  7. poj 3061(二分 or 尺取法)

    传送门:Problem 3061 https://www.cnblogs.com/violet-acmer/p/9793209.html 马上就要去上课了,先献上二分AC代码,其余的有空再补 题意: ...

  8. get方式传值中文乱码

    问题描述: 本机可以,服务器乱码: 解决方案: 我采用了第三条,改server.xml配置 方法一: get方式提交的参数编码,只支持iso8859-1编码.因此,如果里面有中文.在后台就需要转换编码 ...

  9. 64位Ubuntu系统下ROP攻击

    64位Ubuntu系统下ROP攻击 基础知识 ROP攻击 ROP全称为Retrun-oriented Programmming(面向返回的编程)是一种新型的基于代码复用技术的攻击,攻击者从已有的库或可 ...

  10. hp电脑重装win7 64位 后 所有软件都装不上问题【转】

    hp 电脑重装后 所有软件都装不上问题 装了近100来次机,第一次遇到这样的. bug描述: 新笔记本刚装了纯净版的64位旗舰版win7,想装软件,就弹出已停止工作.比如装火狐浏览器,弹出火狐浏览器网 ...