对于struct file_operations中ioctl消失的学习笔记

2024-02-05 16:48

本文主要是介绍对于struct file_operations中ioctl消失的学习笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

     很久都没有写驱动代码了,对于一些驱动相关的内核变化也没有怎么关心。这次重游《LDD3》获益良多,其值对于struct file_operations中ioctl的消失也让我长了不少见识。
当年看《LDD3》的时候已经注意到了书中对ioctl的评价不是很好:“ioctl调用的非结构化本质导致众多内核开发者倾向于放弃它。” ,而在这次阅读3.0代码的时候,这个成员在struct file_operations中早已消失了。这个激起了我学习的兴趣,以下是对这个ioctl的学习小结:

1、消失的确切时间
    ioctl的消失到底是从哪个版本开始的?网上给出的时间是2.6.36开始。网上就是这么说,但是自己必须找到代码中的证据。于是我通过git搜索主线内核代码,找到的删除ioctl的那个提交:

  1. commit b19dd42faf413b4705d4adb38521e82d73fa4249
  2. Author: Arnd Bergmann <arnd@arndb.de>
  3. Date: Sun Jul 4 00:15:10 2010 +0200
  4. bkl: Remove locked .ioctl file operation
  5. The last user is gone, so we can safely remove this
  6. Signed-off-by: Arnd Bergmann <arnd@arndb.de>
  7. Cc: John Kacur <jkacur@redhat.com>
  8. Cc: Al Viro <viro@ZenIV.linux.org.uk>
  9. Cc: Thomas Gleixner <tglx@linutronix.de>
  10. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>

好不容易找到了这个提交,好的,这样就可以确定消失的时间了:

  1. git tag --contains b19dd42
  2. v2.6.36
  3. v2.6.36-rc1
  4. v2.6.36-rc2
  5. v2.6.36-rc3
  6. v2.6.36-rc4
  7. v2.6.36-rc5
  8. v2.6.36-rc6
  9. v2.6.36-rc7
  10. v2.6.36-rc8
  11. ......以下省略ooxx行

可以证明ioctl消失的版本是v2.6.35到v2.6.36-rc1间,于是我导出了v2.6.35到v2.6.36-rc1的补丁,果真在其中!
git diff v2.6.35..v2.6.36-rc1 > ../temp.patch

补丁过大不易上传,请自行生成。

2、消失的原因

   简单的概括:这次ioctl的消失,并不是要把ioctl清理出去,而是要逐步的清理大内核锁(BKL)。

   这个让ioctl消失的过渡期长达5年,从2005年开始内核黑客就开始替换ioctl了。具体的原因在lwn.net中有一篇很好的文章:The new way of ioctl()。我将他翻译了一下:ioctl()的新方法(必看)

   当然,顺便了解一下大内核锁也是很有必要的:转载好文:《大内核锁将何去何从》

3、ioctl的替代者

    对于原来的ioctl,其实可以叫做locked ioctl。这个其实是相对于他的替代方法来讲的。我们来看看2.6.35以前在struct file_operations中有关ioctl的成员:

  1. /*
  2.  * NOTE:
  3.  * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
  4.  * can be called without the big kernel lock heldin all filesystems.
  5.  */
  6. struct file_operations {
  7.     struct module *owner;
  8.     loff_t (*llseek)(struct file*, loff_t,int);
  9.     ssize_t (*read)(struct file*, char __user*, size_t, loff_t*);
  10.     ssize_t (*write)(struct file*,const char __user*, size_t, loff_t*);
  11.     ssize_t (*aio_read)(struct kiocb*,const struct iovec*, unsigned long, loff_t);
  12.     ssize_t (*aio_write)(struct kiocb*,const struct iovec*, unsigned long, loff_t);
  13.     int (*readdir)(struct file*, void*, filldir_t);
  14.     unsigned int (*poll)(struct file*, struct poll_table_struct*);
  15.     int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  16.     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  17.     long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  18.     int (*mmap)(struct file*, struct vm_area_struct*);
  19.     int (*open)(struct inode*, struct file*);
  20.     int (*flush)(struct file*, fl_owner_t id);
  21.     int (*release)(struct inode*, struct file*);
  22.     int (*fsync)(struct file*, struct dentry*,int datasync);
  23.     int (*aio_fsync)(struct kiocb*,int datasync);
  24.     int (*fasync)(int, struct file*,int);
  25.     int (*lock)(struct file*,int, struct file_lock*);
  26.     ssize_t (*sendpage)(struct file*, struct page*,int, size_t, loff_t*,int);
  27.     unsigned long (*get_unmapped_area)(struct file*, unsigned long, unsigned long, unsigned long, unsigned long);
  28.     int (*check_flags)(int);
  29.     int (*flock)(struct file*,int, struct file_lock*);
  30.     ssize_t (*splice_write)(struct pipe_inode_info*, struct file*, loff_t*, size_t, unsignedint);
  31.     ssize_t (*splice_read)(struct file*, loff_t*, struct pipe_inode_info*, size_t, unsignedint);
  32.     int (*setlease)(struct file*, long, struct file_lock**);
  33. };

这个结构体其实是在过渡期的结构体,unlocked_ioctl就是ioctl的替代者。对于新的驱动,不要再使用ioctl了,而是使用unlocked_ioctl。

4、调用ioctl与unlocked_ioctl在内核代码上的不同

   其实ioctl与unlocked_ioctl所对应的系统调用都是ioctl。但是在应用层调用ioctl的时候,对于我们实现ioctl或者unlocked_ioctl有什么不同呢?这里我们可以追溯一下ioctl系统调用代码的执行过程,这里我简单的写出这个系统调用对于设备驱动(一般是设备驱动使用ioctl)的执行顺序:(fs/ioctl.c)

 SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)--->do_vfs_ioctl---> vfs_ioctl

ioctl与unlocked_ioctl的区别就体现在了这个vfs_ioctl中,我们先来看ioctl被删除前的函数:

  1. /**
  2.  * vfs_ioctl -call filesystem specific ioctl methods
  3.  * @filp:    open fileto invoke ioctl methodon
  4.  * @cmd:    ioctl commandto execute
  5.  * @arg:    command-specific argumentfor ioctl
  6.  *
  7.  * Invokes filesystem specific ->unlocked_ioctl,if one exists; otherwise
  8.  * invokes filesystem specific ->ioctl method.If neither method exists,
  9.  * returns -ENOTTY.
  10.  *
  11.  * Returns 0 on success,-errnoon error.
  12.  */
  13. static long vfs_ioctl(struct file *filp, unsigned int cmd,
  14.          unsigned long arg)
  15. {
  16.     int error=-ENOTTY;

  17.     if (!filp->f_op)
  18.         goto out;

  19.     if (filp->f_op->unlocked_ioctl){
  20.         error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
  21.         if (error==-ENOIOCTLCMD)
  22.             error = -EINVAL;
  23.         goto out;
  24.     } elseif(filp->f_op->ioctl){
  25.         lock_kernel();
  26.         error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,
  27.                      filp, cmd, arg);
  28.         unlock_kernel();
  29.     }

  30.  out:
  31.     return error;
  32. }

从这个函数中我们可以看出:

  1. ioctl是受到大内核锁保护的,而unlocked_ioctl是直接执行的。
  2. unlocked_ioctl优先级高于ioctl,如果存在unlocked_ioctl,则执行unlocked_ioctl,否则才执行ioctl。这个优先级的产生明显是为了过渡。

而在ioctl被删除后,vfs_ioctl函数也做了相应的改变(Linux-3.0):

  1. /**
  2.  * vfs_ioctl -call filesystem specific ioctl methods
  3.  * @filp:    open fileto invoke ioctl methodon
  4.  * @cmd:    ioctl commandto execute
  5.  * @arg:    command-specific argumentfor ioctl
  6.  *
  7.  * Invokes filesystem specific ->unlocked_ioctl,if one exists; otherwise
  8.  * returns -ENOTTY.
  9.  *
  10.  * Returns 0 on success,-errnoon error.
  11.  */
  12. static long vfs_ioctl(struct file *filp, unsigned int cmd,
  13.          unsigned long arg)
  14. {
  15.     int error=-ENOTTY;

  16.     if (!filp->f_op||!filp->f_op->unlocked_ioctl)
  17.         goto out;

  18.     error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
  19.     if (error==-ENOIOCTLCMD)
  20.         error = -EINVAL;
  21.  out:
  22.     return error;
  23. }

5、在驱动编程时的注意事项

  • 在注册文件操作方法的结构体struct file_operations的时候原先的.ioctl=OOXX;替换为 .unlocked_ioctl=OOXX;
  • 但是要注意ioctl和unlocked_ioctl的定义有一点不同:unlocked_ioctl少了一个inode参数。但是如果方法中真的需要其中的数据,可以通过filp->f_dentry->d_inode获得。
  • 由于失去了大内核锁的保护,所以必须unlocked_ioctl方法中自行实现锁机制,以保证不会在操作设备的时候(特别在SMP系统中)产生竞态。(也就实现了用小锁替换大锁)

这篇关于对于struct file_operations中ioctl消失的学习笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/681559

相关文章

VMWare报错“指定的文件不是虚拟磁盘“或“The file specified is not a virtual disk”问题

《VMWare报错“指定的文件不是虚拟磁盘“或“Thefilespecifiedisnotavirtualdisk”问题》文章描述了如何修复VMware虚拟机中出现的“指定的文件不是虚拟... 目录VMWare报错“指定的文件不是虚拟磁盘“或“The file specified is not a virt

提示:Decompiled.class file,bytecode version如何解决

《提示:Decompiled.classfile,bytecodeversion如何解决》在处理Decompiled.classfile和bytecodeversion问题时,通过修改Maven配... 目录问题原因总结问题1、提示:Decompiled .class file,China编程 bytecode

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识