ioctl

除了读取和写入设备之外,大部分驱动程序还需要另外一种能力,即通过设备驱动程序执行各种类型的硬件控制,通常这种需求使用ioctl方法支持,该方法实现了同名的系统调用;

在用户空间,ioctl系统调用的原型如下:

 int ioctl(int d, int request, ...);

原型中的可变参数不是数目不定的一串参数,而只是一个可选参数;可选参数的具体格式依赖于控制命令,也就是第二个参数;某些控制命令不需要参数,某些需要一个整数参数,某些需要一个指针参数;使用指针参数可以向ioctl传递任意数据,这样设备可以与用户空间交换任意数量的数据;

ioctl系统调用内核中的定义如下:

 SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)

ioctl在file_operations中的函数原型如下:

 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

大多数ioctl的实现都包含了一个switch语句来根据cmd参数选择对应的操作;不同的命令被赋予不同的数值,为了简化代码,通常会在代码中使用符号名代替数值,这些符号名由c语言的预处理语句定义,订制设备驱动程序通常会在它们的头文件中声明这些符号;

选择ioctl命令

ioctl命令号码定义使用了4个为字段,其含义如下:

type:幻数;选择一个号码,并在整个驱动程序中使用这个号码;这个字段是8位宽(_IOC_TYPEBITS);通常使用一个英文字母;比如#define MY_IOC_MAGIC ‘k’;需要注意避免命令号冲突;

number:序数(顺序编号);是8位宽(_IOC_NRBITS);通常从0开始顺序编号;

direction:如果命令涉及到数据的传输,则该位字段定义数据传输的方向;可以使用的值包括_IOC_NONE(没有数据传输)、_IOC_READ、_IOC_WRITE、_IOC_READ|_IOC_WRITE(双向传输);数据传输是从应用程序的角度来看的,也就是说,IOC_READ意味着从设备中读取数据,所以驱动程序必须向用户空间写入数据;注意,该字段是一个位掩码,因此可以使用逻辑AND操作从中分解出_IOC_READ和_IOC_WRITE;

size:所涉及的用户数据大小;这个字段的宽度与体系结构有关,通常是13或者14位,具体可以通过宏_IOC_SIZEBIT找到针对特定体系结构的具体数值;系统并不强制只用这个字段,也就是说,内核不会检查这个字段;对该字段的正确使用可以帮助我们检测用户程序的错误,并且如果我们从不改变相关的数据项大小的话,这个位字段哈可以帮我们实现向后的兼容性;但是,如果需要很大的数据传输,则可以忽略这个位字段;

用于构造命令编号的宏如下,其中type和number位字段通过参数传入,而size位字段通过对datatype参数取sizeof获取的;

_IO(type,nr)用于构造无参数的命令编号;

_IOR(type,nr,size)用于构造从驱动程序读取数据的命令编号;

_IOW(type,nr,size)用于构造用用户空间写入数据的命令;

_IOWR(type,nr,size)用于双向传输;

用于解开位字段的宏如下:

_IOC_DIR(nr)、_IOC_TYPE(nr)、_IOC_NR(nr)、_IOC_SIZE(nr);

返回值

ioctl实现通常就是一个基于命令号的switch语句;当命令号不合法时,有些内核函数返回-ENVAL(非法参数);POSIX标准规定,应该返回-ENOTTY,C库将这个错误码解释为不合适的设备ioctl;但是普遍做法是返回-EINVAL;

使用ioctl参数

ioctl的附加参数,如果是个整数,直接使用就可以了,如果是个指针,就需要注意一些问题;

当用一个指针指向用户空间时,必须确保指向的用户空间是合法的;对未验证的用户空间指针的访问,可能导致内核oops,系统崩溃或者安全问题;驱动程序应该负责对每个用到的用户空间地址做适当的检查,如果是非法地址则应该返回一个错误;

copy_from_user和copy_to_user可以安全的与用户空间交换数据,这两个函数也可以在ioctl中使用,但是因为ioctl通常涉及较小的数据项,因此可以通过其他方法更有效的操作;为此,我们首先要通过access_ok函数验证地址,而不传输数据,函数声明如下:<asm-generic/uaccess.h>

 #define access_ok(type, addr, size) __access_ok((unsigned long)(addr),(size))

第一个参数type应是VERIFY_READ或者VERIFY_RIWTE,取决于要执行的动作是读取还是写入用户空间内存区;addr参数是一个用户空间地址,size是字节数,如ioctl要从用户空间读取一个整数,则size是sizeof(int);如果在指定地址处既要读取又要写入,则应该用VERIFY_WRITE,因为它是VERIFY_READ的超集;

与大多数函数不同,access_ok返回1标识成功,0标识失败;如果返回失败,则通常需要返回-EFAULT给调用者;

关于该函数,需要注意两点:第一,它并没有完成验证内存的全部工作,而只是检查了所引用的内存是否位于进程有对应访问权限的区域中,特别是要确保访问的地址没有指向内核空间的内存区;第二,大多数驱动程序代码中都不需要真正调用access_ok,因为内存管理程序会处理它;

数据传送

除了copy_from_user和copy_to_user函数之外,内核还提供了常用的数据大小为1,2,4,8字节优化过的一组函数;

 #define put_user(x, ptr)
#define __put_user(x, ptr) #define get_user(x, ptr)
#define __get_user(x, ptr)

其中put_user函数把数据x写到用户空间;它们相对比较快,当需要传递单个数据时,使用这些宏而不是用copy_to_user;由于这些宏在展开时不做类型检查,所以可以传递给put_user任意类型的指针,只要是个用户空间地址就行;传递数据大小依赖于ptr参数的类型,在编译时由编译器的内建指令sizeof和typeof确定;总之,若ptr是一个字符指针,就传递1个字节,2,4,8字节的情况类似;

put_user已经进行了检查确保进程可以写入指定的内存地址,并在成功时返回0,出错是返回-EFAULT;__put_user则做的检查少些,它不调用access_ok,但是如果地址指向的用户不能写入内存,会出现操作失败,因为__put_user要在已经使用过access_ok检查后使用;

get_user从用户空间接收一个数据,接收的数值保存在局部变量x中,返回值指明了操作是否成功;通用,__get_user应该在操作地址已经被access_ok检查通过后使用;

如果是不满足上述的传递大小的数值,则必须使用copy_to_user和copy_from_user;

权能与受限操作

权限相关的定义在<uapi/linux/capability.h>中,其中包含了系统能够理解的所有权能;不修改内核源码,驱动程序无法定义新的权能;对驱动程序开发来讲有意义的权能如下:

CAP_DAC_OVERRIDE

越过文件或者目录的访问限制的能力;

CAP_NET_ADMIN

执行网络管理任务的能力,包括哪些能影响网络接口的任务;

CAP_SYS_MODULE

载入或者卸载内核模块的能力

CAP_SYS_RAWIO

执行裸IO操作的能力,例如,访问设备端口或者直接与USB设备通信;

CAP_SYS_ADMIN

截获的能力,它提供了访问许多系统管理操作的途径;

在执行某项特权操作之前,需要检查调用进程是否有合适的权能;如果不进行这类检查,将导致用户进程执行非授权操作,从而影响系统稳定性和安全性;权能检查通过capable实现;在<linux/capability.h>中;

 bool capable(int cap);

Linux设备驱动程序 之 ioctl的更多相关文章

  1. Linux设备驱动之Ioctl控制

    大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力. 一.在用户空间,使用ioctl系统调用来控制设备,原型如下: int ioctl(int fd,unsigned long cm ...

  2. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

  3. 嵌入式Linux设备驱动程序:编写内核设备驱动程序

    嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...

  4. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  5. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  6. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  7. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  8. 教你写Linux设备驱动程序:一个简短的教程

    教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html

  9. linux设备驱动程序_hello word 模块编译各种问题集锦

    在看楼经典书籍<linux设备驱动程序>后,第一个程序就是编写一个hello word 模块. 原以为非常easy,真正弄起来,发现问题不少啊.前两天编过一次,因为没有记录,今天看的时候又 ...

随机推荐

  1. Javascript中的继承与复用

    实现代码复用的方法包括:工厂模式.构造函数模式.原型模式(<高三>6.2章 P144),它们各自的特点归结如下:1.工厂模式虽然使创建对象一定程度上实现了代码复用,但却没有解决对象识别问题 ...

  2. SpringBoot项目的限流

    开发访问量比较大的系统是,爬虫的目的就是解决访问量大的问题:缓存穿透是为了保护后端数据库查询服务:计数服务解决了接近真实访问量以及数据库服务的压力. 架构图 限流 就拿十万博客来说,如果存在热点文章, ...

  3. SQLServer从渣仔到小白

    一.将查询结果插入到另一张表 1. 查询结果插入新表 select * into tableA from tableB where … 2. 查询结果插入已经存在的表 insert into tabl ...

  4. js的一些兼容融性问题

    1.非行内样式获取 高级浏览器 getComputedStyle(obox.false)//获取所有属性 ie浏览器 box.currentStyle//获取所有属性 兼容写法 function ge ...

  5. 组合:abc三个字符的所有组合

    求所有组合也就是abc各个位是否选取的问题,第一位2中可能,第二位2种...所以一共有2^n种.用0表示不取,1表示选取,这样可以用110这样的形式表示ab.abc一共的表示形式从0到2^3-1.然后 ...

  6. 13.MySQL锁机制

    锁的分类 从对数据的类型 (读\写)分: 1.读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响 2.写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁 从对数据操作的粒度 ...

  7. RobHess的SIFT代码解析步骤一

    平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 主要参考:1.代码:RobHess的SIFT源码:SIFT+KD ...

  8. Hadoop_33_Hadoop HA的搭建

    Hadoop HA的搭建,可参考链接:https://blog.csdn.net/mrbcy/article/details/64939623 说明:    1.在hadoop2.0中通常由两个Nam ...

  9. Protocol handler start failedCaused by: java.net.SocketException: Permission denied

    最近在使用mac启动项目的时候,发现原本在Windows下正常跑的项目报错如下: Protocol handler start failedCaused by: java.net.SocketExce ...

  10. 区块链火爆,再不知道Golang就晚了

    Golang,也叫Go语言,是2009年刚刚被发发布的一门新语言. 区块链,是2019年我国提出的新战略. 一个不争的事实就是,大多数从事区块链开发的小伙伴都是用Golang,大多数招聘区块链技术工作 ...