linux驱动开发-ioctl
- ioctl 请求码
- ioctl 请求码的宏
- 常用的 ioctl 宏
- 示例
- 示例 2
- 示例 3 传递结构体字符串参数
- 注意事项
在内核中,ioctl(input/output control)是一个系统调用,用于设备驱动程序和用户空间程序之间的通信。它允许用户空间程序向设备驱动程序发送命令,以执行特定的操作或获取设备的状态信息。ioctl 是一个非常灵活的接口,因为它可以根据设备类型和需求定义不同的命令。
ioctl 的基本结构
ioctl 系统调用的原型如下:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
- fd:文件描述符,指向设备驱动程序的打开文件。
- request:ioctl 命令,由设备驱动程序定义。
- …:可变参数,用于传递额外的数据
ioctl 请求码
请求码通常由以下几个部分组成:
| 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 内核中,请求码通常使用宏来定义,例如:
#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 命令来设置设备的某个参数。
#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(¶m, (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");
#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, ¶m) < 0) {
perror("ioctl");
close(fd);
return -1;
}
// 关闭设备文件
close(fd);
return 0;
}
示例 2
假设我们有一个字符设备驱动程序,我们希望用户空间程序能够通过 ioctl 命令来获取和设置设备的缓冲区大小。
#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 文件内容如下:
#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 传递结构体字符串参数
#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);
#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
}
[ 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 时,务必处理好错误情况,并确保传递的数据是有效的。