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

这篇具有很好参考价值的文章主要介绍了【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_CMD1
            break;
        case MY_IOCTL_CMD2:
            // 处理MY_IOCTL_CMD2
            if (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(); // 自进程ID

bind(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. 从系统中删除cdev
    cdev_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`调用。请注意,这只是一个简单的框架。在实际的驱动实现中,你将需要填充这些函数,处理错误情况,并且可能需要处理并发控制和同步问题。文章来源地址https://www.toymoban.com/news/detail-808636.html

到了这里,关于【linux驱动】用户空间程序与内核模块交互-- IOCTL和Netlink的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Ioctl()方式实现与驱动交互简洁框架

    ioctl是linux中一种除read和write之外的数据传递机制 驱动层IOCTL: 以上函数参数的含义如下 。 inode和fp用来确定被操作的设备。 request就是用户程序下发的命令。 args就是用户程序在必要时传递的参数。 在2.6.36以后ioctl函数已经不存在了,用unlocked_ioctl和compat_ioctl两个函数代替。

    2024年02月09日
    浏览(12)
  • ifconfig工具与驱动交互解析(ioctl)

    ifconfig工具与驱动交互解析(ioctl)

    Linux ifconfig命令用于显示或设置网络设备。ifconfig可设置网络设备的状态,或是显示目前的设置。 同netstat一样,ifconfig源码也位于net-tools中。 源码位于net-tools工具包中,这是linux网络的基本工具包,此外还有arp,hostname,route等命令。 此文章不考虑ifconfig的具体功能介绍,而是侧

    2024年02月07日
    浏览(10)
  • Linux用户空间与内核空间通信(Netlink通信机制)

    Linux用户空间与内核空间通信(Netlink通信机制)

    Netlink是linux提供的用于内核和用户态进程之间的通信方式。但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的广播特性时。 那么Netlink有什么优势呢? 一般

    2024年02月04日
    浏览(13)
  • linux驱动开发--day1(驱动、内核模块及相关命令、内核模块传参)
  • Linux驱动开发——内核模块

    Linux驱动开发——内核模块

    目录 内核模块的由来 第一个内核模块程序  内核模块工具  将多个源文件编译生成一个内核模块  内核模块参数 内核模块依赖 关于内核模块的进一步讨论  习题 最近一直在玩那些其它的技术,眼看快暑假了,我决定夯实一下我的驱动方面的技能,迎接我的实习,找了一本

    2024年02月04日
    浏览(43)
  • linux用户态与内核态通过字符设备交互

    linux用户态与内核态通过字符设备交互

    Linux设备分为三类,字符设备、块设备、网络接口设备。字符设备只能一个字节一个字节读取,常见外设基本都是字符设备。块设备一般用于存储设备,一块一块的读取。网络设备,Linux将对网络通信抽象成一个设备,通过套接字对其进行操作。 对于字符设备的用户态与内核

    2024年02月03日
    浏览(12)
  • 【嵌入式Linux内核驱动】内核模块三要素与验证测试

    内核模块 Linux内核模块是一种可以动态加载和卸载的软件组件,用于扩展Linux操作系统的功能。Linux内核本身只包含了必要的核心功能,而内核模块则允许开发者在运行时向内核添加新的功能、驱动程序或文件系统支持,而无需重新编译整个内核或重新启动系统。 内核模块是

    2024年02月06日
    浏览(65)
  • Linux内核4.14版本——drm框架分析(7)——用户态和内核态间的交互

             驱动会注册一个支持KMS的DRM设备时,会在/dev/drm/下创建一个card%d文件,用户态可以通过打开该文件,并对文件描述符做相应的操作实现相应的功能。该文件描述符对应的文件操作回调函数(filesystem_operations)位于drm_driver中,并由驱动程序填充。典型如下:      

    2024年02月09日
    浏览(12)
  • 【Linux驱动】内核模块编译 —— make modules 的使用(单模块编译、多模块编译)

    编译驱动一般采用的是将驱动编译成模块(.ko 文件),然后加载到内核,这其中就用到了 make modules 命令。 目录 一、单模块编译 1、一个 c 文件编译成一个 ko 文件 2、多个文件编译成一个 ko 文件 二、多模块编译(多文件多模块) 下面是最简易的单文件单模块编译,假设我们

    2024年02月10日
    浏览(47)
  • linux驱动开发(四):ioctl()函数

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

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

    2024年01月16日
    浏览(9)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包