linux驱动开发-ioctl

  • [ioctl 请求码](#ioctl 请求码)
  • [ioctl 请求码的宏](#ioctl 请求码的宏)
  • [常用的 ioctl 宏](#常用的 ioctl 宏)
  • 示例
  • [示例 2](#示例 2)
  • [示例 3 传递结构体字符串参数](#示例 3 传递结构体字符串参数)
  • 注意事项

在内核中,ioctl(input/output control)是一个系统调用,用于设备驱动程序和用户空间程序之间的通信。它允许用户空间程序向设备驱动程序发送命令,以执行特定的操作或获取设备的状态信息。ioctl 是一个非常灵活的接口,因为它可以根据设备类型和需求定义不同的命令。

ioctl 的基本结构

ioctl 系统调用的原型如下:

c 复制代码
#include <sys/ioctl.h>

int ioctl(int fd, unsigned long request, ...);
  • fd:文件描述符,指向设备驱动程序的打开文件。
  • request:ioctl 命令,由设备驱动程序定义。
  • ...:可变参数,用于传递额外的数据

ioctl 请求码

请求码通常由以下几个部分组成:

c 复制代码
| 31 - 30 | 29 - 16 | 15 - 8 | 7 - 0 |
| 方向   |  类型   |  序号  |  大小  |

方向(Direction):2 位,用于指示数据传输的方向。

类型(Type):14 位,用于标识设备或驱动程序的类型。

序号(Number):8 位,用于标识具体的操作。

大小(Size):8 位,用于指示数据的大小。


方向(Direction)
方向字段占用 2 位,用于指示数据传输的方向。常见的方向值如下:

_IOC_NONE(0):没有数据传输。

_IOC_WRITE(1):数据从用户空间写入内核空间。

_IOC_READ(2):数据从内核空间读取到用户空间。

_IOC_READ | _IOC_WRITE(3):双向数据传输。

类型(Type)
类型字段占用 14 位,用于标识设备或驱动程序的类型。通常是一个唯一的标识符,例如一个字符常量。

序号(Number)
序号字段占用 8 位,用于标识具体的操作。通常是一个整数。

大小(Size)
大小字段占用 8 位,用于指示数据的大小。通常是数据结构的大小。

ioctl 请求码的宏

在 Linux 内核中,请求码通常使用宏来定义,例如:

c 复制代码
#define _IOC(dir, type, nr, size) \
    (((dir) << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr) << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))

_IOC_DIRSHIFT、_IOC_TYPESHIFT、_IOC_NRSHIFT、_IOC_SIZESHIFT 是一些预定义的位移常量。

常用的 ioctl 宏

_IO(type, nr):用于定义一个没有数据传输的 ioctl 命令。

_IOR(type, nr, size):用于定义一个从内核读取数据的 ioctl 命令。

_IOW(type, nr, size):用于定义一个向内核写入数据的 ioctl 命令。

_IOWR(type, nr, size):用于定义一个双向数据传输的 ioctl 命令。

_IOC_DIR(request):提取请求码中的方向(Direction)。

_IOC_TYPE(request):提取请求码中的类型(Type)。

_IOC_NR(request):提取请求码中的序号(Number)。

_IOC_SIZE(request):提取请求码中的大小(Size)。

示例

假设我们有一个字符设备驱动程序,我们希望用户空间程序能够通过 ioctl 命令来设置设备的某个参数。

c 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

// 定义设备类型和 ioctl 命令
#define MY_DEVICE_TYPE 'M'
#define MY_IOCTL_SET_PARAM _IOW(MY_DEVICE_TYPE, 0, int)

// 设备打开函数
static int my_device_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "my_device: open\n");
    return 0;
}

// 设备释放函数
static int my_device_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "my_device: release\n");
    return 0;
}

// ioctl 处理函数
static long my_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int param;

    switch (cmd) {
        case MY_IOCTL_SET_PARAM:
            // 从用户空间复制参数到内核空间
            // 注意:copy_from_user() 函数返回 0 表示成功,非 0 表示失败
            if (copy_from_user(&param, (int __user *)arg, _IOC_SIZE(int))) {
                return -EFAULT;
            }
            printk(KERN_INFO "my_device: set param to %d\n", param);
            break;
        default:
            return -EINVAL; // 无效的命令
    }
    return 0;
}

// 文件操作结构体
static struct file_operations my_device_fops = {
    .owner = THIS_MODULE,
    .open = my_device_open,
    .release = my_device_release,
    .unlocked_ioctl = my_device_ioctl,
};

// 设备号和字符设备结构体
static dev_t my_device_dev;
static struct cdev my_device_cdev;

// 模块初始化函数
static int __init my_device_init(void) {
    int ret;

    // 分配设备号
    ret = alloc_chrdev_region(&my_device_dev, 0, 1, "my_device");
    if (ret < 0) {
        printk(KERN_ERR "my_device: failed to allocate device number\n");
        return ret;
    }

    // 初始化字符设备
    cdev_init(&my_device_cdev, &my_device_fops);
    my_device_cdev.owner = THIS_MODULE;

    // 添加字符设备
    ret = cdev_add(&my_device_cdev, my_device_dev, 1);
    if (ret < 0) {
        printk(KERN_ERR "my_device: failed to add character device\n");
        unregister_chrdev_region(my_device_dev, 1);
        return ret;
    }

    printk(KERN_INFO "my_device: module loaded\n");
    return 0;
}

// 模块退出函数
static void __exit my_device_exit(void) {
    // 删除字符设备
    cdev_del(&my_device_cdev);
    // 注销设备号
    unregister_chrdev_region(my_device_dev, 1);
    printk(KERN_INFO "my_device: module unloaded\n");
}

// 模块初始化和退出函数的注册
module_init(my_device_init);
module_exit(my_device_exit);

// 模块许可证、作者和描述信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device driver with ioctl support");
c 复制代码
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// 定义设备类型和 ioctl 命令
#define MY_DEVICE_TYPE 'M'
#define MY_IOCTL_SET_PARAM _IOW(MY_DEVICE_TYPE, 0, int)

int main() {
    int fd;
    int param = 42;

    // 打开设备文件
    fd = open("/dev/my_device", O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    // 调用 ioctl 命令
    if (ioctl(fd, MY_IOCTL_SET_PARAM, &param) < 0) {
        perror("ioctl");
        close(fd);
        return -1;
    }

    // 关闭设备文件
    close(fd);
    return 0;
}

示例 2

假设我们有一个字符设备驱动程序,我们希望用户空间程序能够通过 ioctl 命令来获取和设置设备的缓冲区大小。

c 复制代码
#include <linux/module.h>   // 包含模块相关的宏和函数
#include <linux/init.h>     // 包含模块初始化和退出相关的宏
#include <linux/fs.h>       // 包含文件系统相关的函数和结构体
#include <linux/cdev.h>     // 包含字符设备相关的函数和结构体
#include <linux/uaccess.h>  // 包含用户空间和内核空间数据传输的函数
#include <linux/device.h>   // 包含设备类和设备实例相关的函数
#include <linux/ioctl.h>    // 包含 ioctl 相关的宏和函数

// 定义设备号
static dev_t dev_num;

// 定义字符设备结构体
static struct cdev chr_dev;

// 定义一个设备缓冲区
static char buffer[1024];

// 定义缓冲区的当前大小
static int buffer_size = 0;

// 定义设备类
static struct class *chr_dev_class;

// 定义设备实例
static struct device *chr_dev_device;

// 模块许可证
MODULE_LICENSE("GPL");

// 模块作者
MODULE_AUTHOR("gopher");

// 模块描述
MODULE_DESCRIPTION("A simple character device driver with ioctl support");

// 定义 ioctl 命令
#define CHR_DEV_IOCTL_BASE 'c'
#define CHR_DEV_IOCTL_CLEAR_BUFFER _IO(CHR_DEV_IOCTL_BASE, 0)         // 清空缓冲区命令
#define CHR_DEV_IOCTL_GET_BUFFER_SIZE _IOR(CHR_DEV_IOCTL_BASE, 1, int) // 获取缓冲区大小命令
#define CHR_DEV_IOCTL_SET_BUFFER_SIZE _IOW(CHR_DEV_IOCTL_BASE, 2, int) // 设置缓冲区大小命令

// 打开设备文件时的回调函数
static int chr_dev_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "chr_dev: Device opened\n"); // 打印设备被打开的信息
    return 0; // 返回0表示成功
}

// 关闭设备文件时的回调函数
static int chr_dev_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "chr_dev: Device closed\n"); // 打印设备被关闭的信息
    return 0; // 返回0表示成功
}

// 从设备读取数据时的回调函数
static ssize_t chr_dev_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) {
    int ret;

    if (*ppos >= buffer_size) // 如果请求的偏移量超过缓冲区大小,则返回0表示EOF
        return 0;

    ret = copy_to_user(user_buf, buffer + *ppos, count); // 将数据从内核空间复制到用户空间
    if (ret) // 如果复制失败,返回错误
        return -EFAULT;

    printk(KERN_INFO "chr_dev: Read %zu bytes from device\n", count); // 打印读取的数据字节数
    *ppos += count; // 更新文件位置指针
    return count; // 返回读取的字节数
}

// 向设备写入数据时的回调函数
static ssize_t chr_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) {
    int ret;

    if (count > sizeof(buffer) - buffer_size) // 如果写入的数据超出缓冲区大小,则返回无空间错误
        return -ENOSPC;

    ret = copy_from_user(buffer + buffer_size, user_buf, count); // 将数据从用户空间复制到内核空间
    if (ret) // 如果复制失败,返回错误
        return -EFAULT;

    printk(KERN_INFO "chr_dev: Wrote %zu bytes to device\n", count); // 打印写入的数据字节数
    buffer_size += count; // 更新缓冲区大小
    return count; // 返回写入的字节数
}

// ioctl 回调函数
static long chr_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int ret = 0;
    int size;

    switch (cmd) {
        case CHR_DEV_IOCTL_CLEAR_BUFFER: // 清空缓冲区命令处理
            memset(buffer, 0, sizeof(buffer)); // 将缓冲区清零
            buffer_size = 0; // 重置缓冲区大小
            printk(KERN_INFO "chr_dev: Buffer cleared\n");
            break;

        case CHR_DEV_IOCTL_GET_BUFFER_SIZE: // 获取缓冲区大小命令处理
            size = buffer_size; // 获取当前缓冲区大小
            if (copy_to_user((int __user *)arg, &size, sizeof(size))) // 将缓冲区大小复制到用户空间
                return -EFAULT;
            printk(KERN_INFO "chr_dev: Buffer size retrieved: %d\n", size);
            break;

        case CHR_DEV_IOCTL_SET_BUFFER_SIZE: // 设置缓冲区大小命令处理
            if (copy_from_user(&size, (int __user *)arg, sizeof(size))) // 从用户空间获取新的缓冲区大小
                return -EFAULT;
            if (size < 0 || size > sizeof(buffer)) // 检查新的缓冲区大小是否有效
                return -EINVAL;
            buffer_size = size; // 更新缓冲区大小
            printk(KERN_INFO "chr_dev: Buffer size set to: %d\n", size);
            break;

        default:
            return -EINVAL; // 返回无效命令错误
    }

    return ret; // 返回0表示处理成功
}

// 文件操作结构体
static struct file_operations chr_dev_fops = {
    .owner = THIS_MODULE,
    .open = chr_dev_open,               // 打开设备的回调
    .release = chr_dev_release,         // 关闭设备的回调
    .read = chr_dev_read,               // 读取设备的回调
    .write = chr_dev_write,             // 写入设备的回调
    .unlocked_ioctl = chr_dev_ioctl,    // 添加 ioctl 回调函数
};

// 模块初始化函数
static int __init chr_dev_init(void) {
    int ret;

    // 分配设备号
    ret = alloc_chrdev_region(&dev_num, 0, 1, "chr_dev");
    if (ret < 0) {
        printk(KERN_ERR "chr_dev: Failed to allocate device number\n");
        return ret; // 返回错误
    }

    // 初始化字符设备
    cdev_init(&chr_dev, &chr_dev_fops); // 初始化字符设备结构
    chr_dev.owner = THIS_MODULE; // 设置设备的所有者

    // 添加字符设备
    ret = cdev_add(&chr_dev, dev_num, 1);
    if (ret < 0) {
        printk(KERN_ERR "chr_dev: Failed to add character device\n");
        unregister_chrdev_region(dev_num, 1); // 注销设备号
        return ret; // 返回错误
    }

    // 创建设备类
    chr_dev_class = class_create("chr_dev_class");
    if (IS_ERR(chr_dev_class)) {
        printk(KERN_ERR "chr_dev: Failed to create device class\n");
        cdev_del(&chr_dev); // 删除字符设备
        unregister_chrdev_region(dev_num, 1); // 注销设备号
        return PTR_ERR(chr_dev_class); // 返回错误
    }

    // 创建设备实例
    chr_dev_device = device_create(chr_dev_class, NULL, dev_num, NULL, "chr_dev");
    if (IS_ERR(chr_dev_device)) {
        printk(KERN_ERR "chr_dev: Failed to create device\n");
        class_destroy(chr_dev_class); // 销毁设备类
        cdev_del(&chr_dev); // 删除字符设备
        unregister_chrdev_region(dev_num, 1); // 注销设备号
        return PTR_ERR(chr_dev_device); // 返回错误
    }

    printk(KERN_INFO "chr_dev: Device initialized\n");
    printk(KERN_INFO "chr_dev: Major number: %d\n", MAJOR(dev_num)); // 打印主设备号
    printk(KERN_INFO "chr_dev: Minor number: %d\n", MINOR(dev_num)); // 打印次设备号
    return 0; // 返回0表示成功
}

// 模块退出函数
static void __exit chr_dev_exit(void) {
    // 删除设备实例
    device_destroy(chr_dev_class, dev_num);

    // 删除设备类
    class_destroy(chr_dev_class);

    // 删除字符设备
    cdev_del(&chr_dev);

    // 释放设备号
    unregister_chrdev_region(dev_num, 1);

    printk(KERN_INFO "chr_dev: Device removed\n"); // 打印设备被移除的信息
}

// 注册模块初始化函数
module_init(chr_dev_init);

// 注册模块退出函数
module_exit(chr_dev_exit);

test.c 文件内容如下:

c 复制代码
#include <stdio.h>          // 包含基本输入输出函数的头文件
#include <fcntl.h>         // 包含文件控制的头文件
#include <sys/ioctl.h>     // 包含 ioctl 函数和宏定义的头文件

// 定义 ioctl 命令的基准字符
#define CHR_DEV_IOCTL_BASE 'c'

// 定义 ioctl 命令:清空缓冲区
#define CHR_DEV_IOCTL_CLEAR_BUFFER _IO(CHR_DEV_IOCTL_BASE, 0)
// 定义 ioctl 命令:获取缓冲区大小
#define CHR_DEV_IOCTL_GET_BUFFER_SIZE _IOR(CHR_DEV_IOCTL_BASE, 1, int)
// 定义 ioctl 命令:设置缓冲区大小
#define CHR_DEV_IOCTL_SET_BUFFER_SIZE _IOW(CHR_DEV_IOCTL_BASE, 2, int)

int main() {
    // 打开设备文件,如果失败则返回-1
    int fd = open("/dev/chr_dev", O_RDWR);
    if (fd < 0) {
        perror("open"); // 打印打开设备时的错误信息
        return -1;
    }

    // 清空缓冲区,通过 ioctl 调用设备的清空缓冲区命令
    if (ioctl(fd, CHR_DEV_IOCTL_CLEAR_BUFFER) < 0) {
        perror("ioctl clear buffer"); // 打印清空缓冲区时的错误信息
        close(fd); // 关闭设备文件描述符
        return -1;
    }

    // 获取缓冲区大小的准备
    int size; // 存储缓冲区大小的变量
    if (ioctl(fd, CHR_DEV_IOCTL_GET_BUFFER_SIZE, &size) < 0) {
        perror("ioctl get buffer size"); // 打印获取缓冲区大小时的错误信息
        close(fd); // 关闭设备文件描述符
        return -1;
    }
    printf("Buffer size: %d\n", size); // 打印获取到的缓冲区大小

    // 设置新的缓冲区大小
    size = 512; // 指定要设置的新缓冲区大小
    if (ioctl(fd, CHR_DEV_IOCTL_SET_BUFFER_SIZE, &size) < 0) {
        perror("ioctl set buffer size"); // 打印设置缓冲区大小时的错误信息
        close(fd); // 关闭设备文件描述符
        return -1;
    }

    // 关闭设备文件描述符
    close(fd);
    return 0; // 程序正常结束返回0
}

查看内核

[ 4924.302297] chr_dev: Device initialized
[ 4924.302302] chr_dev: Major number: 240
[ 4924.302303] chr_dev: Minor number: 0
[ 4992.722315] chr_dev: Device opened
[ 4992.722321] chr_dev: Buffer cleared
[ 4992.722335] chr_dev: Buffer size retrieved: 0
[ 4992.722382] chr_dev: Buffer size set to: 512
[ 4992.722401] chr_dev: Device closed

示例 3 传递结构体字符串参数

c 复制代码
#include <linux/module.h>   // 包含模块相关的宏和函数
#include <linux/init.h>     // 包含模块初始化和退出相关的宏
#include <linux/fs.h>       // 包含文件系统相关的函数和结构体
#include <linux/cdev.h>     // 包含字符设备相关的函数和结构体
#include <linux/uaccess.h>  // 包含用户空间和内核空间数据传输的函数
#include <linux/device.h>   // 包含设备类和设备实例相关的函数
#include <linux/ioctl.h>    // 包含 ioctl 相关的宏和函数

// 设备号全局变量
static dev_t dev_num;

// 字符设备结构体
static struct cdev chr_dev;

// 设备类
static struct class *chr_dev_class;

// 设备实例
static struct device *chr_dev_device;

// 模块许可证
MODULE_LICENSE("GPL");

// 模块作者
MODULE_AUTHOR("gopher");

// 模块描述
MODULE_DESCRIPTION("A simple character device driver with ioctl support");

// 定义 ioctl 命令
#define CHR_DEV_IOCTL_BASE 'c'
#define CHR_DEV_IOCTL_SET_STRING _IOW(CHR_DEV_IOCTL_BASE, 0, char *) // 设置字符串命令
#define CHR_DEV_IOCTL_SET_STRUCT _IOW(CHR_DEV_IOCTL_BASE, 1, struct my_struct *) // 设置结构体命令

// 定义结构体
struct my_struct {
    int id;                  // ID字段
    char name[64];          // 名称字段,最多64个字符
};

// 打开设备文件时的回调函数
static int chr_dev_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "chr_dev: Device opened\n"); // 打印设备被打开的日志
    return 0; // 返回0表示成功
}

// 关闭设备文件时的回调函数
static int chr_dev_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "chr_dev: Device closed\n"); // 打印设备被关闭的日志
    return 0; // 返回0表示成功
}

// ioctl 回调函数
static long chr_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int ret = 0;
    char str[1024]; // 用于接收字符串的内核空间缓冲区
    struct my_struct my_data; // 定义结构体变量以接收数据

    switch (cmd) {
        case CHR_DEV_IOCTL_SET_STRING: // 处理设置字符串命令
            // 从用户空间复制字符串到内核空间
            if (copy_from_user(str, (char __user *)arg, sizeof(str)))
                return -EFAULT; // 如果复制失败,返回错误
            printk(KERN_INFO "chr_dev: Received string: %s\n", str); // 打印接收的字符串
            break;

        case CHR_DEV_IOCTL_SET_STRUCT: // 处理设置结构体命令
            // 从用户空间复制结构体到内核空间
            if (copy_from_user(&my_data, (struct my_struct __user *)arg, sizeof(struct my_struct)))
                return -EFAULT; // 如果复制失败,返回错误
            printk(KERN_INFO "chr_dev: Received struct - id: %d, name: %s\n", my_data.id, my_data.name); // 打印接收的结构体数据
            break;

        default:
            return -EINVAL; // 如果命令无效,返回错误
    }

    return ret; // 返回0表示处理成功
}

// 文件操作结构体
static struct file_operations chr_dev_fops = {
    .owner = THIS_MODULE,
    .open = chr_dev_open,               // 打开设备的回调
    .release = chr_dev_release,         // 关闭设备的回调
    .unlocked_ioctl = chr_dev_ioctl,    // 添加 ioctl 回调函数
};

// 模块初始化函数
static int __init chr_dev_init(void) {
    int ret;

    // 分配设备号
    ret = alloc_chrdev_region(&dev_num, 0, 1, "chr_dev");
    if (ret < 0) {
        printk(KERN_ERR "chr_dev: Failed to allocate device number\n");
        return ret; // 返回错误
    }

    // 初始化字符设备
    cdev_init(&chr_dev, &chr_dev_fops); // 将文件操作结构体与字符设备关联
    chr_dev.owner = THIS_MODULE; // 设置设备的所有者

    // 添加字符设备
    ret = cdev_add(&chr_dev, dev_num, 1);
    if (ret < 0) {
        printk(KERN_ERR "chr_dev: Failed to add character device\n");
        unregister_chrdev_region(dev_num, 1); // 注销设备号
        return ret; // 返回错误
    }

    // 创建设备类
    chr_dev_class = class_create("chr_dev_class");
    if (IS_ERR(chr_dev_class)) {
        printk(KERN_ERR "chr_dev: Failed to create device class\n");
        cdev_del(&chr_dev); // 删除字符设备
        unregister_chrdev_region(dev_num, 1); // 注销设备号
        return PTR_ERR(chr_dev_class); // 返回错误
    }

    // 创建设备实例
    chr_dev_device = device_create(chr_dev_class, NULL, dev_num, NULL, "chr_dev");
    if (IS_ERR(chr_dev_device)) {
        printk(KERN_ERR "chr_dev: Failed to create device\n");
        class_destroy(chr_dev_class); // 销毁设备类
        cdev_del(&chr_dev); // 删除字符设备
        unregister_chrdev_region(dev_num, 1); // 注销设备号
        return PTR_ERR(chr_dev_device); // 返回错误
    }

    printk(KERN_INFO "chr_dev: Device driver initialized\n"); // 打印设备驱动初始化成功的信息
    return 0; // 返回0表示成功
}

// 模块退出函数
static void __exit chr_dev_exit(void) {
    // 删除设备实例
    device_destroy(chr_dev_class, dev_num);

    // 删除设备类
    class_destroy(chr_dev_class);

    // 删除字符设备
    cdev_del(&chr_dev);

    // 注销设备号
    unregister_chrdev_region(dev_num, 1);

    printk(KERN_INFO "chr_dev: Device driver exited\n"); // 打印设备驱动退出的信息
}

// 注册模块初始化函数
module_init(chr_dev_init);

// 注册模块退出函数
module_exit(chr_dev_exit);
c 复制代码
#include <stdio.h>          // 包含标准输入输出函数
#include <stdlib.h>         // 包含标准库函数
#include <fcntl.h>         // 包含文件控制操作的头文件
#include <sys/ioctl.h>     // 包含 ioctl 函数和宏定义的头文件
#include <string.h>         // 包含字符串操作的函数
#include <errno.h>         // 包含 errno 变量的定义

// 定义 ioctl 命令
#define CHR_DEV_IOCTL_BASE 'c' // 定义 ioctl 命令基准
#define CHR_DEV_IOCTL_SET_STRING _IOW(CHR_DEV_IOCTL_BASE, 0, char *) // 定义设置字符串的 ioctl 命令
#define CHR_DEV_IOCTL_SET_STRUCT _IOW(CHR_DEV_IOCTL_BASE, 1, struct my_struct *) // 定义设置结构体的 ioctl 命令

// 定义结构体
struct my_struct {
    int id;                  // 结构体中的 ID 字段
    char name[64];          // 结构体中的名称字段,最多64个字符
};

int main() {
    int fd; // 文件描述符
    char *str = "Hello from user space!"; // 定义要传递的字符串
    struct my_struct my_data = {1, "User Struct"}; // 初始化要传递的结构体

    // 打开设备文件,获取文件描述符
    fd = open("/dev/chr_dev", O_RDWR);
    if (fd < 0) { // 检查文件打开是否成功
        perror("Failed to open the device"); // 打印失败信息
        return errno; // 返回错误代码
    }

    // 使用 ioctl 传递字符串到设备
    if (ioctl(fd, CHR_DEV_IOCTL_SET_STRING, str) < 0) { // 调用 ioctl 设置字符串
        perror("Failed to set string via ioctl"); // 打印失败信息
        close(fd); // 关闭设备文件描述符
        return errno; // 返回错误代码
    }

    // 使用 ioctl 传递结构体到设备
    if (ioctl(fd, CHR_DEV_IOCTL_SET_STRUCT, &my_data) < 0) { // 调用 ioctl 设置结构体
        perror("Failed to set struct via ioctl"); // 打印失败信息
        close(fd); // 关闭设备文件描述符
        return errno; // 返回错误代码
    }

    // 关闭设备文件描述符
    close(fd);
    return 0; // 程序正常结束返回0
}
c 复制代码
[ 6474.643614] chr_dev: Device driver initialized
[ 6495.790475] chr_dev: Device opened
[ 6495.790482] chr_dev: Received string: Hello from user space!
[ 6495.790484] chr_dev: Received struct - id: 1, name: User Struct
[ 6495.790540] chr_dev: Device closed

注意事项

ioctl 是一个非常底层的接口,通常用于特定的设备驱动程序。对于大多数应用程序,使用更高级的接口(如 read、write、mmap 等)可能更为合适。

ioctl 的请求码必须在内核和用户空间之间保持一致,否则会导致未定义的行为。

在使用 ioctl 时,务必处理好错误情况,并确保传递的数据是有效的。

相关推荐
LKAI.9 分钟前
搭建Elastic search群集
linux·运维·elasticsearch·搜索引擎
gywl2 小时前
openEuler VM虚拟机操作(期末考试)
linux·服务器·网络·windows·http·centos
hhhhhhh_hhhhhh_3 小时前
rk3568制冷项目驱动开发流程汇总(只适用于部分模块CIF DVP等,自用)
驱动开发
日记跟新中3 小时前
Ubuntu20.04 修改root密码
linux·运维·服务器
码农君莫笑3 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
BUG 4043 小时前
Linux——Shell
linux·运维·服务器
大霞上仙4 小时前
Linux 多命令执行
linux·运维·服务器
晨欣4 小时前
Kibana:LINUX_X86_64 和 DEB_X86_64两种可选下载方式的区别
linux·运维·服务器
AI青年志4 小时前
【服务器】linux服务器管理员查看用户使用内存情况
linux·运维·服务器
dessler5 小时前
Docker-run命令详细讲解
linux·运维·后端·docker