实验要求:编写简单的字符设备驱动模块,能够支持建立和删除节点,节点进行读写操作时分别打印“You are reading!”和“You are writing!”。
思想整理:
1. 本实验需要我们编写一个驱动程序,如mymodule.c
2. 该驱动程序经编译后生成.ko文件,使用Makefile文件生成mymodule.ko
3. 可以挂在到系统上 insmod mymodule.ko
4. 然后通过其主设备号将其生成一个节点 mknod mymodule0 c 249 0
5. 对mumodule0进行读写操作 cat mymodule0 或 ls > mymodule
实验原理:
1. mymodule.c的组成
(1) my_init_module()函数
当使用insmod命令时,系统会调用程序的module_init()函数,这样也就调用了我们写的my_init_module()函数。
而我们应当在my_init_module()中完成的操作有以下这些:
注册设备,注册的目的是注册了自己写的这些file operations,如read,write等,而不是用原来VFS的read,write函数同时以后可以使用主设备号来使用该模块。
注册的语句是:
Major = register_chrdev(0,”chardev”,&my_fops),解释一下参数
第一个0表示让系统帮我分配主设备号,你也可以自己写,但是这样你还得查一下系统中有空的主设备号。
第二个参数是说明这是一个字符设备驱动,不用改
第三个就是我们用file_operations定义的文件操作结构体的引用
获取主设备号,输出一下主设备号,这样你在mknod的时候就可以找到这个设备了。
(2) my_cleanup_module()函数
另一个必须有的函数是module_exit,这个函数在我们rmmod时会被调用,而这个函数又同时调用了我们自己写的函数,所以如果我们在my_cleanup module函数中写printk(“goodbye”),那么在用rmmod时他就输出了。
在my_cleanup_module我们还有一个必要的事要做,把刚刚注册的设备给注销掉,命令是unregister_chrdev(Major,”chardev”);
(3) struct file_operations my_fops{};结构体
定义了这个结构体后,这个file operations就会替换原来虚拟文件系统的file operations,具体的方法很简单,仿照例子即可,唯一需要注意的是顺序必须按照原本file operations的顺序。
struct file_operations my_fops={
.open=my_open,
.release=my_release
};
(4) 根据你要重定义的操作,也就是在file operations结构体里面写的那几个,相应的写他们的函数。
tips:在设备打开时,会用到一个try_module_get函数,这个函数的目的是让系统知道这个模块又被用了一次,只有系统确实知道我们的模块被用了多少次,模块在注销时才不会出错,同理,用module_put函数,可以跟系统说,我的某一次调用已经结束了!!
2.Makefile的编写,由于这个实验编译的是模块,所以需要用到内核里的Makefile文件,具体怎么编译的还没有弄清楚,但是Makefile的代码是这样的
|
代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
/*函数声明*/
int my_init_module(void);
void my_cleanup_module(void);
static int my_open(struct inode *, struct file *);
static int my_release(struct inode *, struct file *);
static ssize_t my_read(struct file *, char *, size_t, loff_t *);
static ssize_t my_write(struct file *, const char *, size_t, loff_t *);
#define SUCCESS 0
#define DEVICE_NAME "chardev"
#define BUF_LEN 80
static int Major;
static int Device_Open = 0;
struct file_operations my_fops={
.read=my_read,
.write=my_write,
.open=my_open,
.release=my_release
};
int my_init_module(void){ /*init module*/
printk("Hello! It is from mymodule /n");
Major = register_chrdev(0,DEVICE_NAME,&my_fops);
if(Major){
printk("major is %d /n",Major);
}
else{
printk("Registeration failed! /n");
}
return 0;
}
void my_cleanup_module(void){
unregister_chrdev(Major,DEVICE_NAME);
printk("mymodule says Good Bye to you!/n");
}
static int my_open(struct inode *inode,struct file *file){
/*The device can be used by more than one process*/
/*Everytime one process open the device*/
/*this device will call the try_module_get function */
/*to increase the use count*/
if(Device_Open)
return -EBUSY;
Device_Open++;
try_module_get(THIS_MODULE);
return SUCCESS;
}
static int my_release(struct inode *inode,struct file *file){
Device_Open--;
module_put(THIS_MODULE); //decreasing use count differs to increasing
return 0;
}
static ssize_t my_read(struct file *flip,char *buffer,size_t length,loff_t *offset){
printk("You are reading!");
return 0;
}
static ssize_t my_write(struct file *flip,const char *buff,size_t len,loff_t *off){
printk("You are writing!");
return -EINVAL;
}
MODULE_LICENSE("GPL");
module_init(my_init_module);
module_exit(my_cleanup_module);