Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2132215
  • 博文数量: 288
  • 博客积分: 10594
  • 博客等级: 上将
  • 技术积分: 3469
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-27 19:27
文章分类

全部博文(288)

文章存档

2012年(4)

2011年(30)

2010年(40)

2009年(32)

2008年(71)

2007年(79)

2006年(32)

分类: LINUX

2009-02-20 15:41:17

 

/*scull.h*/

/*main.c*/

/*=========================================*/

/*头文件相关定义*/

1.定义scull_dev结构体用来描述scull设备储存结构

/*主函数流程分析*/

1.在程序的开始处使用module_param定义模块参数

2.初始化模块module_init(scull_init_module){

     scull_init_module分析:

     (1)获得内核识别的设备号若动态分配则调用alloc_chrdev_region,若已知则调用register_chrdev_region

     (2)使用kmalloc动态的给scull_dev设备结构体在普通内核内存空间分配内存空间,并使用memset初始化为0

     (3)初始化每个设备的访问区块(即内存区块),设定量子和量子集,互斥锁,调用scull_setup_cdev向内核注册这个char device

         scull_setup_cdev注册char设备的流程:

         a.调用MKDEV由实参生成具体设备的设备号

         b.初始化cdev,主要是指定其ops cdev_init(&dev->cdev, &scull_fops).dev->cdev在设备结构体中定义.

              关于定义设备的file_operations fops.设备的访问等同与文件,所以这里需要具体指定打开文件时候的操作.

              struct file_operations scull_fops = {

              .owner =    THIS_MODULE,

              .llseek =   scull_llseek,

              .read =     scull_read,

              ...};/*注意这里标记化结构体初始化的语法*/

         c.cdev的初始化,指定所有者 dev->cdev.owner = THIS_MODULE;

         d.注册cdev cdev_add (&dev->cdev, devno, 1);

     (4)初始化其他相关友好的设备/*这里是指pipe和有open control机制的设备实现,本次不学习*/

     (5)调用scull_create_proc建立通过proc文件的debug机制需要的proc文件

     /*注意:这里使用goto的方式对每步骤出现的错误进行恢复*/

     调用scull_cleanup_module进行上述步骤的错误退出.

}

 

3.退出并注销模块module_exit(scull_cleanup_module){

     (1).获得设备号MKDEV

     (2).调用scull_trim来清除dev设备文件,对在初始化分配内存的所有步骤都作kfree释放操作,对设备结构体数组值重新设定.

       /*注意scull_trim必须被拥有旗帜的函数所调用,而且其中所以DATA区域在释放后依然赋值为NULL是从安全角度出发*/

     (3).删除已经注册的char设备:cdev_del

     (4).释放设备结构体

     (5).调用scull_remove_proc删除初始化阶段创立的proc文件调试机制

     (6).调用unregister_chrdev_region注销注册的设备号

     (7).注销其他相关友好的设备

}

4.关于对设备读写等的file-ops函数的定义(){

     (1)open(){

         原型:int (*open)(struct inode *inode, struct file *filp)

         a.通过indode->i_cdev(其本身指向scull_devcdev)获得scull_dev的入口指针/*识别需要被打开的设备*/

              dev = container_of(inode->i_cdev, struct scull_dev, cdev);

         b.将获得的dev保存到filp->private_data filp->private_data = dev

         c.如果是以只读模式打开则调用scull_trim()截短设备为0

              filp->f_flags & O_ACCMODE) == O_WRONLY,判读其打开模式

              对访问区域加锁down_interruptible(&dev->sem)

              调用scull_trim(dev)        

              解锁up(&dev->sem);

     }

     (2)release(){/*作用和open相反*/

         原型:int (*release)(struct inode *inode, struct file *filp)

     }

     (3)read(){

         原型 ssize_t read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)

         a.通过filp->private_data获得已经保存在其中的设备结构体入口指针:struct scull_dev *dev = filp->private_data;

         b.初始化一些结构体和变量.比如scull_qset结构体,量子和量子集等等.

         c.加锁down_interruptible(&dev->sem)

         d.判读off_t *f_pos参数是否超越了设备存储的范围:*f_pos >= dev->size

           修正能够返回数据的长度(*f_pos + count > dev->size) && (count = dev->size - *f_pos)

         e.f_pos获得find listitem, qset index, and offset in the quantum

         f.调用scull_follow依据e步的item获得其设备结构体中的入口地址(即需要的qset的地址),即通过qset->next寻找item-1次即可,如果qset还没有被实体化,那么就调用kmalloc,memset即可.

           找到后,判读qset地址,其指向的量子集,和量子集中的量子的正确性.

         g./*注意:我们在这里约定每次读和写只操作一个量子*/

           所以要根据实际量子中的off和量子长度再次调整count

         h.调用copy_to_user将内核数据复制到用户区域.

         i.f_pos处理加上实际读出来的count

         j.错误和结束:解锁即可.

     }

     (4)write(){

         原型 ssize_t write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)

         基本同read,只是h步骤使用copy_from_user.

         /*注意如果内核访问用户区域,由于user域存在分页机制,进程有可能被休眠以等待不再当前页中的数据,所以要求copy_from_user函数是可重入的.*/

     }

     (5)llseek(){

         原型:(*llseek)(struct file *filp, loff_t off, int whence)

         a.通过filp->private_data获得已经保存在其中的设备结构体入口指针

         b.根据llseek的第二个参数,返回不同的地址

         c.指定filp->f_posb步的返回值

     }

5.iotcl的实现(){

     具体间收获

}

}

/*=========================================*/

/*收获*/

/*=========================================*/

1.如何用宏定义来实现DEBUG调试(){

     程序中:

     #undef PDEBUG          /* undef it, just in case */

     #ifdef SCULL_DEBUG

     #    ifdef __KERNEL__   /* This one if debugging is on, and kernel space */

     #        define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)

     #    else               /* This one for user space */

     #        define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)

     #    endif

     #else                       /* not debugging: nothing */

     #    define PDEBUG(fmt, args...)

     #endif

     编译时候:

     gcc -DSCULL_DEBUG/*define SCULL_DEBUG*/

}

/*--------------------------------*/

2.ioctl向驱动程序发出请求在驱动中的实现(){

     1.定义ioctlcmd参数

         #define SCULL_IOC_MAGIC  'k'/* Use 'k' as magic number */

         #define SCULL_XXX0 _IO(SCULL_IOC_MAGIC, 0)/*无参数的,即无数据传递*/

         #define SCULL_XXX1 _IOW(SCULL_IOC_MAGIC, 1,int)/*无参数的,即无数据传递*/

         #define SCULL_XXX2 _IOR(SCULL_IOC_MAGIC, 2,int)/*无参数的,即无数据传递*/

     2.驱动程序中如何实现ioctl

         原型:int (*ioctl)(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)

         a.判断cmd类型和个数是否正确,调用_IOC_TYPE,_IOC_NR

         b.如果有数据通过指针传递,首先检查传递指针是否有效:access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd))

           调用_IOC_DIR,_IOC_SIZE分别得到cmd的方向和参数的长度

           /*注意如果cmd是读,那么要检查user是否能被内核写,反之则反之*/

         c.经典的switch结构,根据cmd分别做分别的事情.

        

         其中使用指针传递参数的实现方法:

              从用户空间传递到内核:__get_user(内核变量名,(int __user *)参数),

              反之则用__put_user(内核变量名,(int __user *)参数) 

 

}

/*--------------------------------*/

3.设备访问区域使用semaphore实现mutual exclusion的流程(){

     相关头文件asm/semaphore.h

     1.定义旗帜变量与设备结构体中:xxx_dev:struct semaphore sem

     2.在设备访问区块初始时候,初始化旗帜变量init_MUTEX(xxx_dev.sem)

     3.具体运用:

         对访问区域加锁down_interruptible(&xxx_dev->sem)

         解锁up(&xxx_dev->sem);

}

/*--------------------------------*/

4.如何建立通过procdebug机制(){

     方法一:通过一般的/proc文件

     1.创建对/proc文件可读的方法函数

         这个函数虽然是自己定义,但是有共同的type,如下

         int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);也就是说要把返回的信息写在kernel中的buf区域

         然后可以根据自己的需要写对应的读函数,本例中是int scull_read_procmem(char *buf, char **start, off_t offset,int count, int *eof, void *data) 

     2.将上步骤的函数和具体的.proc文件关联

         定义文件相关结构体:struct proc_dir_entry *entry;

         create_proc_read_entry,创建文件并使之关联read函数

     方法二:通过seq_file文件接口

     0.根据seq_file ops的原型定义自己的函数,这些主要用于open时寻找iterator

         scull_seq_start,scull_seq_next,scull_seq_stop,scull_seq_show

         /*在这些函数的创建中尽量使用seq对应的一些函数如seq_printf,seq_putc*/

     1.创建seq文件的ops

         static struct seq_operations scull_seq_ops = {

              .start = scull_seq_start,

              .next  = scull_seq_next,...};

     2.创建file-ops需要的函数open,同时也注册好上述seqops

         static int scull_proc_open(struct inode *inode, struct file *file)

         {return seq_open(file, &scull_seq_ops);}/*由于seq文件(iterator)需要特别的寻找方法,seq ops提供的*/

     3.创建文件ops结构体,指定对该文件的相关操作的函数

         static struct file_operations scull_proc_ops = {

              .owner   = THIS_MODULE,

              .open    = scull_proc_open,/*使用自己定的open*/

              .read    = seq_read,...};/*使用seq的函数*/

     4.创建真实的文件,并指定文件的ops

         entry = create_proc_entry("scullseq", 0, NULL);

         if (entry)

              entry->proc_fops = &scull_proc_ops;

     关于注销对应文件两种方法的做法是一样的

         remove_proc_entry("name", NULL /* parent dir */);

     /*注意对设备有互斥锁部分访问时候的down up机制的运用.*/

}

/*=========================================*/

/*遗留*/

/*=========================================*/

1. 主函数分析 2->(4)步骤涉及用pipe实现和有open control机制,本次暂不学习

   类似的有3->(7)

/*=========================================*/

 

阅读(1061) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~