一、概念
1)内核空间和用户空间
2)Kernel Symbot Table
A:The table contains the address of global kernel itmes---functions and variables---that are needed to implement modularized drivers.
B:When a module is loaded, any symbol exported by the module becomes part of the kernel symbo table. Using this technology,we can stack new modules on top of other modules.
C:If your modules needs to export symbol for other modules to use, the following macros should be used, EXPORT_SYMBOL(name),or EXPORT_SYMBOL_GPL(name).
二、内核模块hello world模型
1)必须的头文件(第三版)
#include <linux/init.h> (lld2提供的例子并没包含该头文件)
#include <linux/module.h>
2)init/clean接口
- #include <linux/module.h>
- #include <linux/sched.h>
- int init_module(void)
- {
- EXPORT_NO_SYMBOLS;
- printk("The process is /"%s/" (pid %i)/n",current->comm,current->pid);
- return 0;
- }
- void cleanup_module(void) { printk("<1>Goodbye cruel world/n"); }
3)编译
使用例子里的Makefile文件,或者直接用GCC命令gcc -D __KERNEL__ -D MODULE -I /usr/src/linux/include/ -c hello.c - o hello.o 特别的是没有包含宏MODULE时,能编译过,但加载时老提示与现有内核版本不一致。书上说只用包含了头文件<linux/module.h>包含了该宏,但2.4.34内核该文件似乎没包含该宏。
4)必要的考虑(或技巧)
A:初始化过程中出错处理
初始化过程中出错,则需仔细回滚已初始化过的步骤,通常goto语句是个不错的选择。(没亲手体验过)
B:使用资源
申请、释放I/O端口和I/O内存(/proc/ioports, /proc/iomem),使用request_region,release_region,request_mem_region,release_mem_regon,其中错误处理机制可采用goto语句。
C:竞争机制
使用信号量
三、简单字符型设备驱动模型(SCULL)
1)主、次设备号
主设备号标识设备对应的驱动程序,内核使用主设备号在open操作中将设备与相应驱动程序(提供方法)对应起来。次设备号,内核不使用,交由驱动程序区分所管理下的“设备们”。几个相关的宏:MAJOR(dev_t dev),MINOR(dev_t dev),MKDEV(int major, int minor)。
2)分配主设备号
int register_chrdev(unsinged int major,const char *name);
int unregister_chrdev(unsinged int major,const char *name)
当注册函数的major参数为0时,是指由内核动态分配主设备号(似乎从255开始递减地分配)。动态分配的缺点是事先无法得知设备的major号,从而难创建设备节点文件,但这个可通过读/proc/device解决。
3)三个重要的结构
A:struct file_operations
Linux内核面向对象化编程的例证,它将文件看作一个“对象”,操作它的函数是“方法”。f_op就是方法的集合。
B:struct file
它代表一个打开的文件,由内核在open时创建,并传递给在该文件上进行操作的所有函数(方法),直到最后的close函数,内核才会释放该数据结构。该结构包含*file_operations,f_pos(文件当前位置)和*private_data(open系统调用在调研驱动程序的open方法前,将这个指针置为NULL,留待驱动程序分配。)
C:struct inode
It is different from the file structure that represents an open file descriptor.There can be numerous file structures representing multiple open descriptors on a single file,but they all point to a signle inode structure.
dev_t i_rdev; sturct cdev *i_cdev;
4)open/close方法
引用《linux device driver》(本文全部摘自该书,呵呵),The open method is provided for a driver to do any initialization in preparation for later operations。由于scull是个全局持久的内存设备,因此它在open方法是没有检查硬件就绪等常规检查,而是识别次设备号,更新f_op指针,并配置filp->private_data。
虽然在scull_init_module中的 result = register_chrdev(scull_major, "scull", &scull_fops);注册的方法函数集是scull_fops,但在open方法中,根据次设备号更新了fops。
- int scull_open(struct inode *inode, struct file *filp)
- {
- Scull_Dev *dev; /* device information */
- int num = NUM(inode->i_rdev);
- int type = TYPE(inode->i_rdev);
- /*
- * the type and num values are only valid if we are not using devfs.
- * However, since we use them to retrieve the device pointer, we
- * don't need them with devfs as filp->private_data is already
- * initialized
- */
- /*
- * If private data is not valid, we are not using devfs
- * so use the type (from minor nr.) to select a new f_op
- */
- if (!filp->private_data && type) {
- if (type > SCULL_MAX_TYPE) return -ENODEV;
- filp->f_op = scull_fop_array[type];
- return filp->f_op->open(inode, filp); /* dispatch to specific open */
- }
- /* type 0, check the device number (unless private_data valid) */
- dev = (Scull_Dev *)filp->private_data;
- if (!dev) {
- if (num >= scull_nr_devs) return -ENODEV;
- dev = &scull_devices[num];
- filp->private_data = dev; /* for other methods */
- }
- MOD_INC_USE_COUNT; /* Before we maybe sleep */
- /* now trim to 0 the length of the device if open was write-only */
- if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
- if (down_interruptible(&dev->sem)) {
- MOD_DEC_USE_COUNT;
- return -ERESTARTSYS;
- }
- scull_trim(dev); /* ignore errors */
- up(&dev->sem);
- }
- return 0; /* success */
- }
- int scull_release(struct inode *inode, struct file *filp)
- {
- MOD_DEC_USE_COUNT;
- return 0;
- }
5)write/read方法
没看太懂。
6)测试程序
编写了一个小测试程序用于验证scull模块中的open,write,read方法。
- #include<fcntl.h>
- #include<sys/stat.h>
- #include<string.h>
- void main()
- {
- int file,file2,ret;
- char *pp="hello pp";
- char recv[100];
- int len = strlen(pp)+1;
- file = open("/dev/scull0",O_RDWR);
- if(file < 0)
- printf("open fail/n");
- else
- printf("open success/n");
- ret = write(file,pp,len);
- if(ret < 0)
- printf("write fail/n");
- else
- printf("write success/n");
- ret = close(file);
- if(ret < 0)
- printf("close fail/n");
- #if 1
- file2 = open("/dev/scull0",O_RDWR);
- ret = read(file2,recv,len);
- if(ret < 0)
- printf("read fail/n");
- else
- printf("%s/n",recv);
- #endif
- ret = close(file2);
- if(ret < 0)
- printf("close fail/n");
- }