一、ioctl
1)概念:#include<sys/ioctl.h>
2)功 能: 控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令 ,有些命令需要控制参数,这些数据是不能用read / write 读写的,称为Out-of-band数据。也就是说,read / write 读写的数据是in-band数据,是I/O操作的主体,而ioctl
命令传送的是控制信息,其中的数据是辅助的数据。
3)用 法: int ioctl(int handle, int cmd,[int *argdx, int argcx]);
4)返回值:成功为0,出错为-1
usr/include/asm-generic/ioctl.h中定义的宏的注释:
#define _IOC_NRBITS 8 //序数(number)字段的字位宽度,8bits
#define _IOC_TYPEBITS 8 //幻数(type)字段的字位宽度,8bits
#define _IOC_SIZEBITS 14 //大小(size)字段的字位宽度,14bits
#define _IOC_DIRBITS 2 //方向(direction)字段的字位宽度,2bits
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) //序数字段的掩码,0x000000FF
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) //幻数字段的掩码,0x000000FF
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) //大小字段的掩码,0x00003FFF
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) //方向字段的掩码,0x00000003
#define _IOC_NRSHIFT 0 //序数字段在整个字段中的位移,0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) //幻数字段的位移,8
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) //大小字段的位移,16
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) //方向字段的位移,30
5)ioctl 系统调用原型
int (*ioctl) (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg);
需要注意的是:不管可选的参数arg是否由用户给定为一个整数或一个指针,它都以一个unsigned long的形式传递。如果调用程序不传递arg参数, 被驱动收到的 arg 值是未定义的。因为在arg参数上的类型检查被关闭了,所以若一个非法参数传递给 ioctl,编译器是无法报警的,且任何关联的错误难以查找.
二,选择ioctl命令
在编写ioctl代码前,需要选择对应不同命令的编号,多数程序员选择从0开始的一组小编号。为了防止向错误的设备使用正确的命令,命令号应该在系统范围内唯一。为方便程序员创建唯一的 ioctl 命令代号, 每个命令号被划分为多个位字段。要按 Linux 内核的约定方法为驱动选择 ioctl 的命令号, 应该首先看看 include/asm/ioctl.h
和 Documentation/ioctl-number.txt。 (linux第一个版本使用了一个16位编号,高八位:幻数 低八位:序列号)
要使用的位字段符号定义在 :
type(幻数):8 位宽(_IOC_TYPEBITS),参考ioctl-number.txt选择一个数,并在整个驱动中使用它。
number(序数):顺序编号,8 位宽(_IOC_NRBITS)。
direction(数据传送的方向):可能的值是 _IOC_NONE(没有数据传输)、_IOC_READ、 _IOC_WRITE和 _IOC_READ|_IOC_WRITE (双向传输数据)。该字段是一个位掩码(两位), 因此可使用 AND 操作来抽取_IOC_READ 和 _IOC_WRITE。
size(数据的大小):宽度与体系结构有关,ARM为14位.可在宏 _IOC_SIZEBITS 中找到特定体系的值.
<linux/ipctl.h>中包含<asm/ioctl.h>中包含的 定义了一些构造命令编号的宏:
_IO(type,nr)/*没有参数的命令*/
_IOR(type, nr, datatype)/*从驱动中读数据*/
_IOW(type,nr,datatype)/*写数据*/
_IOWR(type,nr,datatype)/*双向传送*/
/*type 和 number 成员作为参数被传递, 并且 size 成员通过应用 sizeof 到 datatype 参数而得到*/
这个头文件还定义了用来解开这个字段的宏:
_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)
三,返回值
POSIX 标准规定:如果使用了不合适的 ioctl 命令号,应当返回-ENOTTY 。这个错误码被 C 库解释为"不合适的设备 ioctl。然而,它返回-EINVAL仍是相当普遍的。
四,预定义命令
有一些ioctl命令是由内核识别的,当这些命令用于自己的设备时,他们会在我们自己的文件操作被调用之前被解码. 因此, 如果你选择一个ioctl命令编号和系统预定义的相同时,你永远不会看到该命令的请求,而且因为ioctl 号之间的冲突,应用程序的行为将无法预测。预定义命令分为 3 类:
(1)用于任何文件(常规, 设备, FIFO和socket) 的命令
(2)只用于常规文件的命令
(3)特定于文件系统类型的命令
下列 ioctl 命令是预定义给任何文件,包括设备特定文件:
FIOCLEX :设置 close-on-exec 标志(File IOctl Close on EXec)。
FIONCLEX :清除 close-no-exec 标志(File IOctl Not CLose on EXec)。
FIOQSIZE :这个命令返回一个文件或者目录的大小; 当用作一个设备文件, 但是, 它返回一个 ENOTTY 错误。
FIONBIO:"File IOctl Non-Blocking I/O"(在"阻塞和非阻塞操作"一节中描述)。
五,使用ioctl参数
在使用ioctl的可选arg参数时,如果传递的是一个整数,它可以直接使用。如果是一个指针,就必须小心。当用一个指针引用用户空间,我们必须确保用户地址是有效的,其校验(不传送数据)由函数 access_ok
实现,定义在 :
int access_ok(int type, const void *addr, unsigned long size);
type应当是 VERIFY_READ(读)或VERIFY_WRITE(读写);
addr 参数为用户空间地址,
size 为字节数,可使用sizeof()。
access_ok 返回一个布尔值: 1 是成功(存取没问题)和 0 是失败(存取有问题)。如果它返回假,驱动应当返回 -EFAULT 给调用者。
注意:首先, access_ok不做校验内存存取的完整工作; 它只检查内存引用是否在这个进程有合理权限的内存范围中,且确保这个地址不指向内核空间内存。其次,大部分驱动代码不需要真正调用 access_ok,而直接使用put_user(datum, ptr)和get_user(local, ptr),它们带有校验的功能,确保进程能够写入给定的内存地址,成功时返回 0, 并且在错误时返回 -EFAULT.。
<asm/uaccess.h>
put_user(datum, ptr)
__put_user(datum, ptr)
get_user(local, ptr)
__get_user(local, ptr)
这些宏它们相对copy_to_user 和copy_from_user快, 并且这些宏已被编写来允许传递任何类型的指针,只要它是一个用户空间地址. 传送的数据大小依赖 prt 参数的类型, 并且在编译时使用 sizeof 和 typeof 等编译器内建宏确定。他们只传送1、2、4或8 个字节。如果使用以上函数来传送一个大小不适合的值,结果常常是一个来自编译器的奇怪消息,如"coversion to non-scalar type requested". 在这些情况中,必须使用 copy_to_user
或者 copy_from_user。
__put_user和__get_user 进行更少的检查(不调用 access_ok), 但是仍然能够失败如果被指向的内存对用户是不可写的,所以他们应只用在内存区已经用 access_ok 检查过的时候。作为通用的规则:当实现一个 read 方法时,调用 __put_user 来节省几个周期, 或者当你拷贝几个项时,因此,在第一次数据传送之前调用 access_ok 一次。
六,权能与受限操作
Linux 内核提供了一个更加灵活的系统, 称为权能(capability)。内核专为许可管理上使用权能并导出了两个系统调用 capget 和 capset,这样可以从用户空间管理权能,其定义在 中。对设备驱动编写者有意义的权能如下:
CAP_DAC_OVERRIDE /*越过在文件和目录上的访问限制(数据访问控制或 DAC)的能力。*/
CAP_NET_ADMIN /*进行网络管理任务的能力, 包括那些能够影响网络接口的任务*/
CAP_SYS_MODULE /*加载或去除内核模块的能力*/
CAP_SYS_RAWIO /*进行 "raw"(裸)I/O 操作的能力. 例子包括存取设备端口或者直接和 USB 设备通讯*/
CAP_SYS_ADMIN /*截获的能力, 提供对许多系统管理操作的途径*/
七,ioctl命令的实现
八,Linux系统使用ioctl实例
分享到:
相关推荐
本文主要讲解 Linux 设备驱动开发中字符设备驱动的实现,通过对 globalmem 虚拟设备实例的描述,详细介绍了驱动程序设计的前奏、字符设备驱动程序框架、globalmem 虚拟设备实例的实现细节,并提供了测试应用程序的...
第6章涉及高级字符驱动操作。本章详细讲解了ioctl接口,它用于执行设备特定的操作;阻塞I/O;poll和select机制,它们用于非阻塞I/O;异步通知机制;以及如何在设备文件上设置存取控制。 第7章讲述了时间、延时和延...
第一天 1.Linux驱动简介 2.字符设备驱动程序设计 3.驱动调试技术 4. 并发与竞态 第二天 1.Ioctl型驱动 2.内核等待队列 3. 阻塞型驱动程序设计 4.Poll设备操作 第三天 1.Mmap设备操作 2. 硬件访问 3. 混杂...
《Linux设备驱动程序》(中文第二版)是一本深度探讨Linux系统下设备驱动程序开发的专业书籍,对于想要深入了解Linux内核以及如何与硬件交互的开发者来说,是不可或缺的参考资料。这本书详细介绍了Linux环境下设备...
在Linux系统中,驱动程序是操作系统与硬件设备之间的桥梁,它们负责抽象硬件的功能,使得操作系统可以方便地调用这些功能。本章《驱动开发之字符设备驱动程序》聚焦于openWRT环境中的一种特殊驱动——字符设备驱动。...
在第六章“高级字符设备操作”中,作者深入探讨了如何构建更复杂、功能更丰富的字符设备驱动程序。 ### ioctl系统调用 在讨论高级字符设备驱动程序时,一个关键的概念是`ioctl`系统调用。这个调用允许用户空间程序...
《Linux设备驱动程序》是关于Linux操作系统下设备驱动程序开发的重要参考书籍,尤其适用于嵌入式ARM平台的开发者。在Linux系统中,设备驱动程序是操作系统与硬件设备之间的桥梁,它使得操作系统能够控制和管理硬件...
《Linux驱动程序开发第三版》是一本深入探讨Linux内核驱动程序编程的权威书籍,英文版为程序员提供了全面了解和掌握Linux系统底层运作机制的宝贵资源。这本书详细讲解了如何为Linux操作系统编写设备驱动,涵盖了从...
《LINUX驱动程序开发实例第2版》是冯国进先生编著的一本关于Linux内核驱动程序开发的专业书籍,该书深入浅出地讲解了Linux系统下的驱动编写技术,旨在帮助读者掌握如何为Linux系统编写高效、稳定的硬件驱动程序。...
《Linux设备驱动程序》(中文版第三版)是一本深入探讨Linux系统下设备驱动程序开发的专业书籍。本书针对想要理解Linux内核工作原理以及如何编写高效、稳定的设备驱动的开发者,提供了详尽的指导和实践案例。以下是该...
第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 第七章 时间、延迟及延缓操作 度量时间差 获取当前时间 延迟执行 内核定时器 tasklet ...
总的来说,《Linux设备驱动程序第二版》是一本全面覆盖Linux驱动开发各个方面的教材,无论你是初学者还是有经验的开发者,都能从中受益匪浅。通过深入学习,你可以掌握编写高效、稳定的设备驱动程序所需的技能,为...
《Linux设备驱动开发详解-第6章字符设备驱动(二)-支持2个globalmem》 在Linux设备驱动开发中,字符设备驱动是重要的组成部分,它允许应用程序与硬件设备进行低级别的交互。本章节主要探讨如何扩展之前的单个...
《Linux设备驱动程序(LDD)第三版》是Linux内核开发者和系统工程师的必备参考书。这本书详尽地介绍了如何为Linux操作系统编写设备驱动程序,是深入理解Linux内核与硬件交互机制的重要教程。以下是对该书核心知识点的...
《Linux驱动程序第三版》是一本深入探讨Linux设备驱动编程的经典著作,由Robert Love编写,为开发者提供了详尽的理论知识和实践经验。该书的示例代码是理解Linux内核与设备交互的关键部分,帮助读者将理论转化为实际...
首先,Linux驱动程序是操作系统与硬件设备之间的桥梁,它们负责将硬件的功能接口化,使得操作系统能够有效地管理和使用硬件资源。Linux内核的模块化设计允许驱动程序动态加载和卸载,这为开发者提供了极大的灵活性。...