【linux驱动】用户空间程序与内核模块交互-- IOCTL和Netlink

创建自定义的IOCTL(输入/输出控制)或Netlink命令以便用户空间程序与内核模块交互涉及几个步骤。这里将分别介绍这两种方法。

一、IOCTL 方法

1. 定义IOCTL命令

在内核模块中,需要使用宏定义你的IOCTL命令。通常情况下,IOCTL命令包括了一个命令编号、请求类型的方向(读/写/两者)以及数据大小:

#include <linux/ioctl.h>#define MY_IOCTL_TYPE 'x'  // 通常是一个字符#define MY_IOCTL_CMD1 _IOR(MY_IOCTL_TYPE, 1, my_data_struct)
#define MY_IOCTL_CMD2 _IOW(MY_IOCTL_TYPE, 2, my_data_struct)
// ...

2. 实现ioctl函数

在你的内核模块中,实现ioctl系统调用的函数处理:

static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {my_data_struct data;switch (cmd) {case MY_IOCTL_CMD1:if (copy_from_user(&data, (my_data_struct __user *)arg, sizeof(data)))return -EFAULT;// 处理MY_IOCTL_CMD1break;case MY_IOCTL_CMD2:// 处理MY_IOCTL_CMD2if (copy_to_user((my_data_struct __user *)arg, &data, sizeof(data)))return -EFAULT;break;default:return -ENOTTY; // 未知的命令}return 0; // 成功
}const struct file_operations fops = {.unlocked_ioctl = my_ioctl,// 其他的file_operations成员
};

3. 在用户空间调用IOCTL

应用程序使用`ioctl`系统调用与内核模块交流:

#include <sys/ioctl.h>
#include <fcntl.h>int fd = open("/dev/mydevice", O_RDWR);
my_data_struct data;
// 设置 data
ioctl(fd, MY_IOCTL_CMD2, &data);
// 读取 data
ioctl(fd, MY_IOCTL_CMD1, &data);
close(fd);

二、Netlink 方法

1. 初始化Netlink Socket

在内核模块中,创建并初始化Netlink Socket:

#include <net/sock.h>
struct sock *nl_sk = NULL;static void nl_recv_msg(struct sk_buff *skb) {// 从skb中解析出消息并处理
}static int __init my_module_init(void) {struct netlink_kernel_cfg cfg = {.input = nl_recv_msg,};nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);if (!nl_sk) {pr_err("Error creating socket.\n");return -10;}return 0;
}

2. 实现Netlink消息处理函数

如上所示,`nl_recv_msg`是在用户空间发送消息到内核时调用的接收消息处理函数。处理逻辑根据具体需求实现。

3. 用户空间程序

在用户空间程序中,使用Netlink进行通讯:

#include <sys/socket.h>
#include <linux/netlink.h>struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
int nl_sock;// 创建Netlink Socket
nl_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);// 初始化地址结构
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); // 自进程IDbind(nl_sock, (struct sockaddr*)&src_addr, sizeof(src_addr));memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0;   // 对端的ID,0表示内核
dest_addr.nl_groups = 0; // 无组播// 发送消息到内核

记得在模块中注册Netlink操作,并且在模块退出时释放Netlink Socket,用户空间程序需要负责构造和解码Netlink消息。以上只是一个概述,实现时往往需要处理更多的细节和错误情况。

三、创建一个字符设备让用户空间程序进行读写操作,内核模块可以对这些操作进行响应

在Linux中,字符设备是可以进行按字节流读写操作的设备。创建一个字符设备使得用户空间的程序可以打开、读写、关闭等操作,并使得内核模块能够对这些操作进行响应,通常是通过实现一个设备驱动来完成的。`ioctl`是一个系统调用,用于设备特定的操作,如配置或获取设备信息。在字符设备驱动中实现`ioctl`是可选的,取决于设备是否需要提供额外的设备控制功能。

以下是创建和注册字符设备的基本步骤:

1. 分配设备号:

使用`alloc_chrdev_region`来动态申请主设备号和从设备号,或者使用`register_chrdev_region`如果你希望静态指定设备号。

2. 创建设备类和设备节点:

通常采用`class_create`创建一个设备类,并使用`device_create`创建设备节点。设备节点是用户空间与设备交云的接口,在`/dev/`目录下创建。

3. 初始化`cdev`结构:

`cdev`结构代表字符设备的内核结构。使用`cdev_init`来初始化`cdev`结构,并关联该结构与之前定义的文件操作函数集合。

4. 添加 cdev 到内核中:

使用`cdev_add`将`cdev`结构添加到内核中,设备就会变为活跃状态,用户空间就可以访问它了。

5. 实现文件操作函数:

定义一个包含`open`、`release`、`read`、`write`等操作的`file_operations`结构,这样用户空间应用就可以通过系统调用来操作驱动。

例如,以下代码段演示了以上步骤的基本框架:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>static int major;
static struct class *my_class;
static struct cdev my_cdev;static int my_open(struct inode *inode, struct file *file)
{// 打开设备的代码return 0;
}static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{// 读取设备的代码return 0; // 返回读取的字节数
}static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{// 写入设备的代码return count; // 返回写入的字节数
}static int my_release(struct inode *inode, struct file *file)
{// 关闭设备的代码return 0;
}static const struct file_operations my_fops = {.owner = THIS_MODULE,.open = my_open,.read = my_read,.write = my_write,.release = my_release,// 如果你需要使用ioctl,则在这里添加.unlocked_ioctl = my_ioctl,
};static int __init my_init(void)
{dev_t dev_id;// 1. 分配设备号if (alloc_chrdev_region(&dev_id, 0, 1, "my_device") < 0) {return -1;}major = MAJOR(dev_id);// 2. 创建设备类和设备节点my_class = class_create(THIS_MODULE, "my_device_class");device_create(my_class, NULL, dev_id, NULL, "mydevice");// 3. 初始化cdev结构cdev_init(&my_cdev, &my_fops);// 4. 添加cdev到内核中if (cdev_add(&my_cdev, dev_id, 1) < 0) {unregister_chrdev_region(dev_id, 1);return -1;}return 0;
}static void __exit my_exit(void)
{dev_t dev_id = MKDEV(major, 0);// 5. 从系统中删除cdevcdev_del(&my_cdev);// 销毁设备节点和设备类device_destroy(my_class, dev_id);class_destroy(my_class);// 释放设备号unregister_chrdev_region(dev_id, 1);
}module_init(my_init);
module_exit(my_exit);MODULE_LICENSE("GPL");

在`my_fops`中,你可以实现`.unlocked_ioctl`(或`.ioctl`,取决于内核版本)来响应`ioctl`调用。请注意,这只是一个简单的框架。在实际的驱动实现中,你将需要填充这些函数,处理错误情况,并且可能需要处理并发控制和同步问题。

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

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

相关文章

实战内网穿透NPS搭建过程

前提条件 首先你要有个公网IP的服务器&#xff0c;既然是内网穿透&#xff0c;那必然是通过公网IP或者域名访问本地服务。 官网下载地址 https://github.com/ehang-io/nps/releases 服务端 选择linux_amd64_server.tar.gz 客户端 选择windows_amd64_client.tar.gz 服…

EasyRecovery2024电脑数据恢复工具好不好用?

Ontrack是我们综述中的第一个产品&#xff0c;由于该软件的功效和广度&#xff0c;我认为它完全基于业务。有一个具有基本功能的免费版本和一系列付费版本&#xff0c;不仅可以恢复文件&#xff08;免费版和家庭版&#xff09;&#xff0c;还可以创建磁盘映像/从 CD 和 DVD 恢复…

统计学-R语言-7.1

文章目录 前言假设检验的原理假设检验的原理提出假设做出决策表述结果效应量 总体均值的检验总体均值的检验(一个总体均值的检验) 练习 前言 本章主题是假设检验(hypothesis testing)。与参数估计一样&#xff0c;假设检验也是对总体参数感兴趣&#xff0c;如比例、比例间的差…

【PyTorch】在PyTorch中使用线性层和交叉熵损失函数进行数据分类

在PyTorch中使用线性层和交叉熵损失函数进行数据分类 前言&#xff1a; 在机器学习的众多任务中&#xff0c;分类问题无疑是最基础也是最重要的一环。本文将介绍如何在PyTorch框架下&#xff0c;使用线性层和交叉熵损失函数来解决分类问题。我们将以简单的Iris数据集作为起点…

数组中第K个最大元素(算法村第十关白银挑战)

215. 数组中的第K个最大元素 - 力扣&#xff08;LeetCode&#xff09; 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 **k** 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现…

JVM工作原理与实战(二十一):内存管理

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、不同语言的内存管理 1.C/C的内存管理 2.Java的内存管理 二、垃圾回收的对比 1.自动垃圾回收与手动垃圾回收的对比 2.优点与缺点 总结 前言 JVM作为Java程序的运行环境&#…

Golang 搭建 WebSocket 应用(八) - 完整代码

本文应该是本系列文章最后一篇了&#xff0c;前面留下的一些坑可能后面会再补充一下&#xff0c;但不在本系列文章中了。 整体架构 再来回顾一下我们的整体架构&#xff1a; 在我们的 demo 中&#xff0c;包含了以下几种角色&#xff1a; 客户端&#xff1a;一般是浏览器&am…

XSS漏洞:利用多次提交技巧实现存储型XSS

目录 搭建环境 XSS攻击 测试 xss系列往期文章&#xff1a; 初识XSS漏洞-CSDN博客 利用XSS漏洞打cookie-CSDN博客 XSS漏洞&#xff1a;xss-labs靶场通关-CSDN博客 XSS漏洞&#xff1a;prompt.mi靶场通关-CSDN博客 XSS漏洞&#xff1a;xss.haozi.me靶场通关-CSDN博客 本…

磁盘分区机制

lsblk查看分区 Linux分区 挂载的经典案例 1. 虚拟机增加磁盘 点击这里&#xff0c;看我的这篇文章操作 添加之后&#xff0c;需要重启系统&#xff0c;不重启在系统里看不到新硬盘哦 出来了&#xff0c;但还没有分区 2. 分区 还没有格式化 3. 格式化磁盘 4. 挂载 5. 卸载…

汇编语言----X86汇编指令

目录 1.汇编指令的构成 2.X86架构CPU中包含的寄存器 3.常见的x86汇编指令 &#xff08;1&#xff09;算数运算 &#xff08;2&#xff09;逻辑运算 &#xff08;3&#xff09;其他 4.AT&T格式 5.选择语句&#xff08;分支结构&#xff09; 6.循环语句 &#xff0…

huggingface学习 | 云服务器使用hf_hub_download下载huggingface上的模型文件

系列文章目录 huggingface学习 | 云服务器使用git-lfs下载huggingface上的模型文件 文章目录 系列文章目录一、hf_hub_download介绍二、找到需要下载的huggingface文件三、准备工作及下载过程四、全部代码 一、hf_hub_download介绍 hf_hub_download是huggingface官方支持&…

Linux中的共享内存

定义&#xff1a; 共享内存允许两个或者多个进程共享物理内存的同一块区域&#xff08;通常被称为段&#xff09;。由于一个共享内存段会称为一个进程用户空间的一部分&#xff0c;因此这种 IPC 机制无需内核介入。所有需要做的就是让一个进程将数 据复制进共享内存中&#xff…