转个scull笔记

LDD3源码学习笔记之scull_main

/*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_dev的cdev)获得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_pos为b步的返回值
     }
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.定义ioctl的cmd参数
         #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.怎么建立通过proc的debug机制(){
     方法一:通过一般的/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,同时也注册好上述seq的ops
         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)
/*=========================================*/

你可能感兴趣的:(struct,Module,File,Semaphore,user,IOC)