【Linux】ioctl分析
简介
一个字符设备驱动通常会实现常规的open
、release
、read
和write
接口,但是如果需要扩展新的功能,通常以ioctl
接口的方式实现。
用户空间的ioctl
#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...) ;
参数 | 描述 |
---|---|
fd | 文件描述符 |
cmd | 交互协议,设备驱动将根据cmd执行对应操作 |
… | 可变参数arg,依赖cmd指定的长度以及类型 |
返回值 | 执行成功时返回0,失败则返回-1 |
驱动中的ioctl
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
在新版内核中,unlocked_ioctl()与compat_ioctl()取代了ioctl()。unlocked_ioctl(),顾名思义,应该在无大内核锁(BKL)的情况下调用;compat_ioctl(),compat全称compatible(兼容的),主要目的是为64位系统提供32位ioctl的兼容方法,也是在无大内核锁的情况下调用。
在字符设备驱动开发中,一般情况下只要实现unlocked_ioctl()即可,因为在vfs层的代码是直接调用unlocked_ioctl()。
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命令访问模式(数据传输方向),占据2bit,可以为_IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
- type(device type),设备类型,占据8bit,在一些文献中翻译为“幻数”或者“魔数”,可以为任意char型字符,例如‘a’、‘b’、‘c’等等,其主要作用是使ioctl命令有唯一的设备标识。**tips:**Documentions/ioctl-number.txt记录了在内核中已经使用的“魔数”字符,为避免冲突,在自定义ioctl命令之前应该先查阅该文档。
- nr(number),命令编号/序数,占据8bit,可以为任意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)))
同时,内核还提供了反向解析ioctl命令的宏接口:
// include/uapi/asm-generic/ioctl.h/* used to decode ioctl numbers */
#define _IOC_DIR(cmd) (((cmd) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(cmd) (((cmd) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(cmd) (((cmd) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(cmd) (((cmd) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
示例
#ifndef __HAPTCIS_IOCTL_H__
#define __HAPTCIS_IOCTL_H__/** @Date: 2024-10-12 15:53:37* @LastEditors: zdk* @LastEditTime: 2024-10-14 11:47:49* @FilePath: \kernel\drivers\haptics\haptics_ioctl.h*/#include <linux/ioctl.h>
// #include <sys/ioctl.h> // 用户空间/*这里使用ioctl定义两个协议,读寄存器和写寄存器
*用户空间和内核空间共用的头文件,包含ioctl命令及相关宏定义,可以理解为一份“协议”文件
*/
//cmd中的type
#define DEVICE_TYPE 'H'
#define HAPTICS_READ_REG_NR (0)
#define HAPTICS_WRITE_REG_NR (1)
#define HAPTICS_READ_REG _IOR(DEVICE_TYPE,HAPTICS_READ_REG_NR,ioctl_protocol_t *)
#define HAPTCIS_WRITE_REG _IOW(DEVICE_TYPE,HAPTICS_WRITE_REG_NR,ioctl_protocol_t *)typedef struct
{uint8_t reg_addr;uint8_t reg_value;
}ioctl_protocol_t;#endif
这个头文件是共用的,因为他定义了协议的具体内容。
/** Silicon Integrated Co., Ltd haptic sih688x haptic driver file** Copyright (c) 2021 heater <daokuan.zhu@si-in.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include "haptics_ioctl.h"#define HAPTICS_MISC_DEV_NAME "haptics"//打开设备
static int haptics_open(struct inode* inode,struct file * filp)
{printk("%s\n",__FUNCTION__);return 0;
}//关闭设备
static int haptics_release(struct inode* inode ,struct file* filp)
{printk("%s\n",__FUNCTION__);return 0;
}//ioctl
static long haptics_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{int ret = 0;ioctl_protocol_t msg;//反解cmd中的字段int type = _IOC_TYPE(cmd);int dir = _IOC_DIR(cmd);int nr = _IOC_NR(cmd);int size = _IOC_SIZE(cmd);printk("dir=%d size=%d\n",dir,size);//检验cmd是否正确//1.校验cmd_typeif(DEVICE_TYPE != type){printk(KERN_ERR "cmd type error\n");ret =-1;return ret;}if(HAPTICS_READ_REG == cmd)//读寄存器{//校验nrif(HAPTICS_READ_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("read_reg:%#02x\n",msg.reg_addr);msg.reg_value=0xff;//这里模拟读寄存器的值//将读取到的值传给用户空间ret = copy_to_user((ioctl_protocol_t __user *)arg, &msg, sizeof(ioctl_protocol_t));}}else if(HAPTCIS_WRITE_REG == cmd)//写寄存器{//校验nrif(HAPTICS_WRITE_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("write_reg:%#02x=%#02x\n",msg.reg_addr,msg.reg_value);//模拟写寄存器的值}}else//{printk(KERN_ERR "unknown cmd\n");}return ret;
}static struct file_operations haptics_fops=
{.owner = THIS_MODULE,.open = haptics_open,.release = haptics_release,.unlocked_ioctl = haptics_ioctl,
};struct miscdevice mdev =
{.minor = MISC_DYNAMIC_MINOR,.name = HAPTICS_MISC_DEV_NAME,.fops = &haptics_fops,
};//定义一个杂项设备结构体static int __init haptics_init(void)
{int ret = 0;//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "%s\n",__FUNCTION__); //输出等级为0ret = misc_register(&mdev);if(0 == ret){printk(KERN_EMERG "misc_register ok minor=%d\n",mdev.minor);}return 0;
}static void __exit haptics_exit(void)
{misc_deregister(&mdev);printk(KERN_EMERG "%s\n",__FUNCTION__); //输出等级为0
}module_init(haptics_init);//驱动入口
module_exit(haptics_exit);//驱动出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptics Driver V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本
上面是驱动代码,负责解析ioctl
命令。
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "haptics_ioctl.h"
/*
argc:应用程序参数个数,包括应用程序本身
argv[]:具体的参数内容,字符串形式
./main <filename> <r:w> r表示读 w表示写
*/int main(int argc,char * argv[])
{ioctl_protocol_t msg;char* filename;int fd=0;if(argc!=3){printf("error usage\n");return -1;}filename=argv[1];fd = open(filename,O_RDWR);if(fd<0){printf("can not open file %s\n",filename);return -2;}if(0 == strcmp(argv[2],"r")){msg.reg_addr = 0x01;ioctl(fd,HAPTICS_READ_REG,&msg);printf("read %#02x=%#02x\n", msg.reg_addr,msg.reg_value);}else if(0 == strcmp(argv[2],"w")){msg.reg_addr = 0x01;msg.reg_value = 0xff;ioctl(fd,HAPTCIS_WRITE_REG,&msg);}else{printf("error usage\n");}close(fd);
}
上面是应用层代码,负责发送ioctl
消息。
注意:
_IOR
中第三个参数size是参数的大小,用于驱动中解析使用,但是如果是固定长度,这个参数也没有用到。理论上使用_IOR
、_IOW
或_IO
是没有严格区别的,所以我们上述代码中可以改为\#define HAPTICS_READ_REG _IO(DEVICE_TYPE,HAPTICS_READ_REG_NR)
ioctl
函数中第三个参数,表示传入驱动的实际参数,可以是一个整型值,也可以是一个结构体指针。我们示例中是传入的结构体指针。
相关文章:

【Linux】ioctl分析
简介 一个字符设备驱动通常会实现常规的open、release、read和write接口,但是如果需要扩展新的功能,通常以ioctl接口的方式实现。 #mermaid-svg-uY8EyPklf5e4ZMQo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill…...

物联网通信会给人们的生活带来什么样的变化
物联网(IoT)通信的崛起正以前所未有的速度改变着人们的生活方式。从智能家居、智能交通到远程医疗、工业自动化,物联网技术的应用已经渗透到我们日常生活的方方面面。以下是对物联网通信如何具体影响并改变人们生活的详细探讨。 一、智能家居…...
Android 中获取当前 CPU 频率和占用率
最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下: 目前没有标准的 API 来获取 CPU 的使用频率,只能通过读取指定 CPU 文件获取当前 CPU 频率,在某些机器或者特定版本中…...

pymobiledevice3使用介绍(安装、常用命令、访问iOS沙盒目录)
项目地址:https://github.com/doronz88/pymobiledevice3 首先先介绍一下pymobiledevice3, pymobiledevice3是用Python3 实现的,用于处理 iDevices(iPhone 等)。它可以跨平台使用,支持:windows…...
python 爬虫模拟登录
在使用 Python 编写爬虫时,模拟登录是一个非常常见的需求,尤其是当你需要爬取需要身份验证的数据时。模拟登录通常需要以下步骤: 分析登录页面:确定提交登录请求的 URL 和相关参数。发送登录请求:模拟用户发送登录表单…...

AOP基础、快速入门、进阶
一、概述 AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程 那什么又是面向方法编程呢,为什么又需要面向方法编程呢?来我们举个例子做一个说明: 比如…...

哪款宠物空净运行吸毛好、噪音小?希喂、霍尼韦尔、安德迈测评!
作为宠物领域目前最火热的产品,宠物空气净化器的讨论度一直很高。身为铲屎官的我在产品刚出的时候就购入了一台,结果让我非常失望! 抛开产品效果不提,它运行起来的声音实在太大了!我家猫根本不愿意靠近,每…...

新兴的安全职业挑战
我们经常与安全专业人士交谈,他们希望在努力提升职业发展的同时提高自己的价值并克服组织内部的挑战。在这些谈话中,花费大量时间讨论公司未来将面临的安全问题并不罕见。 安全领导者希望为问题制定计划并获得领导层对其计划的支持。这通常意味着实施修…...

代码随想录算法训练营Day32 | 122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组和
目录 122.买卖股票的最佳时机Ⅱ 55.跳跃游戏 45.跳跃游戏Ⅱ 1005.K次取反后最大化的数组和 122.买卖股票的最佳时机Ⅱ 题目 122. 买卖股票的最佳时机 II - 力扣(LeetCode) 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i…...

3D Slicer 教程一
先了解一下什么是3D Slicer,这个是做什么,然后一步步了解功能,一起看看源码 一.初识 这块软件用来处理医学影像,是一款开源的软件. 里面涉及到一些 2d 常见的操作,图像处理,调窗,测量; 涉及到3d的一些常见重建,mpr,vr,cpr等, 还包括一些分割,变换等(越高级精确的一些通过插件…...

github pages + hugo 搭建静态博客网站
体验地址 1. 起因, 目的: 其实6年前,我就写过这个。 项目代码 博客地址 最近想改写一下。 github 推荐的主题是 Jekyll, 我当时用的就是这个,感觉很麻烦。尤其是文章命名。 新的主题 hugo 用起来还行。 2.过程: 过程记录&am…...

Python爬虫如何爬取并解析JSON数据
前言 Python爬虫是一种用于从互联网上获取数据的程序,而JSON(JavaScript Object Notation)是一种常用的数据交换格式。本文将介绍如何使用Python爬虫来爬取并解析JSON数据,同时还会讲解如何使用代理IP来提高爬取效率。 1. 什么是…...

【C++】精妙的哈希算法
🚀个人主页:小羊 🚀所属专栏:C 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 一、哈希结构1、哈希概念2、哈希函数3、哈希冲突3.1 闭散列3.2 开散列 4、完整代码 一、哈希结构 1、哈希概念 A…...

智慧链动青春:国家区块链中心接待北京市十一学校青少年访学探索
以生动科学的方法点燃青少年科学探索欲望是构建未来科技人才梯队的基石。近期国家区块链技术创新中心接待北京市十一学校新生访学,以科普讲座、实操互动的方式让学生在深度思考中感受科学魅力、接触前沿科技,激发学生对区块链、隐私计算和芯片设计制造的…...

利用C++封装鼠标轨迹算法为DLL:游戏行为检测的利器
在现代软件开发中,鼠标轨迹模拟技术因其在自动化测试、游戏脚本编写等领域的广泛应用而备受青睐。本文将介绍如何使用C语言将鼠标轨迹算法封装为DLL(动态链接库),以便在多种编程环境中实现高效调用,同时探讨其在游戏行…...

Qt- QSS风格选择器常用属性选择器样式表盒子
1. 风格设置 Qt 提供了 3 种整体风格,使用 QStyleFactory::keys() 来获取 (windowsvista 、Windows 、Fusion) 可以在 main.cpp 中调用 setStyle 方法对应用程序进行全局风格的设置 int main(int argc, char *argv[]) {QApplication a(arg…...

粤智助自助一体机大厂浮出水面 OBOO鸥柏已成服务终端中坚力量
自助服务查询一体机作为操作自主化便民的重要窗口,OBOO鸥柏自助服务终端机以其显著的技术优化,通过触摸屏或其他交互界面,使用户能够自助服务完成各种操作,如支付、查询信息终端、办理业务,自助查档答应一体化等。为交…...
SpringBoot-application.properties配置
默认配置最终都是映射/关联到某个类 #SPRING CONFIG(ConfigFileApplicationListener) spring.config.name #配置文件名(默认 为 application ) spring.config.location #配置文件的位置 …...

STM32-ADC模数转换
一、概述 ADC(Analog-Digital Converter)模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁12位逐次逼近型ADC,1us转换时间输入电压范围:0~3.3Vÿ…...
lspci | grep VGA
执行lspci | grep VGA后如下,解释含义 00:0f.0 VGA compatible controller: VMware SVGA II Adapter 0b:00.0 VGA compatible controller: NVIDIA Corporation GA104 [GeForce RTX 3070] (rev a1) 执行 lspci | grep VGA 命令后,您得到了两条输出&#…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...