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

article/2025/11/8 7:22:51

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

一、应用程序中的ioctl接口

首先,我们需要规定一些命令码,这些命令码在应用程序和驱动程序中需要保持一致。应用程序只需向驱动程序下发一条指令码,用来通知它执行哪条命令。如何解读这条指令和怎么实现相关操作,就是驱动程序自己要做的事。

应用程序的接口函数为ioctl,参考官方文档,函数原型为

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);

下面我们解释各个参数的含义。
1)fd是文件描述符。当我们的设备作为特殊文件被open()函数打开后,会返回一个文件描述符,通过操作这个文件描述符达到操作设备文件的目的。

2)request是命令码,应用程序通过下发命令码来控制驱动程序完成对应操作。

3)第三个参数“…”是可变参数arg,一些情况下应用程序需要向驱动程序传参,参数就通过ag来传递。ioctl函数中的“…”只能传递一个参数,但内核不会检查这个参数的类型。那么,就有两种传参方式:只传一个整数,传递一个指针。

如果ioctl执行成功,它的返回值就是驱动程序中ioctl接口给的返回值,驱动程序可以通过返回值向用户程序传参。但驱动程序最好返回一个非负数,因为用户程序中的ioctl运行失败时一定会返回-1并设置全局变量errorno。

errono不同的值代表的含义如下:

EBADF:fd是一个无效的文件描述符。
EFAULT:在arg是指针的前提下,argp指向一个不可访问的内存空间。
EINVAL:request或argp是无效的。
ENOTTY:fd没有关联到一个字符特殊设备,或该request不适用于文件描述符fd引用的对象类型。(说人话就是fd没有指向一个字符设备,或fd指向的文件不支持ioctl操作)

因此,在用户空间调用ioctl时,可以使用如下的错误判断处理。包括的两个头文件,string.h声明了strerror函数,errno.h定义了错误码errno。

#include <string.h>
#include <errno.h>int ret;
ret = ioctl(fd, MYCMD);
if (ret == -1)printf("ioctl: %s\n", strerror(errno));

二、驱动程序中的ioctl接口

在驱动程序的ioctl函数体中,实现了一个switch-case结构,每一个case对应一个命令码,case内部是驱动程序实现该命令的相关操作。

ioctl的实现函数要传递给file_operations结构体中对应的函数指针,函数原型为

#include <linux/ioctl.h>
long (*unlocked_ioctl) (struct file * fp, unsigned int request, unsigned long args);
long (*compat_ioctl) (struct file * fp, unsigned int request, unsigned long args);

unlocked_ioctl在无大内核锁(BKL)的情况下调用。64位用户程序运行在64位的kernel,或32位的用户程序运行在32位的kernel上,都是调用unlocked_ioctl函数。

compat_ioctl是64位系统提供32位ioctl的兼容方法,也在无大内核锁的情况下调用。即如果是32位的用户程序调用64位的kernel,则会调用compat_ioctl。如果驱动程序没有实现compat_ioctl,则用户程序在执行ioctl时会返回错误Not a typewriter。

另外,如果32位用户态和64位内核态发生交互时,第三个参数的长度需要保持一致,否则交互协议会出错。

int (*ioctl) (struct inode *inode, struct file *fp, unsigned int request, unsigned long args);

在2.6.35.7及以前的内核版本中,file_operations还定义了ioctl()接口,与unlocked_ioctl是等价的。但是在2.6.36以后就不再支持这个接口,全部使用unlocked_ioctl了。

以上函数参数的含义如下。
1)inode和fp用来确定被操作的设备。
2)request就是用户程序下发的命令。
3)args就是用户程序在必要时传递的参数。

返回值:可以在函数体中随意定义返回值,这个返回值也会被直接返回到用户程序中。通常使用非负数表示正确的返回,而返回一个负数系统会判定为ioctl调用失败。

三、用户与驱动之间的ioctl协议构成

也就是request或cmd,本质上就是一个32位数字,理论上可以是任何一个数,但为了保证命令码的唯一性,linux定义了一套严格的规定,通过计算得到这个命令吗数字。linux将32位划分为四段,如下图。

含义如下。

1)dir,即direction,表示ioctl命令的访问模式,分为无数据(_IO)、读数据(_IOR)、写数据(_IOW)、读写数据(_IOWR)四种模式。

2)type,即device type,表示设备类型,也可翻译成“幻数”或“魔数”,可以是任意一个char型字符,如’a’、‘b’、‘c’等,其主要作用是使ioctl命令具有唯一的设备标识。不过在内核中’w’、‘y’、'z’三个字符已经被使用了。

3)nr,即number,命令编号/序数,取值范围0~255,在定义了多个ioctl命令的时候,通常从0开始顺次往下编号。

4)size,涉及到ioctl的参数arg,占据13bit或14bit,这个与体系有关,arm使用14bit。用来传递arg的数据类型的长度,比如如果arg是int型,我们就将这个参数填入int,系统会检查数据类型和长度的正确性。

在上面的四个参数都需要用户自己定义,linux系统提供了宏可以使程序员方便的定义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)))

分别对应了四个dir:
_IO(type, nr):用来定义不带参数的ioctl命令。
_IOR(type,nr,size):用来定义用户程序向驱动程序写参数的ioctl命令。
_IOW(type,nr,size):用来定义用户程序从驱动程序读参数的ioctl命令。
_IOWR(type,nr,size):用来定义带读写参数的驱动命令。

当然了,系统也定义反向解析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

四、ioctl使用的简单实例——整数传参

本例中,我们让ioctl传递三个命令,分别是一个无参数、写参数、读参数三个指令。首先我们需要确定两个头文件,命名为ioctl_test.h和user_ioctl.h,用来分别定义内核空间和用户空间下的命令码协议。两个头文件中除了引用不同的头文件外,其他内容需要完全一致,以保证协议的一致性。

我们使用字符’a’作为幻数,三个命令的作用分别是用户程序让驱动程序打印一句话,用户程序从驱动程序读一个int型数,用户程序向驱动程序写一个int型数。

ioctl_test.h

#include <linux/ioctl.h>#define CMD_IOC_MAGIC	'a'
#define CMD_IOC_0		_IO(CMD_IOC_MAGIC, 0)
#define CMD_IOC_1		_IOR(CMD_IOC_MAGIC, 1, int)
#define CMD_IOC_2		_IOW(CMD_IOC_MAGIC, 2, int)

user_ioctl.h

#include <sys/ioctl.h>#define CMD_IOC_MAGIC	'a'
#define CMD_IOC_0		_IO(CMD_IOC_MAGIC, 0)
#define CMD_IOC_1		_IOR(CMD_IOC_MAGIC, 1, int)
#define CMD_IOC_2		_IOW(CMD_IOC_MAGIC, 2, int)

编写驱动程序ioctl_test.c。核心函数是demo_ioctl,使用一个switch-case完成用户程序下发的指令。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include "ioctl_test.h"MODULE_LICENSE("GPL");
MODULE_AUTHOR("zz");static dev_t devno;static int demo_open(struct inode *ind, struct file *fp)
{printk("demo open\n");return 0;
}static int demo_release(struct inode *ind, struct file *fp)
{printk("demo release\n");return 0;
}static long demo_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{int rc = 0;int arg_w;const int arg_r = 566;if (_IOC_TYPE(cmd) != CMD_IOC_MAGIC) {pr_err("%s: command type [%c] error.\n", __func__, _IOC_TYPE(cmd));return -ENOTTY;}switch(cmd) {case CMD_IOC_0:printk("cmd 0: no argument.\n");rc = 0;break;case CMD_IOC_1:printk("cmd 1: ioc read, arg = %d.\n", arg_r);arg = arg_r;rc = 1;break;case CMD_IOC_2:arg_w = arg;printk("cmd 2: ioc write, arg = %d.\n", arg_w);rc = 2;break;default:pr_err("%s: invalid command.\n", __func__);return -ENOTTY;}return rc;
}static struct file_operations fops = {.open = demo_open,.release = demo_release,.unlocked_ioctl = demo_ioctl,
};static struct cdev cd;static int demo_init(void)
{int rc;rc = alloc_chrdev_region(&devno, 0, 1, "test");if(rc < 0) {pr_err("alloc_chrdev_region failed!");return rc;}printk("MAJOR is %d\n", MAJOR(devno));printk("MINOR is %d\n", MINOR(devno));cdev_init(&cd, &fops);rc = cdev_add(&cd, devno, 1);if (rc < 0) {pr_err("cdev_add failed!");return rc;}return 0;
}static void demo_exit(void)
{cdev_del(&cd);unregister_chrdev_region(devno, 1);return;
}module_init(demo_init);
module_exit(demo_exit);

编写用户程序user_ioctl.c,主要作用就是打开设备节点,依次下发三条指令,打印参数和ioctl的返回值,关闭设备节点。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "user_ioctl.h"int main()
{int rc;int arg_r;const int arg_w = 233;int fd = open("/dev/test_chr_dev", O_RDWR);if (fd < 0) {printf("open file failed!\n");return -1;}rc = ioctl(fd, CMD_IOC_0);printf("rc = %d.\n", rc);rc = ioctl(fd, CMD_IOC_1, arg_r);printf("ioc read arg = %d, rc = %d.\n", arg_r, rc);rc = ioctl(fd, CMD_IOC_2, arg_w);printf("ioc write arg = %d, rc = %d.\n", arg_w, rc);close(fd);return 0;
}

编写Makefile。

ifneq ($(KERNELRELEASE),)obj-m := ioctl_test.o
elseKDIR    := /lib/modules/$(shell uname -r)/buildPWD     := $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modulesgcc user_ioctl.c -o user
clean:make -C $(KDIR) M=$(PWD) cleanrm -rf user
endif

运行结果。首先make编译。

进入root模式,安装模块,打印内核信息查看系统分配给设备的设备号。

可以看到系统分配了236给该模块。创建设备节点,设备节点的名称为/dev/test_chr_dev,与user_ioctl.c中的保持一致。

运行用户程序,打印结果如下。

打印一下内核输出信息,结果如下。

可以看到结果是正确的。删除设备节点,移除模块。


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

相关文章

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…

基于python的种子搜索网站,你懂得!

该项目是基于python的web类库django开发的一套web网站&#xff0c;给师弟做的毕业设计。本人的研究方向是一项关于搜索的研究项目。在该项目中&#xff0c;笔者开发了一个简单版的搜索网站&#xff0c;实现了对数据库数据的检索和更新。通过开发该项目&#xff0c;笔者学习和巩…

android下载工具 磁力,【安卓+iOS】磁力搜索+下载工具

【安卓iOS】磁力搜索下载工具 2020-03-29 19:46:20 3点赞 16收藏 2评论 1、比特舟Pro(安卓) 比特舟Pro是一款磁力搜索工具&#xff0c;支持BT和磁力搜索。前身是比特羊&#xff0c;后来改名比特知了&#xff0c;现在又一此改名。名字虽然变了&#xff0c;功能还是一样。 想搜什…

Ubuntu 能直接搜 BT 种子了

Ubuntu的Dash搜索工具允许用户搜索本地和在线资源如亚马逊和维基百科上的内容。现在&#xff0c;一位第三方开发者为Dash搜索工具加入了BT搜索功能&#xff0c;允许用户搜索海盗湾上的torrent文件。这项功能获得了 Canonical 创始人Mark Shuttleworth的支持。 Canonical表示&am…

利用Python爬虫建立自己的磁力搜索引擎

现在磁力站很多,但是搜出来的东西乱七八糟的,广告也多,我看多了觉得挺烦的,正好周末无聊,想着自己做一个,下面附上本次利用Python爬虫磁力站点的教程。 下面是我写爬虫时候主要引用的库 当然,抓取的关键词可以自己从代码里设置,比如title,文件大小之类的。 但我怎么知…

TT盒子种子搜索神器

2、软件名称&#xff1a;tt盒子种子搜索神器 3、软件版本&#xff1a;V1.5 4、软件大小&#xff1a;2.31MB 5、软件作者及网址&#xff1a;TT盒子 网站&#xff1a;www.tthezi.com 6、软件类别&#xff1a;搜索引擎 7、软件语言&#xff1a;简体中文 8、软件授权&#xff1a;免…

想做个磁力链搜索引擎 3

上一篇中&#xff0c;我们已经实现了对tracker的访问&#xff0c;从而获取到了peer对等体的ip地址以及端口号。我们这一篇要实现的是对等体之间的通讯。 在bt种子下载中&#xff0c;对等体就是正在下载你需要文件的另一台主机或提供下载你需要文件的主机。每一个种子一开始都是…

[搜片神器]直接从DHT网络下载BT种子的方法

转自:http://www.cnblogs.com/miao31/p/3332819.html DHT抓取程序开源地址:https://github.com/h31h31/H31DHTDEMO 数据处理程序开源地址:https://github.com/h31h31/H31DHTMgr DHT系列文章: 1.[搜片神器] 之P2P中DHT网络爬虫原理 2.[搜片神器]之DHT网络爬虫的代码实现方法…