【OS】操作系统-实验四:驱动程序的编写
操作环境:
- 主机:MacOS 10.13.3
- 虚拟机软件:Parallels Desktop
- Linux:Ubuntu 16.04 Kernal 4.13.0-36-generic
实验要求:
编写一个字符设备驱动程序,要求实现对该字符设备的打开、读、写、I/O控制和关闭 5 个基本操作。为了避免牵涉到汇编语言,这个字符设备并非一个真实的字符设备,而是用一段内存空间来模拟的。以模块方式加载该驱动程序。
编写一个应用程序,测试(1)中实现的驱动程序的正确性。
有兴趣的读者还可以编写一个块设备的驱动程序。
请根据自身情况,进一步阅读分析程序中用到的相关内核函数的源码实现。
操作说明:
储存空间设计:
- 一个char类型的指针作为字符设备
- 由于无法使用malloc()函数,所以空间变化使用一大一小两个数组暴力表示
- 小空间大小为32字节,大空间大小为64字节,用于测试ioctl()
五个操作函数的实现:
- OPEN:
- dmesg输出
char_dev device open
- dmesg输出
- WRITE:
- dmesg输出
char_dev device is write!
- 将信息拷贝给用户
- dmesg输出
- READ:dmesg输出
char_dev device is read!
- 从用户拷贝信息到数组
- RELEASE:
- dmesg输出
char_dev device release!
- dmesg输出
- IOCTL:
- cmd = 0: dmesg输出
char_dev device received cmd 0!
- cmd = 1: 换一个大一点的储存空间
- 其他:dmesg输出
No such command!
- cmd = 0: dmesg输出
- OPEN:
建立设备节点:
# mknod /dev/aeonnicdev 666 0
测试命令:
$ make && sudo insmod aeonni_Cdriver.ko && gcc test.c -o test && ./test && sudo rmmod aeonni_Cdriver
测试结果:
附录:
- 驱动内核模块代码
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.");
- 测试代码
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;
}