Linux内核模块开发 第 9 章 谈及设备文件

news2025/1/17 3:04:54

The Linux Kernel Module Programming Guide

  • Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang
  • 译 断水客(WaterCutter
  • 源 LKMPG
    在这里插入图片描述

9 谈及设备文件

设备文件被用于表示物理设备。多数物理设备既被用于输入,也被用作输出,所以有一些机制用于支持内核从进程获取输出,然后传送给设备。

上述需求可以通过打开设备文件并向该文件写入数据实现,这和写入普通文件没有太多区别。下面的例子中,我们使用 device_weite 函数来做这件事。

device_weite 并不能满足我们的所有需求。设想我们通过串口连接一个 modem(即使这是一个内置的modem,从 CPU 的角度看,仍是通过串口连接的,所以这种设想并不费力)。一般情况下(the natural thing to do),使用设备文件向 modem 写数据或指令,读指令的应答或者收到的数据。但问题是,当你需要直接与串口通信——比如配置串口发送和接受数据的速率该怎么办呢?

答案是 Unix 使用一种被称作 ioctl(Inout and Output Control) 的特殊函数。每个设备都有自己的 ioctl 函数,这些命令可以读取 ioctl 命令(将信息从进程发送到内核),写入 ioctl 命令(将信息返回到进程),两者兼而有之,或者两者都没有。请注意,这里读取和写入的角色再次颠倒,在 ioctl 中,读取是向内核发送信息,写入是从内核接收信息。

ioctl 函数有三个参数:

  • 对应设备文件的文件描述符
  • ioctl 编号
  • ioctl 参数,该参数为 long 类型,因此您可以使用强制转换使用它来传递任何内容。您将无法以这种方式传递结构,但可以传递指向该结构的指针,下面是一个示例:
/* 
 * ioctl.c 
 */ 
#include <linux/cdev.h> 
#include <linux/fs.h> 
#include <linux/init.h> 
#include <linux/ioctl.h> 
#include <linux/module.h> 
#include <linux/slab.h> 
#include <linux/uaccess.h> 
 
struct ioctl_arg { 
    unsigned int val; 
}; 
 
/* Documentation/ioctl/ioctl-number.txt */ 
#define IOC_MAGIC '\x66' 
 
#define IOCTL_VALSET _IOW(IOC_MAGIC, 0, struct ioctl_arg) 
#define IOCTL_VALGET _IOR(IOC_MAGIC, 1, struct ioctl_arg) 
#define IOCTL_VALGET_NUM _IOR(IOC_MAGIC, 2, int) 
#define IOCTL_VALSET_NUM _IOW(IOC_MAGIC, 3, int) 
 
#define IOCTL_VAL_MAXNR 3 
#define DRIVER_NAME "ioctltest" 
 
static unsigned int test_ioctl_major = 0; 
static unsigned int num_of_dev = 1; 
static struct cdev test_ioctl_cdev; 
static int ioctl_num = 0; 
 
struct test_ioctl_data { 
    unsigned char val; 
    rwlock_t lock; 
}; 
 
static long test_ioctl_ioctl(struct file *filp, unsigned int cmd, 
                             unsigned long arg) 
{ 
    struct test_ioctl_data *ioctl_data = filp->private_data; 
    int retval = 0; 
    unsigned char val; 
    struct ioctl_arg data; 
    memset(&data, 0, sizeof(data)); 
 
    switch (cmd) { 
    case IOCTL_VALSET: 
        if (copy_from_user(&data, (int __user *)arg, sizeof(data))) { 
            retval = -EFAULT; 
            goto done; 
        } 
 
        pr_alert("IOCTL set val:%x .\n", data.val); 
        write_lock(&ioctl_data->lock); 
        ioctl_data->val = data.val; 
        write_unlock(&ioctl_data->lock); 
        break; 
 
    case IOCTL_VALGET: 
        read_lock(&ioctl_data->lock); 
        val = ioctl_data->val; 
        read_unlock(&ioctl_data->lock); 
        data.val = val; 
 
        if (copy_to_user((int __user *)arg, &data, sizeof(data))) { 
            retval = -EFAULT; 
            goto done; 
        } 
 
        break; 
 
    case IOCTL_VALGET_NUM: 
        retval = __put_user(ioctl_num, (int __user *)arg); 
        break; 
 
    case IOCTL_VALSET_NUM: 
        ioctl_num = arg; 
        break; 
 
    default: 
        retval = -ENOTTY; 
    } 
 
done: 
    return retval; 
} 
 
static ssize_t test_ioctl_read(struct file *filp, char __user *buf, 
                               size_t count, loff_t *f_pos) 
{ 
    struct test_ioctl_data *ioctl_data = filp->private_data; 
    unsigned char val; 
    int retval; 
    int i = 0; 
 
    read_lock(&ioctl_data->lock); 
    val = ioctl_data->val; 
    read_unlock(&ioctl_data->lock); 
 
    for (; i < count; i++) { 
        if (copy_to_user(&buf[i], &val, 1)) { 
            retval = -EFAULT; 
            goto out; 
        } 
    } 
 
    retval = count; 
out: 
    return retval; 
} 
 
static int test_ioctl_close(struct inode *inode, struct file *filp) 
{ 
    pr_alert("%s call.\n", __func__); 
 
    if (filp->private_data) { 
        kfree(filp->private_data); 
        filp->private_data = NULL; 
    } 
 
    return 0; 
} 
 
static int test_ioctl_open(struct inode *inode, struct file *filp) 
{ 
    struct test_ioctl_data *ioctl_data; 
 
    pr_alert("%s call.\n", __func__); 
    ioctl_data = kmalloc(sizeof(struct test_ioctl_data), GFP_KERNEL); 
 
    if (ioctl_data == NULL) 
        return -ENOMEM; 
 
    rwlock_init(&ioctl_data->lock); 
    ioctl_data->val = 0xFF; 
    filp->private_data = ioctl_data; 
 
    return 0; 
} 
 
static struct file_operations fops = { 
    .owner = THIS_MODULE, 
    .open = test_ioctl_open, 
    .release = test_ioctl_close, 
    .read = test_ioctl_read, 
    .unlocked_ioctl = test_ioctl_ioctl, 
}; 
 
static int __init ioctl_init(void) 
{ 
    dev_t dev; 
    int alloc_ret = -1; 
    int cdev_ret = -1; 
    alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME); 
 
    if (alloc_ret) 
        goto error; 
 
    test_ioctl_major = MAJOR(dev); 
    cdev_init(&test_ioctl_cdev, &fops); 
    cdev_ret = cdev_add(&test_ioctl_cdev, dev, num_of_dev); 
 
    if (cdev_ret) 
        goto error; 
 
    pr_alert("%s driver(major: %d) installed.\n", DRIVER_NAME, 
             test_ioctl_major); 
    return 0; 
error: 
    if (cdev_ret == 0) 
        cdev_del(&test_ioctl_cdev); 
    if (alloc_ret == 0) 
        unregister_chrdev_region(dev, num_of_dev); 
    return -1; 
} 
 
static void __exit ioctl_exit(void) 
{ 
    dev_t dev = MKDEV(test_ioctl_major, 0); 
 
    cdev_del(&test_ioctl_cdev); 
    unregister_chrdev_region(dev, num_of_dev); 
    pr_alert("%s driver removed.\n", DRIVER_NAME); 
} 
 
module_init(ioctl_init); 
module_exit(ioctl_exit); 
 
MODULE_LICENSE("GPL"); 
MODULE_DESCRIPTION("This is test_ioctl module");

You can see there is an argument called cmd in test_ioctl_ioctl() function. It is the ioctl number. The ioctl number encodes the major device number, the type of the ioctl, the command, and the type of the parameter. This ioctl number is usually created by a macro call ( _IO , _IOR , _IOW or _IOWR — depending on the type) in a header file. This header file should then be included both by the programs which will use ioctl (so they can generate the appropriate ioctl’s) and by the kernel module (so it can understand it). In the example below, the header file is chardev.h and the program which uses it is userspace_ioctl.c.

如果你想在自己的内核模块中使用 ioctl,最好收到一个正式的 ioctl 分配,所以如果你不小心得到了别人的 ioctl,或者如果他们得到了你的,你就会知道出了问题。有关更多信息,请参阅内核源代码树 Documentation/userspace-api/ioctl/ioctl-number.rst

此外,我们需要注意,对共享资源的并发访问将导致争用条件。解决方案是使用我们在 6.5 节中提到的原子比较和交换 (Atomic Compare-And-Swap) 来强制执行独占访问( atomic Compare-And-Swap )。

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

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

相关文章

【使用机器学习和深度学习对城市声音进行分类】基于两种技术(ML和DL)对音频数据(城市声音)进行分类(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

OpenCV开发实战 --(C++/Python) 进行视频中的简单背景估计-附源码

在许多计算机视觉应用中,您可以使用的处理能力较低。在这种情况下,我们必须使用简单但有效的技术。 在这篇文章中,我们将介绍一种这样的技术,用于在相机静态且场景中存在一些移动物体时估计场景的背景。这种情况并不少见。例如,许多交通和监控摄像头都是固定固定的。 时间…

Python爬虫——urllib_请求对象定制

UA介绍&#xff1a; User Agent中文名为用户代理&#xff0c;j简称UA&#xff0c;它是一个特殊字符串头&#xff0c;使得服务器能够识别客户使用的操作系统及版本&#xff0c;cpu类型&#xff0c;浏览器及版本&#xff0c;浏览器内核&#xff0c;浏览器渲染引擎&#xff0c;浏览…

【编译之美】【4. 代码优化:为什么你的代码比他的更高效】

什么是代码优化 代码优化是编译器后端的两大工作之一&#xff0c;弄懂它&#xff0c;你就掌握了一大块后端技术。 代码优化的目标 代码优化的目标&#xff0c;是优化程序对计算机资源的使用。 代码优化的对象 大多数的代码优化都是在 IR 上做的&#xff0c;而不是在前一阶段…

wvp-GB28181-pro /ZLMediaKit 部署学习

前言&#xff1a; 请认真阅读作者提供的文档&#xff01;认真阅读&#xff01;认真阅读&#xff01;认真阅读&#xff01; 背景&#xff1a; 公司项目需要对接不同地方的监控设备&#xff08;海康、大华、火星等等未知设备&#xff09;。由于本人对于海康设备不太了解出现一大…

CAD Exchanger SDK 3.20.0 for Linux Crack

引入新格式 Autodesk Revit&#xff0c;支持 BIM 特定数据模型以及 CAD Exchanger 3.19.0 中的原生格式 CDXBIM 独立于 Revit 软件读取 RVT 文件&#xff0c;使用 BIM 特定数据模型探索 BIM 属性&#xff0c;并利用我们专有的 BIM 友好 CDXBIM 格式更快地加载大型模型。 BIM数…

运维自动化前三阶段

运维自动化前三阶段 纯手工阶段&#xff1a;手工操作重复地进行软件部署和运维&#xff1b; 脚本阶段&#xff1a;通过编写脚本、方便地进行软件部署和运维&#xff1b; 工具阶段&#xff1a;借助第三方工具高效、方便地进行软件部署和运维。 这几个阶段是随着运维知识、经验…

tcn介绍

tcn 介绍 背景 对于大多数深度学习从业者来说&#xff0c;序列建模是递归网络的代名词。 然而&#xff0c;最近的结果表明&#xff0c;卷积架构在音频合成和机器翻译等任务上可以优于递归网络。给定一个新的序列建模任务或数据集&#xff0c;应该使用哪种架构&#xff1f;我…

Apple visionOS UI设计规范

果发布首款 MR 设备 Vision Pro&#xff0c;正在倡导用于Vision Pro在真实空间中显示应用程序窗口和内容“空间用户界面设计”&#xff0c;苹果的设计团队解释了开发人员应该知道的空间用户界面设计的原则。visionOSUI设计规范 支持&#xff1a;sketch Vision Pro中安装的visio…

无线电音频-BPA600蓝牙协议分析仪名词解析

1 介绍 2 Baseband基带分析 (1)Delta 是什么含义? "Delta" 有多个含义,取决于上下文。以下是常见的几种含义: 希腊字母:Delta&#x

“layui助力博客管理升级!用增删改查功能打造优质博客体验“

目录 引文1.前置条件2.数据接口2.1 UserDao(CRUD)2.2 R工具类 3.HTML 结构3.1 主界面的HTML3.2 用户的查询所有界面的HTML3.3 新增修改通用的的HTML 4.JavaScript 代码4.1 用户的CRUD javaScript 代码(userManage)4.2 新增修改的javaScript代码(userEdit) 5. 运行截图总结 引文…

【SpringBoot构建Hippo4j-Client安装部署教程】

&#x1f680; 线程池管理工具-Hippo4j &#x1f680; &#x1f332; AI工具、AI绘图、AI专栏 &#x1f340; &#x1f332; 如果你想学到最前沿、最火爆的技术&#xff0c;赶快加入吧✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域优质创作者&#…

uniapp和uview组件实现下拉触底刷新列表

下面是一个在UniApp中使用uView组件实现下拉触底刷新列表的示例&#xff0c;并使用Axios来请求分页数据列表&#xff1a; 首先&#xff0c;确保你已经在UniApp项目中添加了uView组件库。你可以在项目根目录执行以下命令安装它们&#xff1a; npm install uview-ui或者使用 Hb…

一文速览大语言模型在分子领域中的探索

随着 ChatGPT 的快速崛起&#xff0c;大型语言模型&#xff08;LLM&#xff09;已经在人类语言建模领域展示出了其非凡的能力。无论是证明数学公式、编写代码&#xff0c;还是以不同的风格创作诗歌&#xff0c;LLM 都能胜任。然而&#xff0c;尽管 LLM 在人类语言的掌握上已达到…

1、网络基础

网络发展背景、IP、端口、网络通信协议&#xff0c;TCP/IP五层模型、字节序 一、网络发展背景 局域网&#xff1a;网络覆盖在1000m以内的网络 城域网&#xff1a;网络覆盖在20km以内的网络 广域网&#xff1a;网络覆盖在20km以上的网络 因特网、互联网&#xff1a;更大的国…

SSH跳转远程目标服务器的高阶使用

在日常开发和运维的过程中&#xff0c;我一般是使用Xshell的工具对linux服务器的相关操作。我说一下我写这篇文章的背景&#xff1a;甲方因为安全需要&#xff0c;给了一台可以通过vpn访问的跳板机&#xff0c;通过这台跳板机去操作另外的十多台应用服务器&#xff0c;那么肯定…

MySQL 8目录结构与源码

目录 一、主要目录结构 二、获取MySQL 源代码 一、主要目录结构 MySQL的主要目录结构说明bin目录所有MySQL的可执行文件。如&#xff1a;mysql.exedata目录系统数据库所在的目录my.ini文件MySQL的主要配置文件D:\MySQL\Data用户创建的数据库所在的目录 二、获取MySQL 源代码 …

【OpenCV • c++】图像几何变换 | 图像平移

&#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&#xff1a;锡兰_CC ❣️&#x1f4dd; 专 栏&#xff1a;【OpenCV • c】计算机视觉&#x1f308; 若有帮助&#xff0c;还请关注➕点赞➕收藏&#xff…

代码随想录算法训练营day3 | 203. 移除链表元素,707. 设计链表,206. 反转链表

目录 203. 移除链表元素 707. 设计链表 206. 反转链表 203. 移除链表元素 难度&#xff1a;easy 思路&#xff1a; 代码&#xff1a; // 使用虚拟头结点 class Solution {public ListNode removeElements(ListNode head, int val) {if (head null) {return head;}// 虚拟…

Waves 14 Complete对Mac和Windows系统的最低要求

Waves 14 Complete是一款功能齐全的音频编辑软件&#xff0c;适用于音乐制作、音频工程和声音设计等领域。它提供了一系列强大的工具和效果&#xff0c;帮助用户在音频处理过程中实现专业水平的效果和混音。 Waves 14 Complete包含了多个实用的插件&#xff0c;如均衡器、压缩…