【Linux】设备驱动中的ioctl详解

在这里插入图片描述

在Linux设备驱动开发中,ioctl(输入输出控制)是一个非常重要的接口,用于用户空间应用程序与内核空间设备驱动之间进行通信。通过ioctl,应用程序可以发送命令给设备驱动,控制设备的行为或获取设备的状态信息。本文将详细介绍ioctl的基本原理、实现方法及其应用场景,并给出相应的示例代码。

1. ioctl概述

ioctl 是一个通用的系统调用,用于对打开的文件描述符执行各种控制操作。在Linux中,ioctl 主要有两个用途:

  1. 控制设备:应用程序可以通过ioctl发送命令给设备驱动,实现对设备的控制。
  2. 获取设备信息:应用程序可以通过ioctl从设备驱动获取设备的状态信息。

2. ioctl的基本原理

2.1 ioctl函数原型

ioctl 的函数原型如下:

#include <unistd.h>
int ioctl(int fd, unsigned long request, ...);
  • fd:文件描述符,通常通过open函数获得。
  • request:指定的控制命令,通常是一个宏定义。
  • ...:命令相关的参数,根据不同的命令可能需要传递不同的参数。
2.2 ioctl命令定义

在内核空间,每个ioctl命令都由一个宏定义来表示。这个宏定义通常包含命令的类型(读、写、读写)、命令号、数据类型和数据长度等信息。常用的宏定义包括:

#define _IOC(dir, type, nr, len) \(((dir)  << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr)   << _IOC_NRSHIFT) |  \((len)  << _IOC_SIZESHIFT))#define _IO(type, nr) _IOC(_IOC_NONE, (type), (nr), 0)
#define _IOR(type, nr, len) _IOC(_IOC_READ, (type), (nr), (len))
#define _IOW(type, nr, len) _IOC(_IOC_WRITE, (type), (nr), (len))
#define _IORW(type, nr, len) _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), (len))
  • _IO:用于没有参数的命令。
  • _IOR:用于从内核读取数据到用户空间。
  • _IOW:用于从用户空间写入数据到内核。
  • _IORW:用于读写操作。
2.3 ioctl的实现

在设备驱动中,需要实现一个unlocked_ioctl函数来处理来自用户空间的ioctl请求。通常情况下,还需要实现一个compat_ioctl函数来兼容32位和64位的用户空间。

static long my_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{// 实现ioctl处理逻辑
}static long my_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{// 实现兼容ioctl处理逻辑
}

3. ioctl的应用场景

3.1 控制设备

假设有一个简单的设备,用户空间应用程序可以通过ioctl来控制设备的开关。

3.2 获取设备信息

用户空间应用程序可以通过ioctl来获取设备的状态信息,如设备的工作模式、配置参数等。

4. 示例代码

下面是一个具体的示例,展示了如何在Linux设备驱动中实现ioctl接口。

4.1 定义设备结构
#define DEVICE_NAME_LEN 32
struct my_device {struct cdev cdev;struct class *class;struct device *device;dev_t devno;int state; // 设备状态
};
4.2 ioctl命令定义
#define MY_IOCTL_MAGIC 'M' // 自定义的命令类型// 开启设备
#define MY_IOCTL_OPEN _IO(MY_IOCTL_MAGIC, 0)// 关闭设备
#define MY_IOCTL_CLOSE _IO(MY_IOCTL_MAGIC, 1)// 获取设备状态
#define MY_IOCTL_GET_STATE _IOR(MY_IOCTL_MAGIC, 2, int)// 设置设备状态
#define MY_IOCTL_SET_STATE _IOW(MY_IOCTL_MAGIC, 3, int)
4.3 初始化模块
static int __init my_device_init(void)
{struct my_device *dev;int ret;dev = kzalloc(sizeof(struct my_device), GFP_KERNEL);if (!dev)return -ENOMEM;// 分配设备号alloc_chrdev_region(&dev->devno, 0, 1, "my_device");// 初始化字符设备dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_device_fops;cdev_init(&dev->cdev, &my_device_fops);ret = cdev_add(&dev->cdev, dev->devno, 1);if (ret)goto err_free_dev;// 创建设备类dev->class = class_create(THIS_MODULE, "my_device_class");if (IS_ERR(dev->class)) {ret = PTR_ERR(dev->class);goto err_free_cdev;}// 创建设备实例dev->device = device_create(dev->class, NULL, dev->devno, NULL, "my_device");if (IS_ERR(dev->device)) {ret = PTR_ERR(dev->device);goto err_free_class;}return 0;err_free_class:class_destroy(dev->class);
err_free_cdev:cdev_del(&dev->cdev);
err_free_dev:kfree(dev);return ret;
}module_init(my_device_init);
4.4 文件操作结构体
static const struct file_operations my_device_fops = {.owner       = THIS_MODULE,.open        = my_device_open,.release     = my_device_release,.unlocked_ioctl = my_device_ioctl,.compat_ioctl = my_device_compat_ioctl,
};static int my_device_open(struct inode *inode, struct file *file)
{// 实现设备打开逻辑return 0;
}static int my_device_release(struct inode *inode, struct file *file)
{// 实现设备关闭逻辑return 0;
}
4.5 实现ioctl处理函数
static long my_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct my_device *dev = filp->private_data;int ret = -EINVAL;switch (cmd) {case MY_IOCTL_OPEN:dev->state = 1; // 设备开启ret = 0;break;case MY_IOCTL_CLOSE:dev->state = 0; // 设备关闭ret = 0;break;case MY_IOCTL_GET_STATE:if (copy_to_user((int *)arg, &dev->state, sizeof(int))) {ret = -EFAULT;} else {ret = 0;}break;case MY_IOCTL_SET_STATE:if (copy_from_user(&dev->state, (int *)arg, sizeof(int))) {ret = -EFAULT;} else {ret = 0;}break;default:ret = -ENOTTY;break;}return ret;
}static long my_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct my_device *dev = filp->private_data;int ret = -EINVAL;union {int int_val;long long_val;} uval;switch (cmd) {case MY_IOCTL_GET_STATE:uval.int_val = dev->state;if (put_user(uval.long_val, (long *)arg)) {ret = -EFAULT;} else {ret = 0;}break;case MY_IOCTL_SET_STATE:if (get_user(uval.long_val, (long *)arg)) {ret = -EFAULT;} else {dev->state = uval.int_val;ret = 0;}break;default:ret = my_device_ioctl(filp, cmd, arg);break;}return ret;
}
4.6 清理模块
static void __exit my_device_exit(void)
{struct my_device *dev;// 获取设备结构dev = container_of(cdev, struct my_device, cdev);// 删除设备实例device_destroy(dev->class, dev->devno);// 销毁设备类class_destroy(dev->class);// 注销字符设备unregister_chrdev_region(dev->devno, 1);// 释放设备结构kfree(dev);
}module_exit(my_device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple device driver demonstrating ioctl usage.");

5. 用户空间示例

下面是一个简单的用户空间应用程序示例,展示了如何通过ioctl来控制设备。

5.1 用户空间程序
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#define MY_IOCTL_MAGIC 'M'#define MY_IOCTL_OPEN _IO(MY_IOCTL_MAGIC, 0)
#define MY_IOCTL_CLOSE _IO(MY_IOCTL_MAGIC, 1)
#define MY_IOCTL_GET_STATE _IOR(MY_IOCTL_MAGIC, 2, int)
#define MY_IOCTL_SET_STATE _IOW(MY_IOCTL_MAGIC, 3, int)int main()
{int fd;int state = 0;// 打开设备文件fd = open("/dev/my_device", O_RDWR);if (fd == -1) {perror("Failed to open device");return 1;}// 开启设备if (ioctl(fd, MY_IOCTL_OPEN) == -1) {perror("Failed to open device");close(fd);return 1;}// 设置设备状态state = 1;if (ioctl(fd, MY_IOCTL_SET_STATE, &state) == -1) {perror("Failed to set device state");close(fd);return 1;}// 获取设备状态if (ioctl(fd, MY_IOCTL_GET_STATE, &state) == -1) {perror("Failed to get device state");close(fd);return 1;}printf("Device state: %d\n", state);// 关闭设备if (ioctl(fd, MY_IOCTL_CLOSE) == -1) {perror("Failed to close device");close(fd);return 1;}// 关闭文件描述符close(fd);return 0;
}

6. 总结

ioctl 是Linux设备驱动开发中的重要接口之一,用于实现用户空间应用程序与内核空间设备驱动之间的通信。本文详细介绍了ioctl的基本原理、实现方法及其应用场景,并给出了相应的示例代码。希望上述内容能帮助读者更好地理解和掌握Linux设备驱动中的ioctl机制及其应用。在实际开发中,可以根据具体的需求选择合适的ioctl命令,并注意处理好用户空间与内核空间之间的数据传输。通过深入理解ioctl机制的底层原理,开发者可以更好地应对各种设备驱动开发中的挑战。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/67783.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

再次梳理ISP的大致流程

前言&#xff1a; 随着智能手机的普及&#xff0c;相机与我们的生活越来越紧密相关。在日常生活中&#xff0c;我们只需要轻轻按下手机上的拍照按钮&#xff0c;就能记录下美好时刻。那么问题来了&#xff1a;从我们指尖按下拍照按钮到一张色彩丰富的照片呈现在我们面前&#x…

基于R语言森林生态系统的结构、功能与稳定性

在生态学研究中&#xff0c;森林生态系统的结构、功能与稳定性是核心研究内容之一。这些方面不仅关系到森林动态变化和物种多样性&#xff0c;还直接影响森林提供的生态服务功能及其应对环境变化的能力。森林生态系统的结构主要包括物种组成、树种多样性、树木的空间分布与密度…

nacos学习笔记(一)

1.前言 何为nacos&#xff0c;nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。可以说集注册中心&#xff0c;配置中心&#xff0c;服务管理于一起的平台。注册中心&#xff1a;相当于我们可以把服务注册到注册中心上&#xff0c;我们以后可以通过服…

logback日志

一、使用两个以上spring环境变量做三目操作 <springProperty name"application_name" scope"context" source"spring.application.name"/><springProperty name"trace_app_name" scope"context" source"sprin…

mysql和redis的最大连接数

平时我们要评估mysql和redis的最大连接数&#xff0c;可以选择好环境&#xff08;比如4核8G&#xff09;,定好压测方法&#xff08;没有索引的mysql单表&#xff0c;redis单key&#xff09;进行压测&#xff0c;评估其最大并发量。 也可以查看各大云厂商的规格进行评估。 mys…

QEMU通过OVS实现联网

这篇笔记也是记录了一下自己的辛酸历程&#xff0c;仅供有需要的人参考。 首先关于qemu虚拟机的搭建&#xff0c;这不多赘述了&#xff0c;大家应该都会&#xff0c;这里可以给大家提供一个链接和一些命令。 QEMU搭建X86_64 Ubuntu虚拟系统环境https://blog.csdn.net/m0_531…

IT面试求职系列主题-Jenkins

想成功求职&#xff0c;必要的IT技能一样不能少&#xff0c;先说说Jenkins的必会知识吧。 1) 什么是Jenkins Jenkins 是一个用 Java 编写的开源持续集成工具。它跟踪版本控制系统&#xff0c;并在发生更改时启动和监视构建系统。 2&#xff09;Maven、Ant和Jenkins有什么区别…

(五)ROS通信编程——参数服务器

前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享&#xff08;P2P&#xff09;。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据&#xff0c;关…

攻防靶场(34):隐蔽的计划任务提权 Funbox1

目录 1. 侦查 1.1 收集目标网络信息&#xff1a;IP地址 1.2 主动扫描&#xff1a;扫描IP地址段 1.3 搜索目标网站 2. 初始访问 2.1 有效账户&#xff1a;默认账户 2.2 利用面向公众的应用 2.3 有效账户&#xff1a;默认账户 3. 权限提升 3.1 计划任务/作业&#xff1a;Cron 靶场…

嵌入式入门Day38

C Day1 第一个C程序C中的输入输出输出操作coutcin练习 命名空间使用方法自定义命名空间冲突问题 C对字符串的扩充C风格字符串的使用定义以及初始化C风格字符串与C风格字符串的转换C风格的字符串的关系运算常用的成员变量输入方法 布尔类型C对堆区空间使用的扩充作业 第一个C程序…

kubernetes第七天

1.影响pod调度的因素 nodeName 节点名 resources 资源限制 hostNetwork 宿主机网络 污点 污点容忍 Pod亲和性 Pod反亲和性 节点亲和性 2.污点 通常是作用于worker节点上&#xff0c;其可以影响pod的调度 语法&#xff1a;key[value]:effect effect:[ɪˈfek…

直流无刷电机控制(FOC):电流模式

目录 概述 1 系统框架结构 1.1 硬件模块介绍 1.2 硬件实物图 1.3 引脚接口定义 2 代码实现 2.1 软件架构 2.2 电流检测函数 3 电流环功能实现 3.1 代码实现 3.2 测试代码实现 4 测试 概述 本文主要介绍基于DengFOC的库函数&#xff0c;实现直流无刷电机控制&#x…

Google发布图像生成新工具Whisk:无需复杂提示词,使用图像和人工智能将想法可视化并重新混合

Whisk 是 Google Labs 的一项新实验&#xff0c;可使用图像进行快速而有趣的创作过程。Whisk不会生成带有长篇详细文本提示的图像&#xff0c;而是使用图像进行提示。只需拖入图像&#xff0c;即可开始创建。 whisk总结如下&#xff1a; Whisk 是 Google 实验室最新的生成图像实…

【面试题】技术场景 4、负责项目时遇到的棘手问题及解决方法

工作经验一年以上程序员必问问题 面试题概述 问题为在负责项目时遇到的棘手问题及解决方法&#xff0c;主要考察开发经验与技术水平&#xff0c;回答不佳会影响面试印象。提供四个回答方向&#xff0c;准备其中一个方向即可。 1、设计模式应用方向 以登录为例&#xff0c;未…

MySQL 子查询(重在练习)

第九章: 子查询 1.子查询的需求分析和问题解决 1.1基本使用 子查询(内查询)在主查询之前一次执行完成 子查询的结果被主查询(外查询)调用 注意事项 子查询要包含在括号内 将子查询放在比较条件的右侧 单行操作符对应单行子查询,多行操作符对应多行子查询 1.2子查询的分类…

海外招聘丨卡尔斯塔德大学—互联网隐私和安全副高级讲师

雇主简介 卡尔斯塔德大学以研究、教育和合作为基础。通过让社区参与知识发展&#xff0c;卡尔斯塔德大学为地区、国家和国际研究和教育发展做出了贡献&#xff0c;旨在提高可持续性、民主和健康。我们富有创造力的学术环境以好奇心、勇气和毅力为特征。通过采取批判性方法&…

CTFshow—文件包含

Web78-81 Web78 这题是最基础的文件包含&#xff0c;直接?fileflag.php是不行的&#xff0c;不知道为啥&#xff0c;直接用下面我们之前在命令执行讲过的payload即可。 ?filephp://filter/readconvert.base64-encode/resourceflag.php Web79 这题是过滤了php&#xff0c;…

iOS实际开发中使用Alamofire实现多文件上传(以个人相册为例)

引言 在移动应用中&#xff0c;图片上传是一个常见的功能&#xff0c;尤其是在个人中心或社交平台场景中&#xff0c;用户经常需要上传图片到服务器&#xff0c;用以展示个人风采或记录美好瞬间。然而&#xff0c;实现多图片上传的过程中&#xff0c;如何设计高效的上传逻辑并…

Cannot run program “docker“: CreateProcess error=2,系统找不到指定的文件

今天被这个问题坑了, 网上教程全是直接装插件就行 ,结果我连接可以成功 但是执行docker compose 就会出错, 检测配置 报错com.intellil,execution,process.ProcessNotCreatedException: Cannot run program “docker”: CreateProcess error2,系统找不到指定的文件 gpt 要我去…

Blender 2D动画与MATLAB数学建模:跨界融合的创新实践探索

文章目录 一、数据交换&#xff1a;从科学计算到创意表达的桥梁二、脚本自动化&#xff1a;从手动操作到无缝连接的升级三、跨界融合的创新实践意义《Blender 2D动画制作从入门到精通》亮点内容简介作者简介 《MATLAB数学建模从入门到精通》亮点内容简介作者简介 在数字创意与科…