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(&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");
#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 命令来获取和设置设备的缓冲区大小。

#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表示EOFreturn 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() {// 打开设备文件,如果失败则返回-1int 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 时,务必处理好错误情况,并确保传递的数据是有效的。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能开发实战matplotlib库应用基础

内容导读 matplotlib简介绘制直方图绘制撒点图 一、matplotlib简介 matplotlib是一个Python 2D绘图库&#xff0c;它以多种硬拷贝格式和跨平台的交互式环境生成高质量的图形。 matplotlib 尝试使容易的事情变得更容易&#xff0c;使困难的事情变得可能。 我们只需几行代码…

解锁全球机遇:澳大利亚服务器租用市场的独特魅力

在浩瀚的全球数字版图中&#xff0c;澳大利亚以其独特的地理位置、丰富的资源禀赋、以及日益增长的数字经济活力&#xff0c;成为了众多互联网企业竞相布局的重要市场。特别是当谈及服务器租用这一关键环节时&#xff0c;澳大利亚以其稳定的网络环境、先进的基础设施和开放的市…

[数据集][目标检测]智慧交通铁路异物入侵检测数据集VOC+YOLO格式802张7类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;802 标注数量(xml文件个数)&#xff1a;802 标注数量(txt文件个数)&#xff1a;802 标注类别…

知识竞赛活动舞台搭建要多少钱

每次举办活动&#xff0c;舞台搭建总是让人头疼的一部分&#xff0c;尤其是费用问题。今天就来揭开活动舞台搭建费用的神秘面纱。 活动舞台搭建的费用主要包括舞台结构、设备、音响、灯光、舞美装饰等各方面的成本。具体来说&#xff1a; 1.舞台结构&#xff1a;包括舞台平台…

5.TensorBoard的使用(二)--add_image()

TensorBoard的使用&#xff08;二&#xff09; 1.使用add_image()给添加图片 首先导入Tensorboard包 from torch.utils.tensorboard import SummaryWriter创建一个SummaryWriter类的实例&#xff0c;并将所有的事件文件保存在logs文件夹中 writer SummaryWriter(logs)使用add…

完整版订单超时自动取消功能

前几天对实习还是继续学习技术产生了抉择&#xff0c;问了一个前辈&#xff0c;他抛给我一个问题&#xff0c;怎么做15分钟订单自动取消&#xff0c;我说然后到时间之后&#xff0c;自动执行这个订单关闭业务&#xff0c;比如把锁了的库存给解开等等操作&#xff0c;然后在数据…

【算法篇】哈希类(笔记)

目录 一、常见的三种哈希结构 二、LeetCode 练习 1. 有效的字母异位词 2. 两个数组的交集 3. 快乐数 4. 两数之和 5. 四数相加II 6. 赎金信 7. 三数之和 8. 四数之和 一、常见的三种哈希结构 当想使用哈希法来解决问题的时候&#xff0c;一般会选择如下三种数据…

4.接口测试基础(Jmter工具/场景二:一个项目由多个人负责接口测试,我只负责其中三个模块,协同)

一、场景二&#xff1a;一个项目由多个人负责接口测试&#xff0c;我只负责其中三个模块&#xff0c;协同 1.什么是测试片段&#xff1f; 1&#xff09;就相当于只是项目的一部分用例&#xff0c;不能单独运行&#xff0c;必须要和控制器&#xff08;include,模块&#xff09;一…

C++——哈希unordered_set/unordered_map的封装

目录 前言 二、unordered_set的封装 1.模板参数列表的改造 2. 增加迭代器操作 3. 模板参数的意义 三、unordered_map的封装 1、“轮子所需要的参数 2、迭代器 四、完整代码 1、HashTable 2、unordered_set 3、unordered_map 总结 前言 unordered_set和map的介绍在上一篇博客有…

2、.Net 前端框架:ASP.Net Core - .Net宣传系列文章

ASP.NET Core 是一个跨平台、高性能、开源的框架&#xff0c;用于构建现代化的、基于云的、互联网连接的应用程序。它是微软对原始ASP.NET框架的重构和扩展&#xff0c;提供了更多的灵活性和改进的性能。ASP.NET Core 可以用于开发Web应用程序、Web API、以及服务端渲染的Web页…

windows系统docker装milvus向量数据库

首先创建一个文件夹比如milvus,在创建如下文件 docker-compose.yml文件如下: version: 3.5services:etcd:container_name: milvus-etcdimage: quay.io/coreos/etcd:v3.5.5environment:- ETCD_AUTO_COMPACTION_MODErevision- ETCD_AUTO_COMPACTION_RETENTION1000- ETCD_QUOTA_B…

计算机毕业设计hadoop+spark+hive物流预测系统 物流大数据分析平台 物流信息爬虫 物流大数据 机器学习 深度学习

流程&#xff1a;1.Python爬虫采集物流数据等存入mysql和.csv文件&#xff1b;2.使用pandasnumpy或者MapReduce对上面的数据集进行数据清洗生成最终上传到hdfs&#xff1b;3.使用hive数据仓库完成建库建表导入.csv数据集&#xff1b;4.使用hive之hive_sql进行离线计算&#xff…

Qt常用控件——QComboBox

文章目录 核心属性、方法、信号模拟点餐文件加载 核心属性、方法、信号 QComboBox表示下拉框 核心属性&#xff1a; 属性说明currentText当前选中文本currentIndex当前选中的条目下标editable是否允许修改设置为true时&#xff0c;QComboBox的行为就非常接近于QLineEdit&…

【智路】智路OS Airos Edge 2.0 Quick Start

Airos Edge 2.0 Quick Start 1 智路OS2.0 1.1 简介 智路OS路侧操作系统airos-edge自下而上分别由内核层&#xff0c;硬件抽象层、框架层、服务层和应用层构成&#xff1b;提供了一系列抽象和框架&#xff0c;支持设备接入、服务、应用等组件开发&#xff0c;兼容X86和ARM操作…

【光照增强论文略读】Zero-Reference Deep Curve Estimation for Low-Light Image Enhancement

这篇题为《用于低光照图像增强的零参考深度曲线估计》的论文介绍了一种名为Zero-DCE的创新方法&#xff0c;用于增强低光照图像。其主要创新点在于&#xff0c;它在训练过程中不需要成对或非成对的参考图像&#xff0c;因此是一种“零参考”方法。通过轻量级深度学习模型DCE-Ne…

SAP学习笔记 - 开发06 - CDSView + Fiori Element 之 List Report

上一章讲了Fiori UI5开发环境搭建和实践&#xff1a; - VSCode 安装Fiori Tools插件 - SEGW 创建后台程序&#xff0c;注册服务&#xff0c;Gateway Client确认服务 - 使用SEGW公开的服务来查询数据显示到页面 SAP学习笔记 - 开发05 - Fiori UI5 开发环境搭建2 Fiori Tools…

北极星计划的回响:从Leap Motion到Midjourney的AI 3D硬件梦想

在科技的浩瀚星空中,总有一些梦想如同北极星般璀璨,指引着探索者前行。六年前,Leap Motion的CEO David以一篇充满激情的博客文章,向我们揭示了“北极星计划”——一个旨在打破数字与物理界限,创造流畅统一体验的增强现实平台。今天,随着Midjourney在AI文生图领域的全球爆…

使用OpenFeign在不同微服务之间传递用户信息时失败

文章目录 起因原因解决方法&#xff1a; 起因 从pay-service中实现下单时&#xff0c;会调用到user-service中的扣减余额。 因此这里需要在不同微服务之间传递用户信息。 但是user-service中始终从始至终拿不到user的信息。 原因 在pay-service中&#xff0c;不仅要Enable O…

Android 10.0 mtk平板camera2横屏预览旋转90度横屏保存圆形预览缩略图旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,点击录像和照片下保存的圆形预览缩略图 依然是竖屏的,所以说同样需要…

【JavaEE】IO基础知识及代码演示

目录 一、File 1.1 观察get系列特点差异 1.2 创建文件 1.3.1 delete()删除文件 1.3.2 deleteOnExit()删除文件 1.4 mkdir 与 mkdirs的区别 1.5 文件重命名 二、文件内容的读写----数据流 1.1 InputStream 1.1.1 使用 read() 读取文件 1.2 OutputStream 1.3 代码演示…