linux ioctl函数介绍

article/2025/11/8 7:08:02

1. 概念
ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。
在文件 I/O 中,ioctl 扮演着重要角色,本文将以驱动开发为侧重点,从用户空间到内核空间纵向分析 ioctl 函数。
在这里插入图片描述
2. 用户空间 ioctl

#include <sys/ioctl.h> 
int ioctl(int fd, int cmd, ...) ;
参数描述
fd文件描述符
cmd交互协议,设备驱动将根据 cmd 执行对应操作
可变参数 arg,依赖 cmd 指定长度以及类型

ioctl() 函数执行成功时返回 0,失败则返回 -1 并设置全局变量 errorno 值,如下:

EBADF d is not a valid descriptor. 
EFAULT argp references an inaccessible memory area. 
EINVAL Request or argp is not valid. 
ENOTTY d is not associated with a character special device. 
ENOTTY The specified request does not apply to the kind of object that the descriptor d references.

因此,在用户空间使用 ioctl 时,可以做如下的出错判断以及处理:

int ret;
ret = ioctl(fd, MYCMD);
if (ret == -1) {printf("ioctl: %s\n", strerror(errno));
}

在实际应用中,ioctl 最常见的 errorno 值为 ENOTTY(error not a typewriter),顾名思义,即第一个参数 fd 指向的不是一个字符设备,不支持 ioctl 操作,这时候应该检查前面的 open 函数是否出错或者设备路径是否正确
3. 驱动程序 ioctl

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

unlocked_ioctl,顾名思义,应该在无大内核锁(BKL)的情况下调用;compat_ioctl,compat 全称 compatible(兼容的),主要目的是为 64 位系统提供 32 位 ioctl 的兼容方法,也是在无大内核锁的情况下调用。
在《Linux Kernel Development》中对两种 ioctl 方法有详细的解说。在字符设备驱动开发中,一般情况下只要实现 unlocked_ioctl 函数即可,因为在 vfs 层的代码是直接调用 unlocked_ioctl 函数

// fs/ioctl.c
static long vfs_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{int error = -ENOTTY;if (!filp->f_op || !filp->f_op->unlocked_ioctl)           goto out;error = filp->f_op->unlocked_ioctl(filp, cmd, arg);if (error == -ENOIOCTLCMD) {error = -ENOTTY;}   out:return error;
}

4. ioctl 用户与驱动之间的协议
前文提到 ioctl 方法第二个参数 cmd 为用户与驱动的 “协议”,理论上可以为任意 int 型数据,可以为 0、1、2、3……,但是为了确保该 “协议” 的唯一性,ioctl 命令应该使用更科学严谨的方法赋值,在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段,如下图所示:
在这里插入图片描述
在内核中,提供了宏接口以生成上述格式的 ioctl 命令:

// include/uapi/asm-generic/ioctl.h
#define _IOC(dir,type,nr,size) \(((dir)  << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr)   << _IOC_NRSHIFT) | \((size) << _IOC_SIZESHIFT))

dir(direction),ioctl 命令访问模式(数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
type(device type),设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如
‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;
nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;
size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;

通常而言,为了方便会使用宏 _IOC() 衍生的接口来直接定义 ioctl 命令:

// include/uapi/asm-generic/ioctl.h
/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
_IO:       定义不带参数的 ioctl 命令
_IOW:      定义带写参数的 ioctl 命令(copy_from_user)
_IOR:      定义带读参数的ioctl命令(copy_to_user)
_IOWR:     定义带读写参数的 ioctl 命令

同时,内核还提供了反向解析 ioctl 命令的宏接口:

// include/uapi/asm-generic/ioctl.h
/* used to decode ioctl numbers */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

5. ioctl_test 实例分析
本例假设一个带寄存器的设备,设计了一个 ioctl 接口实现设备初始化、读写寄存器等功能。在本例中,为了携带更多的数据,ioctl 的第三个可变参数为指针类型,指向自定义的结构体 struct msg。
1、ioctl-test.h,用户空间和内核空间共用的头文件,包含 ioctl 命令及相关宏定义,可以理解为一份 “协议” 文件,代码如下:

// ioctl-test.h
#ifndef __IOCTL_TEST_H__
#define __IOCTL_TEST_H__#include <linux/ioctl.h>    // 内核空间
// #include <sys/ioctl.h>   // 用户空间/* 定义设备类型 */
#define IOC_MAGIC  'c'
/* 初始化设备 */
#define IOCINIT    _IO(IOC_MAGIC, 0)
/* 读寄存器 */
#define IOCGREG    _IOW(IOC_MAGIC, 1, int)
/* 写寄存器 */
#define IOCWREG    _IOR(IOC_MAGIC, 2, int)
#define IOC_MAXNR  3
struct msg {int addr;unsigned int data;
};
#endif

2、ioctl-test-driver.c,字符设备驱动,实现了unlocked_ioctl 接口,根据上层用户的 cmd 执行对应的操作(初始化设备、读寄存器、写寄存器)。在接收上层 cmd 之前应该对其进行充分的检查,流程及具体代码实现如下:

// ioctl-test-driver.c
static const struct file_operations fops = {.owner = THIS_MODULE,.open = test_open,.release = test_close,.read = test_read,.write = etst_write,.unlocked_ioctl = test_ioctl,
};
static long test_ioctl(struct file *file, unsigned int cmd, \unsigned long arg)
{//printk("[%s]\n", __func__);int ret;struct msg my_msg;/* 检查设备类型 */if (_IOC_TYPE(cmd) != IOC_MAGIC) {pr_err("[%s] command type [%c] error!\n", \__func__, _IOC_TYPE(cmd));return -ENOTTY; }/* 检查序数 */if (_IOC_NR(cmd) > IOC_MAXNR) { pr_err("[%s] command numer [%d] exceeded!\n", __func__, _IOC_NR(cmd));return -ENOTTY;}    /* 检查访问模式 */if (_IOC_DIR(cmd) & _IOC_READ)ret= !access_ok(VERIFY_WRITE, (void __user *)arg, \_IOC_SIZE(cmd));else if (_IOC_DIR(cmd) & _IOC_WRITE)ret= !access_ok(VERIFY_READ, (void __user *)arg, \_IOC_SIZE(cmd));if (ret)return -EFAULT;switch(cmd) {/* 初始化设备 */case IOCINIT:init();break;/* 读寄存器 */case IOCGREG:ret = copy_from_user(&msg, \(struct msg __user *)arg, sizeof(my_msg));if (ret) return -EFAULT;msg->data = read_reg(msg->addr);ret = copy_to_user((struct msg __user *)arg, \&msg, sizeof(my_msg));if (ret) return -EFAULT;break;/* 写寄存器 */case IOCWREG:ret = copy_from_user(&msg, \(struct msg __user *)arg, sizeof(my_msg));if (ret) return -EFAULT;write_reg(msg->addr, msg->data);break;default:return -ENOTTY;}return 0;
}

3、ioctl-test.c,运行在用户空间的测试程序:

// ioctl-test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h> #include "ioctl-test.h"int main(int argc, char **argv)
{int fd;int ret;struct msg my_msg;fd = open("/dev/ioctl-test", O_RDWR);if (fd < 0) {perror("open");exit(-2);}/* 初始化设备 */ret = ioctl(fd, IOCINIT);if (ret) {perror("ioctl init:");exit(-3);}/* 往寄存器0x01写入数据0xef */memset(&my_msg, 0, sizeof(my_msg));my_msg.addr = 0x01;my_msg.data = 0xef;ret = ioctl(fd, IOCWREG, &my_msg);if (ret) {perror("ioctl read:");exit(-4);}/* 读寄存器0x01 */memset(&my_msg, 0, sizeof(my_msg));my_msg.addr = 0x01;ret = ioctl(fd, IOCGREG, &my_msg);if (ret) {perror("ioctl write");exit(-5);}printf("read: %#x\n", my_msg.data);return 0;
}

http://chatgpt.dhexx.cn/article/xrrwsa9u.shtml

相关文章

ioctl函数详解(参数详解,驱动unlocked_ioctl使用、命令码如何封装)

ioctl函数详解 一、ioctl函数的原型 在用户空间的函数原型 #include <sys/ioctl.h> int ioctl(int d, int request, ...); //io的控制&#xff0c;设备的控制/***第一个参数d是打开的文件描述符***//***The second argument is a device-dependent request code&…

IOCTL函数用法

.ioctl 的实现 一、ioctl的简介&#xff1a; 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数&#xff0c;但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动&#xff0c;想要一个弹出光驱的操作&#xff0c;这种操作并不是所有的…

IOCTL函数用法详解

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数 。所谓对I/O通道进行管理&#xff0c;就是对设备的一些特性进行控制&#xff0c;例如串口的传输波特率、马达的转速等等。它的调用个数如下&#xff1a; int ioctl(int fd, ind cmd, …)&#xff1b; 其中fd是用户程序打…

linux ioctl()详解

一、ioctl的简介&#xff1a; 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数&#xff0c;但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动&#xff0c;想要一个弹出光驱的操作&#xff0c;这种操作并不是所有的字符设备都需要的…

ioctl 详细介绍

ioctl 详细介绍 (一)ioctl 的作用: 通过设备驱动程序执行各种类型的硬件控制。除了简单数据传输外,大部分设备可以执行其他的一些操作,比如,用户空间经常请求设备锁门、弹出介质、报告错误信息、改变波特率或者执行自破坏等等。 Ioctl的操作通过流程图简言之: 从图…

ioctl函数

一、什么是ioctl   ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理&#xff0c;就是对设备的一些特性进行控制&#xff0c;例如串口的传输波特率、马达的转速等等。   ioctl函数是文件结构中的一个属性分量&#xff0c;就是说如果你的驱动程序…

linux驱动开发(四):ioctl()函数

前文中我们介绍了应用程序通过使用虚拟文件系统VFS提供的接口&#xff0c;来控制字符驱动程序&#xff0c;完成字符驱动设备的open、close、read、write操作。但是如果我们想进行除此以外的其他操作&#xff0c;拓展一些file_operations给出的接口中没有的自定义功能&#xff0…

linux 内核 - ioctl 函数详解

1. 概念 ioctl 是设备驱动程序中设备控制接口函数&#xff0c;一个字符设备驱动通常会实现设备打开、关闭、读、写等功能&#xff0c;在一些需要细分的情境下&#xff0c;如果需要扩展新的功能&#xff0c;通常以增设 ioctl() 命令的方式实现。 在文件 I/O 中&#xff0c;ioc…

第10章综合案例1广电大数据分析

第10章综合案例1广电大数据分析 实验目的及要求 &#xff08;1&#xff09;现有用户观看历史和用户信息两个广电大数据文件&#xff0c;将对用户数据进行大数据分析。 实验系统环境及版本 Linux Ubuntu 20.04 JDK1.8 Hadoop3.1.0 MySQL8.0.28 Hive3.1.2 实验任务 基本…

图解大数据 | 综合案例-使用Spark分析挖掘零售交易数据

作者&#xff1a;韩信子ShowMeAI 教程地址&#xff1a;http://www.showmeai.tech/tutorials/84 本文地址&#xff1a;http://www.showmeai.tech/article-detail/177 声明&#xff1a;版权所有&#xff0c;转载请联系平台与作者并注明出处 收藏ShowMeAI查看更多精彩内容 引言 …

大数据之实践案例分析

前言 公司由页游转手游&#xff0c;公司的数据分析需要针对手游进行设计&#xff0c;所以原来的那一套针对页游的数据分析框架就显得不是很合适了&#xff0c;一方面在于手游和页游一些业务逻辑上的不同&#xff0c;另外一方面是数据量级上的改变&#xff0c;以及渠道、区服之间…

大数据分析案例-基于朴素贝叶斯算法构建电信客户流失分析预警模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Python+大数据-数据分析与处理(六)-综合案例

Python大数据-数据分析与处理(六)-综合案例 案例一&#xff1a;Appstore数据分析 学习目标 掌握描述性数据分析流程 能够使用pandas、seaborn进行数据分析和可视化 1.案例介绍 案例背景&#xff1a; 对 App 下载和评分数据分析&#xff0c;帮助 App 开发者获取和留存用户…

大数据挖掘分析的经典案例,主要有哪几种?

大数据挖掘分析经典案例有以下几种&#xff1a; 1.预测产品未来一段时间用户是否会流失&#xff0c;流失情况怎么样&#xff1b; 2.公司做了某个促销活动&#xff0c;预估活动效果怎么样&#xff0c;用户接受度如何&#xff1b; 3.评估用户信用度好坏&#xff1b; 4.对现有…

第11章综合案例2影评大数据分析

第11章综合案例2影评大数据分析 实验目的及要求 &#xff08;1&#xff09;现有电影、影评和用户信息3个数据文件&#xff0c;将对其进行大数据分析。 实验系统环境及版本 Linux Ubuntu 20.04 JDK1.8 Hadoop3.1.0 MySQL8.0.28 Hive3.1.2 实验任务 评分次数最多的10部电…

淘宝大数据分析案例

项目介绍 本次结合的是一份淘宝大数据数据&#xff0c;数据集的大小共177MB&#xff0c;数据一共有3182261份&#xff08;三百多万份数据集&#xff09;&#xff0c;一般的软件是无法计算和分析的&#xff0c;比如Excel&#xff0c;MySQL&#xff0c;Python这些都无法较好的完…

数据分析综合案例

数据分析综合案例&#xff1a; 数据分析流程 什么是数据清洗&#xff1f; 简单来说&#xff0c;数据清洗就是把“脏数据”变为“干净的数据”。数据清洗虽然很繁琐&#xff0c;但也很重要。数据清洗流程&#xff1a; 数据的读写、数据的探索与描述、数据简单处理、重复值的处…

磁力搜索网站+下载神器放送2019-03-05

先介绍下背景,因为喜欢看的电影因为版权问题,不能用迅雷及百度云离线下载.今天找了好久,终于发现了一个好用的解决方案. 先介绍常用的磁力搜索网站: 搜索网 https://btsow.pw/tags https://cn.torrentkitty.tv https://www.ciliurl.com/ http://www.zhizhuc.com/ https://www.a…

几款磁力搜索引擎,找资料更方便

Bt177.info 一款强大的磁力搜索引擎网站&#xff0c;这款网站包含有7万多个磁力链接&#xff0c;提供提供网盘形式和磁力形式的储存&#xff0c;有很多你想要的东西。如果是音频和视频的话支持在线观看。 Bt977 磁力搜索引擎&#xff0c;支持网盘播放&#xff0c;磁力下载。 To…

搜索下载神器

前言 新闪存云app是一款功能非常强大的云盘软件&#xff0c;为用户提供了非常给力资源搜索功能&#xff0c;支持多种下载方式&#xff0c;让你在这里体验全网最快速的资源下载&#xff0c;多种格式的文件以及视频也都可以在这里进行下载并进行解析&#xff0c;操作十分的简单&a…