【OS】实验六:设备管理

636 阅读3分钟

实验六:设备管理

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

参考内核源码中的drivers/usb/usb-skeleton.c文件,编写一个USB探测驱动程序,能够实现以下基本功能: (1)在插入U盘时能够探测到; (2)在拔出U盘时能够探测到;

源码

usb_detect.c

/*
 * USB Detect driver
 *
 * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
 */
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>
/* Define these values to match your devices */
#define USB_DETECT_VENDOR_ID    0x0951
#define USB_DETECT_PRODUCT_ID    0x160b
/* table of devices that work with this driver */
static const struct usb_device_id usbdetect_table[] = {
    { USB_DEVICE(USB_DETECT_VENDOR_ID, USB_DETECT_PRODUCT_ID) },
    { }                    /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbdetect_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_DETECT_MINOR_BASE    192
#define WRITES_IN_FLIGHT    8
/* arbitrarily chosen */
/* Structure to hold all of our device specific stuff */
struct usb_detect {
    struct usb_device    *udev;            /* the usb device for this device */
    struct usb_interface    *interface;        /* the interface for this device */
    struct semaphore    limit_sem;        /* limiting the number of writes in progress */
    struct usb_anchor    submitted;        /* in case we need to retract our submissions */
    struct urb        *bulk_in_urb;        /* the urb to read data with */
    unsigned char           *bulk_in_buffer;    /* the buffer to receive data */
    size_t            bulk_in_size;        /* the size of the receive buffer */
    size_t            bulk_in_filled;        /* number of bytes in the buffer */
    size_t            bulk_in_copied;        /* already copied to user space */
    __u8            bulk_in_endpointAddr;    /* the address of the bulk in endpoint */
    __u8            bulk_out_endpointAddr;    /* the address of the bulk out endpoint */
    int            errors;            /* the last request tanked */
    bool            ongoing_read;        /* a read is going on */
    spinlock_t        err_lock;        /* lock for errors */
    struct kref        kref;
    struct mutex        io_mutex;        /* synchronize I/O with disconnect */
    unsigned long        disconnected:1;
    wait_queue_head_t    bulk_in_wait;        /* to wait for an ongoing read */
};
#define to_detect_dev(d) container_of(d, struct usb_detect, kref)
static struct usb_driver usbdetect_driver;
static void usbdetect_delete(struct kref *kref)
{
    struct usb_detect *dev = to_detect_dev(kref);
    usb_free_urb(dev->bulk_in_urb);
    usb_put_intf(dev->interface);
    usb_put_dev(dev->udev);
    kfree(dev->bulk_in_buffer);
    kfree(dev);
}
static const struct file_operations usbdetect_fops = {};
/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver usbdetect_class = {
    .name =        "usbdetect%d",
    .fops =        &usbdetect_fops,
    .minor_base =    USB_DETECT_MINOR_BASE,
};
static int usbdetect_probe(struct usb_interface *interface,
              const struct usb_device_id *id)
{
    struct usb_detect *dev;
    struct usb_endpoint_descriptor *bulk_in, *bulk_out;
    int retval;
    /* allocate memory for our device state and initialize it */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
    kref_init(&dev->kref);
    sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
    mutex_init(&dev->io_mutex);
    spin_lock_init(&dev->err_lock);
    init_usb_anchor(&dev->submitted);
    init_waitqueue_head(&dev->bulk_in_wait);
    dev->udev = usb_get_dev(interface_to_usbdev(interface));
    dev->interface = usb_get_intf(interface);
    /* set up the endpoint information */
    /* use only the first bulk-in and bulk-out endpoints */
    retval = usb_find_common_endpoints(interface->cur_altsetting,
            &bulk_in, &bulk_out, NULL, NULL);
    if (retval) {
        dev_err(&interface->dev,
            "Could not find both bulk-in and bulk-out endpoints\n");
        goto error;
    }
    dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
    dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;
    dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
    if (!dev->bulk_in_buffer) {
        retval = -ENOMEM;
        goto error;
    }
    dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!dev->bulk_in_urb) {
        retval = -ENOMEM;
        goto error;
    }
    dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;
    /* save our data pointer in this interface device */
    usb_set_intfdata(interface, dev);
    /* we can register the device now, as it is ready */
    retval = usb_register_dev(interface, &usbdetect_class);
    if (retval) {
        /* something prevented us from registering this driver */
        dev_err(&interface->dev,
            "Not able to get a minor for this device.\n");
        usb_set_intfdata(interface, NULL);
        goto error;
    }
    /* let the user know what node this device is now attached to */
    dev_info(&interface->dev,
         "USB detect device now attached to USBdetect-%d",
         interface->minor);
    return 0;
error:
    /* this frees allocated memory */
    kref_put(&dev->kref, usbdetect_delete);
    return retval;
}
static void usbdetect_disconnect(struct usb_interface *interface)
{
    struct usb_detect *dev;
    int minor = interface->minor;
    dev = usb_get_intfdata(interface);
    usb_set_intfdata(interface, NULL);
    /* give back our minor */
    usb_deregister_dev(interface, &usbdetect_class);
    /* prevent more I/O from starting */
    mutex_lock(&dev->io_mutex);
    dev->disconnected = 1;
    mutex_unlock(&dev->io_mutex);
    usb_kill_anchored_urbs(&dev->submitted);
    /* decrement our usage count */
    kref_put(&dev->kref, usbdetect_delete);
    dev_info(&interface->dev, "USB detect #%d now disconnected", minor);
}
static struct usb_driver usbdetect_driver = {
    .name =        "usbdetect",
    .probe =    usbdetect_probe,
    .disconnect =    usbdetect_disconnect,
    .id_table =    usbdetect_table,
    .supports_autosuspend = 1,
};
MODULE_LICENSE("GPL v2");
static int __init usb_detect_init(void)
{
    int result;
    printk("Start usb_detect module...");
    /* register this driver with the USB subsystem */
    result = usb_register(&usbdetect_driver);
    if (result < 0) {
        printk("usb_register failed.""Error number %d", result);
        return -1;
    }
    return 0;
}
static void __exit usb_detect_exit(void)
{
    printk("Exit usb_detect module...");
    /* deregister this driver with the USB subsystem */
    usb_deregister(&usbdetect_driver);
}
module_init(usb_detect_init);
module_exit(usb_detect_exit);

Makefile

ifneq ($(KERNELRELEASE),)
    obj-m := usb_detect.o
else
    KERNELDIR ?= /root/kernel-5.10.0-13.0.0
    PWD := $(shell pwd)
default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
.PHONY:clean
clean:
    -rm *.mod.c *.o *.order *.symvers *.ko

内核模块加载结果

加载U盘检测模块

image-20211225230221839

usb接口插入U盘后,查看信息可见,模块检测到U盘插入;并且获取到的U盘信息与lsusb显示的一致

img

img

拔出U盘,查看打印信息可见U盘已经断开连接

img

img

任务2:编写内核模块测试硬盘的读写速率,并与 iozone工具的测试结果比较

  1. 编写内核模块测试硬盘的读写速率,加载、卸载模块并查看模块打印信息。

  2. 使用用户态下 iozone 工具测试硬盘的读写速率,注意:测试范围需包含与内核模块读写相同的文件大小和块大小。

  3. 对比用户态和内核态下测试的读写速率,并作分析。

内核模块测试硬盘的写速率

write_to_disk.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/rtc.h>
#include <linux/time.h>
#include <linux/timekeeping.h>

#define buf_size 1024
#define write_times 524288

MODULE_LICENSE("GPL");

struct timespec64 tv;

static int __init write_disk_init(void)
{
	struct file *fp_write;
	char buf[buf_size];
	int i;
	int write_start_time;
	int write_start_time_u;
	int write_end_time;
	int write_end_time_u;
	int write_time;
	loff_t pos;
	printk("Start write_to_disk module...\n");
	for(i = 0; i < buf_size; i++)
	{
		buf[i] = i + '0';
	}
	fp_write = filp_open("/home/tmp_file", O_RDWR | O_CREAT,0644);
	if (IS_ERR(fp_write)) {
		printk("Failed to open file...\n");
		return -1;
	}
	pos = 0;
	ktime_get_real_ts64(&tv);
	write_start_time = (int)tv.tv_sec;
	write_start_time_u = (int)(tv.tv_nsec/1000);
	for(i = 0; i < write_times; i++) {
		kernel_write(fp_write, buf, buf_size, &pos);
	}
	ktime_get_real_ts64(&tv);
	write_end_time = (int)tv.tv_sec;
	write_end_time_u = (int)(tv.tv_nsec/1000);
	filp_close(fp_write, NULL);
	write_time = (write_end_time - write_start_time)  * 1000000 + (write_end_time_u - write_start_time_u);
	printk(KERN_ALERT "Writing to file costs %d us\n", write_time);	
	printk("Writing speed is %d M/s\n", buf_size * write_times / write_time);
	return 0;
}

static void __exit write_disk_exit(void)
{
	printk("Exit write_to_disk module...\n");
}

module_init(write_disk_init);
module_exit(write_disk_exit);

Makefile

ifneq ($(KERNELRELEASE),)
	obj-m := write_to_disk.o
else
	KERNELDIR ?= /root/kernel-5.10.0-13.0.0
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
.PHONY:clean
clean:
	-rm *.mod.c *.o *.order *.symvers *.ko

测试硬盘的写速率

image-20211225232617622

(华为云的硬盘读写速度好快)

内核模块测试硬盘的读速率

read_to_disk.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/rtc.h>
#include <linux/time.h>
#include <linux/timekeeping.h>

#define buf_size 1024
#define read_times 524288

MODULE_LICENSE("GPL");

struct timespec64 tv;

static int __init read_disk_init(void)
{
	struct file *fp_read;
	char buf[buf_size];
	int i;
	int read_start_time;
	int read_start_time_u;
	int read_end_time;
	int read_end_time_u;
	int read_time;
	loff_t pos;
	printk("Start read_from_disk module...\n");
	fp_read = filp_open("/home/tmp_file", O_RDONLY, 0);
	if (IS_ERR(fp_read)) {
		printk("Failed to open file...\n");
		return -1;
	}

	ktime_get_real_ts64(&tv);
	read_start_time = (int)tv.tv_sec;
	read_start_time_u = (int)(tv.tv_nsec/1000);
	pos = 0;
	for(i = 0; i < read_times; i++)	{
		kernel_read(fp_read, buf, buf_size, &pos);
	}
	ktime_get_real_ts64(&tv);
	read_end_time = (int)tv.tv_sec;
	read_end_time_u = (int)(tv.tv_nsec/1000);
	filp_close(fp_read, NULL);
	read_time = (read_end_time - read_start_time)  * 1000000 + (read_end_time_u - read_start_time_u);
	printk(KERN_ALERT "Read file costs %d us\n", read_time);	
	printk("Reading speed is %d M/s\n", buf_size * read_times / read_time);
	return 0;
}

static void __exit read_disk_exit(void)
{
	printk("Exit read_from_disk module...\n");
}

module_init(read_disk_init);
module_exit(read_disk_exit);

Makefile

ifneq ($(KERNELRELEASE),)
	obj-m := read_to_disk.o
else
	KERNELDIR ?= /root/kernel-5.10.0-13.0.0
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
.PHONY:clean
clean:
	-rm *.mod.c *.o *.order *.symvers *.ko

测试硬盘的读速率

image-20211225233805193

iozone工具的测试结果

iozone.sh

#/bin/bash

wget http://www.iozone.org/src/current/iozone3_489.tar
tar -xvf iozone3_489.tar 
cd iozone3_489
cd src/current/
make linux-AMD64
./iozone -Raz -n 512m -g 32g -y 32k -i 0 -i 1 -b /home/iozone.xls

image-20211226172711535

image-20211226172721218

image-20211226172730501

image-20211226172738107