|
一、程序的基本构架如下:
1、头文件包含、宏定义
2、设备结构体(一般都包括cdev结构体与设备的其他参数)
3、用户接口定义(打开文件,关闭文件,写文件,读文件,定位文件等等)
4、用户接口结构体(struct file_operations)
5、加载模块
(1)申请结构体的内存
(2)初始化cdev以及设备的其他参数(cdev_init( ),register_chrdev_region( )静态申请设备号,alloc_chrdev_region( )动态申请设备号)
(3)向内核添加cdev(cdev_add( ))
6、卸载模块
(1)注销cdev(cdev_del( ))
(2)释放设备结构体的内存
(3)释放设备号(unregister_chrdev_region( ))
7、其他声明(MODULE_AUTHOR( )声明作者、MODULE_LICENSE( )声明证书等等)
二、以下就解释一下主要的几部分
1、设备结构体
/*globalmem设备结构体*/struct globalmem_dev{struct cdev cdev;/*cdev结构体*/unsigned char mem[GLOBALMEM_SIZE];/*全局内存*/struct semaphore sem/*并发控制用的信号量*/};一般设备结构体都会包含(1)cdev结构体,这是内核结构体,包含了设备号、文件操作结构体、所属模块等数据项(2)操控设备过程中要用到的一些变量或存储空间,比如并发控制需要用到的信号量或自旋锁、支持阻塞操作需要用到的阻塞等待队列头等等
2、用户接口结构体
/*file operation struct*/static const struct file_operations globalmem_fops={.owner=THIS_MODULE,.llseek=globalmem_llseek,.read=globalmem_read,.write=globalmem_write,.ioctl=globalmem_ioctl,.open=globalmem_open,.release=globalmem_release,};
用户程序主要是为用户服务,而驱动程序主要是为用户程序服务。因为用户程序是通过系统调用来实现某项功能,而驱动程序正是为用户程序提供这些系统调用。用户程序对一个设备的操作一般都不止一个,比如说用户程序对字符设备的操作有打开、定位、读、写、关闭等,而这么多的操作,它们对应的程序到底在哪里呢?所以需要一个(struct file_operations)用户接口结构体来管理着,这个结构体的每一项都是一个函数指针,都指向一个操作函数程序,用户如果需要对该设备进行什么操作,只要通过该结构体就可以找到该操作对应的程序在哪里。
3、加载模块
/*初始化并注册cdev*/static void globalmem_setup_cdev(struct globalmem_dev *dev,int index){int err,devno=MKDEV(globalmem_major,index);cdev_init(&dev->cdev,&globalmem_fops);dev->cdev.owner=THIS_MODULE;err=cdev_add(&dev->cdev,devno,1);if(err)printk(KERN_INFO "Error %d adding globalmem %d",err,index);}/*设备驱动模块加载函数*/int globalmem_init(void){int result;dev_t devno=MKDEV(globalmem_major,0);/*申请设备号*/if(globalmem_major)result=register_chrdev_region(devno,1,"globalmem");else{/*动态申请设备号*/result=alloc_chrdev_region(&devno,0,1,"globalmem");globalmem_major=MAJOR(devno);}if(result<0)return result;/*动态申请结构体的内存*/globalmem_devp=kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);if(!globalmem_devp){/*申请失败*/result=-ENOMEM;goto fail_malloc;}memset(globalmem_devp,0,sizeof(struct globalmem_dev));globalmem_setup_cdev(globalmem_devp,0);init_MUTEX(&globalmem_devp->sem);/*初始化信号量*/return 0;fail_malloc:unregister_chrdev_region(devno,1);return 0;}
一般加载模块的主要工作有二:
(1)初始化设备结构体,即初始化cdev结构体和操控设备过程中要用到的一些变量或存储空间,不过在初始化cdev结构体之前,你需要通过静态或动态的方式申请号主设备号;
(2)向系统添加该设备,系统会对cdev和设备号有个登记的,这样用户程序才能通过系统来使用该设备。
4、卸载模块
/*模块卸载函数*/void globalmem_exit(void){cdev_del(&globalmem_devp->cdev);/*注销cdev*/kfree(globalmem_devp);/*释放设备结构体内存*/unregister_chrdev_region(MKDEV(globalmem_major,0),1);/*释放设备号*/}
卸载模块主要是对设备结构做善后处理,释放设备结构体的内存,不过释放前要先注销cdev,释放后要释放设备号,因为这两者在你加载进去的时候,系统都有记录的。比如小车开进某个小区时,需要要登记你的车牌号表示你进入该小区,离开时你需要让门卫将你的车牌号从登记表上划掉,表示你离开了该小区。
5、出入口
module_init(globalmem_init);module_exit(globalmem_exit);
用户程序都会使用main函数来告知系统该程序应该从哪里执行,驱动程序同样也有,它是通过module_init和module_exit来告知系统应该从哪里进入,从哪里出来。
三、完整代码会在《并发控制》那一篇blog贴出
|
|
|