【OS】操作系统-实验四:驱动程序的编写

操作环境:

  • 主机:MacOS 10.13.3
  • 虚拟机软件:Parallels Desktop
  • Linux:Ubuntu 16.04 Kernal 4.13.0-36-generic

实验要求:

  1. 编写一个字符设备驱动程序,要求实现对该字符设备的打开、读、写、I/O控制和关闭 5 个基本操作。为了避免牵涉到汇编语言,这个字符设备并非一个真实的字符设备,而是用一段内存空间来模拟的。以模块方式加载该驱动程序。

  2. 编写一个应用程序,测试(1)中实现的驱动程序的正确性。

  3. 有兴趣的读者还可以编写一个块设备的驱动程序。

  4. 请根据自身情况,进一步阅读分析程序中用到的相关内核函数的源码实现。

操作说明:

  • 储存空间设计:

    • 一个char类型的指针作为字符设备
    • 由于无法使用malloc()函数,所以空间变化使用一大一小两个数组暴力表示
    • 小空间大小为32字节,大空间大小为64字节,用于测试ioctl()
  • 五个操作函数的实现:

    1. OPEN:
      • dmesg输出 char_dev device open
    2. WRITE:
      • dmesg输出 char_dev device is write!
      • 将信息拷贝给用户
    3. READ:dmesg输出 char_dev device is read!
      • 从用户拷贝信息到数组
    4. RELEASE:
      • dmesg输出 char_dev device release!
    5. IOCTL:
      • cmd = 0: dmesg输出 char_dev device received cmd 0!
      • cmd = 1: 换一个大一点的储存空间
      • 其他:dmesg输出 No such command!
  • 建立设备节点:# mknod /dev/aeonnicdev 666 0

  • 测试命令:$ make && sudo insmod aeonni_Cdriver.ko && gcc test.c -o test && ./test && sudo rmmod aeonni_Cdriver

  • 测试结果: result.png

附录:

  1. 驱动内核模块代码 aeonni_Cdriver.c
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

#define DEV_MAJOR 666
#define DEV_MINOR 0
#define aeonni_count 1

static char mem1[32];
static char mem2[64] ;

static char *mem = mem1;


static struct cdev my_cdev;

static int char_dev_open(struct inode *inode, struct file *filp) {
    printk("char_dev device open! \n");
    return 0;
}

ssize_t char_dev_read(struct file *file, char __user *buff, size_t count, loff_t *offp) {
    printk("char_dev device is read! \n");
    copy_to_user(buff, mem, count);
    return count;
}

ssize_t char_dev_write(struct file *file, const char __user *buff, size_t count, loff_t *offp) {
    printk("char_dev device is write! \n");
    copy_from_user(mem, buff, count);
    return count;
}

static long char_dev_ioctl(struct file *flip, unsigned int cmd, unsigned long arg) {
switch(cmd) {
    case 0: printk("char_dev device received cmd 0! \n"); break;
    case 1: mem = mem2; printk("Mem growed! \n"); break;
    default: printk("No such command! \n");
}
}

static int char_dev_release(struct inode *inode, struct file *file) {
    printk("char_dev device release! \n");
    return 0;
}


static struct file_operations aeonni_fops =   
{  
  .owner = THIS_MODULE,  
  .open = char_dev_open,  
  .release = char_dev_release,
  .read = char_dev_read,  
  .write = char_dev_write,  
  .unlocked_ioctl = char_dev_ioctl,
};

static void __exit aeonni_cdev_exit(void) {  

    printk("Exit aeonni_cdev!\n"); 
  
    cdev_del(&my_cdev);  

    dev_t aeonni_dev_t = MKDEV(DEV_MAJOR, DEV_MINOR);  
    unregister_chrdev_region(aeonni_dev_t, aeonni_count);  
  
}  

static int __init aeonni_cdev_init(void) {

    printk("Init aeonni_cdev!\n");  

    dev_t aeonni_dev_t = MKDEV(DEV_MAJOR, DEV_MINOR);  

    register_chrdev_region(aeonni_dev_t, aeonni_count, "aeonni_cdev");

    cdev_init(&my_cdev, &aeonni_fops);
    my_cdev.owner = THIS_MODULE;

    // registe cdev struct
    cdev_add(&my_cdev, aeonni_dev_t, aeonni_count);
    return 0;
}

module_init(aeonni_cdev_init);
module_exit(aeonni_cdev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Genius.AEONNI.COM");
MODULE_DESCRIPTION("This module is for the Exercise5.");

  1. 测试代码 test.c
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <errno.h>  
#include <fcntl.h> 
#include <sys/types.h>  
#include <sys/stat.h>  
#include <sys/syscall.h> 
#include <sys/ioctl.h> 

char str[100];

int main() {
    int fp = 0;
    fp = open("/dev/aeonnicdev",O_RDWR); 
    printf("%d\n",fp);
    read(fp, str, 10);
    printf("%s\n",str);
    write(fp,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",52);
    read(fp, str, 10);
    ioctl(fp,1);
    write(fp,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",52);
    read(fp, str, 10);
    printf("%s\n",str);
    ioctl(fp,0);
    ioctl(fp,2);
    close(fp);

    return 0;
}