logo资料库

操作系统课设 linux简单字符设备驱动.pdf

第1页 / 共10页
第2页 / 共10页
第3页 / 共10页
第4页 / 共10页
第5页 / 共10页
第6页 / 共10页
第7页 / 共10页
第8页 / 共10页
资料共10页,剩余部分请下载后查看
将后面附上的3个源程序放在一个文件夹中后(也可直接打包下载http://download.csdn.net/detail/creazyapple/4088141)
,开始进行实验:
1.编译驱动程序.
2.  装载模块。
3.分配次设备号。
4.测试驱动程序。
5.删除设备、模块。
源代码附上。共三个文件,将其放在一个文件夹中
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 实验二 驱动程序 要求:掌握添加设备驱动程序的方法 内容: 采用模块方法,添加一个新的设备驱动程序。 要求添加字符设备的驱动。 编写一个应用程序,测试添加的驱动程序。 linxu 系统中,在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件 设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 设备分为两种:字符设备和块设备,这里就以简单的字符设备为例。 在设备驱动程序中有一个非常重要的结构 file_operations,该结构的每个域都对应着一个系统调用。用户 进程利用系统调用在对设备文件进行诸如 read/write 操作时,系统调用通过设备文件的主设备号找到相应 的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。 struct file_operations { , off_t ,int); , char ,int); , off_t ,int); int(*seek) (struct inode * ,structfile * int (*read) (struct inode * ,structfile * int (*write) (struct inode * ,structfile * …… } 编写设备驱动程序的主要工作是编写子函数,并填充 file_operations 的各个域。 例如: Structfile_operations my_fops={ .read=my_read, .write=my_write, .open=my_open, .release=my_release } 然后再定义函数 my_read,my_write,my_open,my_release 相应的函数体。 例如:
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 staticssize_t my_open(struct inode *inode,struct file *file){ staticint counter=0; if(Device_Open) return-EBUSY; Device_Open++; /*写入设备的信息*/ sprintf(msg,"the device hasbeen called %d times\n",counter++); msg_ptr=msg; return0; } 同时对于可卸载的内核模块(LKM),至少还有两个基本的模块: my_init 用于注册设备,获得设备的主设备号 调用 register_chrdev(0,“sky_driver(设备名)”,&my_fops); my_exit 用于注销设备 调用 unregister_chrdev(my_major,“sky_driver(设备名)”); 然后在程序尾再调用这两个函数 Module_init(my_init); Module_exit(my_exit) MODULE_LICENSE(“GPL”); 不过如果使用特殊函数名 init_module 和 cleanup_module, 就不用在程序最后加上上面那三行代码了。 废话少说,以下是实验过程: 首先编写驱动程序。 将后面附上的 3 个源程序放在一个文件夹中后(也可直接打包下载 http://download.csdn.net/detail/creazyapple/4088141) ,开始进行实验: 1.编译驱动程序. 在控制台下进入文件所在目录,输入命令 make。如图:
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 看到了吗?提示错误。原来需要用超级用户权限。 没有错误提示,说明 make 成功,进入文件夹下可以看到多了一些.k,.ko 之类的文件。这就是编译好的 模块。 2. 装载模块。 同样,必须要是 root 下才能进行。装载前,我们可以先看看系统中的模块:lsmod 装载,输入命令: insmod devDrv.ko 完成后,再看看系统中的模块,发现多了一个模块 devDrv”,表示我们成功加载了! “ 可以用命令查看系统日志信息:dmesg 看到如下信息: 这正是在我们的驱动程序中注册函数输出的内容哦!
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 3.分配次设备号。 每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设 备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号 必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.。 分配前,我们必须要知道主设备号(其实在上一步中我们从 dmesg 中已经看到主设备好为 250 cat /proc/devices ): 可见到这一行:250 myDevice,是了,主设备号为 250. 现在我们分配从设备号:mknod /dev/myDevice c 250 0 实际上就是在虚拟文件夹/dev/中加一个设备(在系统看来是文件)myDevice ,注意,这个是设备名,在 测试文件中真是要利用这个设备名打开设备。最后的 0 表示从设备号。可以随便分配(只要不冲突)。 此后便可以在/dev/目录下看到新建的设备 myDevice 了:ls /dev/ 一切成功的话,就可以测试我们的驱动程序啦! 4.测试驱动程序。 首先要编译测试程序:gcc drvTest.c -o sb 得到 sb 可执行程序,再执行 :./sb 测试程序首先列出所有的设备名,让我们选中一个,当然输入 myDevice 啦: 正确读出之前存放在设备中的字符串!然后提示让我们输入一个字符串,随便啦:
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 最后是不是正确保存、读出了呢?of course !而且还告诉我们调用了几次这个设备呢! 5.删除设备、模块。 首先删除设备。就像删除普通文件一样:rm /dev/myDevice 删除后,看看/dev/目录下是佛已经木有 myDevice 啦?:ls /dev/ 接着删除模块:rmmod devDrv. 看看模块列表中是否已经没有 devDrv 模块了?:lsmod 在本次实验中,还特别添加了一些代码来给设备上锁,实现互斥,以免同时有多个进程读取设备造成混 乱。(一般来说,字符设备如打印机是不能共享滴。。)观察驱动程序 devDrv.c 中两个函数 static int my_open(struct inode *inode, struct file *file) { if(mutex)
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 return -EBUSY; mutex = 1;//上锁 printk("<1>main device : %d\n", MAJOR(inode->i_rdev)); printk("<1>slave device : %d\n", MINOR(inode->i_rdev)); printk("<1>%d times to call the device\n", ++counter); try_module_get(THIS_MODULE); return 0; } /* 每次使用完后会 release */ static int my_release(struct inode *inode, struct file *file) { printk("Device released!\n"); module_put(THIS_MODULE); mutex = 0;//开锁 return 0; } int 型 mutex 就是用来上锁的,它被初始化为 0,一旦使用该设备,就首先要调用 my_open 函数,将 mutex 设置为 1,如果此后还想使用该设备,必然因为 mutex 为 1 而返回-EBUSY,而终止。使用完该设 备后,调用 my_release 释放设备,将 mutex 重新设回 0. 特别注意,还有一个 counter 变量,使用来计量使用次数的。 makefile 的格式相当严格,比如不能用 8 个空格来代替一个 table 键等。 makefile 最后有一句: devDrv 一定要对应我们要编译的驱动程序 devDrv.c 的文件名。 源代码附上。共三个文件,将其放在一个 文件夹中 devDrv.c 1. 驱动程序 #include "linux/kernel.h" #include "linux/module.h" #include "linux/fs.h" #include "linux/init.h" #include "linux/types.h" #include "linux/errno.h" #include "linux/uaccess.h" #include "linux/kdev_t.h"
#define MAX_SIZE 1024 华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 static int my_open(struct inode *inode, struct file *file); static int my_release(struct inode *inode, struct file *file); static ssize_t my_read( *f); static ssize_t my_write( loff_t *f); const struct file *file, struct file *file, char char __user *user, size_t t, loff_t __user *user, size_t t, static char message[MAX_SIZE] = "-------congratulations--------!"; static int device_num = 0;//设备号 static int counter = 0;//计数用 static int mutex = 0;//互斥用 static char* devName = "myDevice";//设备名 struct file_operations pStruct = { open:my_open, release:my_release, read:my_read, write:my_write, }; 注册模块 */ /* int init_module() { int ret; /* 函数中第一个参数是告诉系统,新注册的设备的主设备号由系统分配, * 第二个参数是新设备注册时的设备名字, * 第三个参数是指向file_operations的指针, * 当用设备号为0 ret = register_chrdev(0, devName, &pStruct); if (ret < 0) { 创建时,系统一个可以用的设备号创建模块 */ printk("regist failure!\n"); return -1; } else { printk("the device has been registered!\n"); device_num = ret; printk("<1>the virtual device's major number %d.\n", device_num); printk("<1>Or you can see it by using\n"); printk("<1>------more /proc/devices-------\n"); printk("<1>To talk to the driver,create a dev file with\n"); printk("<1>------'mknod /dev/myDevice c %d 0'-------\n", device_num); printk("<1>Use \"rmmode\" to remove the module\n"); return 0; } 注销模块,函数名很特殊 */ } /* void cleanup_module() { unregister_chrdev(device_num, devName); printk("unregister it success!\n"); } static int my_open(struct inode *inode, struct file *file) { if (mutex) return -EBUSY; mutex = 1;//上锁 printk("<1>main device : %d\n", MAJOR(inode->i_rdev));
华中科技大学 计算机 0907 华中科技大学 计算机 0907 朱胜本朱胜本 printk("<1>slave device : %d\n", MINOR(inode->i_rdev)); printk("<1>%d times to call the device\n", ++counter); try_module_get(THIS_MODULE); return 0; } /* 每次使用完后会release */ static int my_release(struct inode *inode, struct file *file) { printk("Device released!\n"); module_put(THIS_MODULE); mutex = 0;//开锁 return 0; } static *f) { if (copy_to_user(user,message, { return -EFAULT; } return } sizeof (message); static ssize_t my_write( loff_t *f) { if (copy_from_user(message,user, { return -EFAULT; } return } sizeof (message); ssize_t my_read(struct file *file, char __user *user, size_t t, loff_t sizeof (message))) const struct file *file, char __user *user, size_t t, sizeof (message))) 2.makefile 文件。注意,文件名就叫 makefile 或者 Makefile,大小写不可随意更改,关于 makefile 的编 写,参考 http://wiki.ubuntu.org.cn/index.php?title=%E8%B7%9F %E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:MakeFile%E4%BB%8B%E7%BB %8D&variant=zh-cn # If KERNELRELEASE is defined, we've been invoked from the # kernel build system and can use its language. ifeq ($(KERNELRELEASE),) # Assume the source tree is where the running kernel was built # You should set KERNELDIR in the environment if it's elsewhere KERNELDIR ?= /lib/modules/$(shell uname -r)/build # The current directory is passed to sub-makes as argument PWD := $(shell pwd) modules: modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
分享到:
收藏