操作系统实验-设备管理

前言

  • 使用vim编写代码,具体操作参考前文进程管理部分

实验操作

任务一:编写USB设备驱动程序

首先插上u盘查看相关信息以便后续编写代码

# 获取u盘vid和pid
lsusb

alt text
如图,vid为090c,pid为2000
设备名称为后面Silicon那一长串

编写相关代码:

  • usb_detect.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>

/* 定义你的U盘的Vendor ID和Product ID */
#define USB_DETECT_VENDOR_ID    0x090c  // u盘vid
#define USB_DETECT_PRODUCT_ID   0x2000  // u盘pid

/* 支持的设备列表 */
static const struct usb_device_id usbdetect_table[] = {
    { USB_DEVICE(USB_DETECT_VENDOR_ID, USB_DETECT_PRODUCT_ID) },
    { } /* 终止项 */
};
MODULE_DEVICE_TABLE(usb, usbdetect_table);

/* 探测函数 - 当设备插入时调用 */
static int usbdetect_probe(struct usb_interface *interface,
                          const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(interface);
    
    printk(KERN_INFO "USB Device Inserted: VID=0x%04x, PID=0x%04x\n",
           le16_to_cpu(udev->descriptor.idVendor),
           le16_to_cpu(udev->descriptor.idProduct));
    
    return 0; /* 返回0表示驱动接受该设备 */
}

/* 断开函数 - 当设备拔出时调用 */
static void usbdetect_disconnect(struct usb_interface *interface)
{
    struct usb_device *udev = interface_to_usbdev(interface);
    
    printk(KERN_INFO "USB Device Removed: VID=0x%04x, PID=0x%04x\n",
           le16_to_cpu(udev->descriptor.idVendor),
           le16_to_cpu(udev->descriptor.idProduct));
}

/* USB驱动结构体 */
static struct usb_driver usbdetect_driver = {
    .name =     "usb_detect",       /* 驱动名称 */
    .probe =    usbdetect_probe,    /* 设备插入时调用 */
    .disconnect = usbdetect_disconnect, /* 设备拔出时调用 */
    .id_table = usbdetect_table,    /* 支持的设备列表 */
};

/* 模块初始化函数 */
static int __init usb_detect_init(void)
{
    int ret;
    
    printk(KERN_INFO "USB Detect Driver: Initializing...\n");
    
    /* 注册USB驱动 */
    ret = usb_register(&usbdetect_driver);
    if (ret < 0) {
        printk(KERN_ERR "USB Detect Driver: Registration failed (%d)\n", ret);
        return ret;
    }
    
    printk(KERN_INFO "USB Detect Driver: Registered successfully\n");
    return 0;
}

/* 模块退出函数 */
static void __exit usb_detect_exit(void)
{
    printk(KERN_INFO "USB Detect Driver: Unregistering...\n");
    
    /* 注销USB驱动 */
    usb_deregister(&usbdetect_driver);
    
    printk(KERN_INFO "USB Detect Driver: Unregistered\n");
}

module_init(usb_detect_init);
module_exit(usb_detect_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple USB Device Detection Driver");
  • Makefile
obj-m := usb_detect.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean

编译驱动

make

加载驱动并查看日志

sudo insmod usb_detect.ko
dmesg | tail

alt text

我在模块加载完成后并不能正常输出,排查后发现是因为usb设备被usb_storage驱动占用,卸载相关驱动后重新插拔U盘,输出如下,可以看到USB插拔过程中能检测并输出提示

alt text

任务二:编写内核模块测试硬盘的读写速率,并与 iozone工具的测试结果比较,并分析结果差异原因

编写内核模块测试硬盘读写速率

编写相关代码:

  • test.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/ktime.h>
#include <linux/namei.h>
#include <linux/file.h>
#include <linux/fcntl.h>

#define FILE_SIZE   (512 * 1024 * 1024) // 512MB
#define MY_BLOCK_SIZE  (1024)           // 1KB,避免与内核BLOCK_SIZE冲突
#define TEST_FILE   "/tmp/disk_bench_testfile"

static char *buffer;

static int __init disk_benchmark_init(void) {
    struct file *filp;
    loff_t pos;
    ktime_t start, end;
    s64 write_time, read_time;
    ssize_t ret;
    size_t written, readed;

    buffer = kmalloc(MY_BLOCK_SIZE, GFP_KERNEL);
    if (!buffer) {
        printk(KERN_ERR "Failed to allocate memory for buffer\n");
        return -ENOMEM;
    }
    memset(buffer, 'A', MY_BLOCK_SIZE);

    // 写测试
    filp = filp_open(TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (IS_ERR(filp)) {
        printk(KERN_ERR "Failed to open file for write test\n");
        kfree(buffer);
        return -EIO;
    }
    pos = 0;
    written = 0;
    start = ktime_get();
    while (written < FILE_SIZE) {
        size_t to_write = MY_BLOCK_SIZE;
        if (FILE_SIZE - written < MY_BLOCK_SIZE)
            to_write = FILE_SIZE - written;
        ret = kernel_write(filp, buffer, to_write, &pos);
        if (ret < 0) break;
        written += ret;
    }
    end = ktime_get();
    filp_close(filp, NULL);
    write_time = ktime_to_ns(ktime_sub(end, start));
    if (ret < 0)
        printk(KERN_ERR "Write error: %zd\n", ret);
    else
        printk(KERN_INFO "Write test: %zu bytes, %lld ns, %lld KB/s\n",
               written, write_time,
               write_time ? (written * 1000000000LL / write_time / 1024) : 0);

    // 读测试
    filp = filp_open(TEST_FILE, O_RDONLY, 0);
    if (IS_ERR(filp)) {
        printk(KERN_ERR "Failed to open file for read test\n");
        kfree(buffer);
        return -EIO;
    }
    pos = 0;
    readed = 0;
    start = ktime_get();
    while (readed < FILE_SIZE) {
        size_t to_read = MY_BLOCK_SIZE;
        if (FILE_SIZE - readed < MY_BLOCK_SIZE)
            to_read = FILE_SIZE - readed;
        ret = kernel_read(filp, buffer, to_read, &pos);
        if (ret <= 0) break;
        readed += ret;
    }
    end = ktime_get();
    filp_close(filp, NULL);
    read_time = ktime_to_ns(ktime_sub(end, start));
    if (ret < 0)
        printk(KERN_ERR "Read error: %zd\n", ret);
    else
        printk(KERN_INFO "Read test: %zu bytes, %lld ns, %lld KB/s\n",
               readed, read_time,
               read_time ? (readed * 1000000000LL / read_time / 1024) : 0);

    kfree(buffer);
    return 0;
}

static void __exit disk_benchmark_exit(void) {
    printk(KERN_INFO "Disk benchmark module unloaded\n");
}

module_init(disk_benchmark_init);
module_exit(disk_benchmark_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple disk benchmark kernel module");
MODULE_VERSION("1.0");
  • Makefile
obj-m += test.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

编译后加载内核,查看日志看到测速信息
alt text

使用iozone工具测速

安装iozone

# 直接安装显示no match了,所以从直接下载源码编译了
# 编译工具,编译内核的时候应该下过?
sudo dnf install gcc make wget tar
# 下载源码
wget https://www.iozone.org/src/current/iozone3_507.tar
# 解压并编译
tar xf iozone3_508.tar
cd iozone3_508/src/current
make linux

指导书上输入命令如下

./iozone -Raz -n 512m -g 16g -r 1k -y 1k -i 0 -i 1 -b /usr/src/kernels/iozone.xls

其中 -g 参数后为最大测试文件大小, 修改为内存大小的两倍以避免缓存影响
输出路径我不是很清楚为什么要放在这个文件,就直接放在当前目录以便后续处理了

按照我自己的配置修改命令如下

./iozone -Raz -n 512m -g 8g -r 1k -y 1k -i 0 -i 1 -b ./iozone.xls

还挺慢的,搞了差不多二十分钟
alt text

其中读写速度分别为(不是很理解为什么要导出到xls文件中,内容与控制台输出是一致的):

alt text
alt text

上述数据单位为KB/s

posted @ 2025-06-05 15:25  Capache  阅读(31)  评论(0)    收藏  举报