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 时,务必处理好错误情况,并确保传递的数据是有效的。

相关推荐
cg501714 小时前
Spring Boot 的配置文件
java·linux·spring boot
暮云星影15 小时前
三、FFmpeg学习笔记
linux·ffmpeg
rainFFrain15 小时前
单例模式与线程安全
linux·运维·服务器·vscode·单例模式
GalaxyPokemon15 小时前
Muduo网络库实现 [九] - EventLoopThread模块
linux·服务器·c++
mingqian_chu15 小时前
ubuntu中使用安卓模拟器
android·linux·ubuntu
GalaxyPokemon17 小时前
Muduo网络库实现 [十] - EventLoopThreadPool模块
linux·服务器·网络·c++
自由鬼17 小时前
开源虚拟化管理平台Proxmox VE部署超融合
linux·运维·服务器·开源·虚拟化·pve
瞌睡不来17 小时前
(学习总结32)Linux 基础 IO
linux·学习·io
inquisiter17 小时前
UEFI镜像结构布局
linux·spring