- 论坛徽章:
- 0
|
#include linux/module.h>
#include moduleparam.h>
#include linux/init.h>
#include linux/kernel.h> /*printk()*/
#include linux/slab.h> /*kmalloc()*/
#include linux/fs.h> /*everything...*/
#include linux/errno.h> /*error codes*/
#include linux/types.h> /*size_t*/
#include linux/cdev.h> /*cdev struct*/
#include asm/system.h> /*cli(),*_flags*/
#include asm/uaccess.h> /*copy_ ....*/
#include "scull_t1.h" /*attention to the "" and */
/*
*Our parameters which can be set at load time.
*
*/
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_nr_devs = SCULL_NR_DEVS; /*number of bare scull devs*/
int scull_quantum = SCULL_QUANTUM; /*every one of quantum array has a quantum size*/
int scull_qset = SCULL_QSET; /*the longth of quantum-set*/
module_param(scull_major,int,S_IRUGO); /*knowledge 1*/
module_param(scull_monor,int,S_IRUGO);
module_param(scull_nr_devs,int,S_IRUGO);
module_param(scull_quantum,int,S_IRUGO);
module_param(scull_qset,int,S_IRUGO);
struct scull_dev *scull_devices; /*allocated in scull_init_module*/
/*
*Empty out (like "clean up")the scull device
*/
int scull_trim(struct scull_dev *dev) /*知识点4*/
{
struct scull_qset *next,*dptr;
int qset = dev->qset; /*amount of quantum*/
int i;
for(dptr = dev->data;dptr;dptr = next) /*循环scull_qset list 个数*/
{
if(dptr->data)
{
for(i = 0; iqset; i++)
kfree(dptr->data); /*释放dptr指向的数组的每个数组元素指向的quantum空间*/
kfree(dptr->data);/* 释放当前的scull_set的量子集的空间*/
dptr->data = NULL;/* 释放一个scull_set中的void **data指针*/
/*其实就我个人写的话下面那句指向空会漏掉*/
}
next = dptr->next; /*下一个scull_qset list开始*/
kfree(dptr); /*knowledge 2*/
}
dev->size =0; /*amount of data store here 为0*/
dev->quantum = scull_quantum; /*set the quantum'size*//*你不是清空不,怎么还来设置它的大小啊*/
dev->qset = scull_qset; /*set the number of quantum in a array*//*同上的疑问*/
dev->data = NULL; /*释放当前的scull_device的struct scull_qset *data指针*/
return 0;
}
/********************************************************************/
/*
*Open and close
*/
int scull_open(struct inode *inode,struct file *filp)
{
struct scull_dev *dev; /*the information of this dev*/
dev = contairner_of(inode->i_cdev,struct scull_dev, cdev); /*知识点3*/
flip->private_data = dev; /*.将获得的dev保存到filp->private_data*/
/*如果是以只读模式打开则调用scull_trim()截短设备为0*/
if((filp->f_flags&O_ACCMODE) == O_WRONLY) //这里注意是检测打开的方式不是读写权限f_mode
if(down_interruptible(&dev->sem)) /* 对访问区域加锁*/
return -ERESTARTSYS; /*猜想应该是信号量不可用的意思吧??*/
scull_trim(dev);
up(&dev->sem); /* 解锁*/
}
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
/*由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做*/
}
/*
*Follow the list
*/
struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
struct scull_qset *qs = dev->data;
/*Allocate frist qset explicitly if need be*/
if(! qs) /*当list为null时*/
{
qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
if(qs == NULL)
return NULL; /*??*/
memset(qs,0,sizeof(struct scull_qset));
}
/*follow the list*/
while(n--)
{
if(!qs->next)
{
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL; /* Never mind */
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue; /*这下我傻了*/
return qs;
}
}
/*
*read and write
*/
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data; /*获得已经保存在其中的设备结构体入口指针*/
struct scull_qset *dptr; /*the first listitem*/
int quantum = dev->quantum,qset = dev->qset;
int itemsize = quantum *qset; /*这句很怪哦 不同的部分相互赋值干吗啊??*/
int item,s_pos,q_pos,rest;
ssize_t retval = 0;
if(down_interruptible(&dev->sem))
return -ERESTARSYS;
if(*f_pos >= dev->size) /*判读off_t *f_pos参数是否超越了设备存储的范围*/
goto out;
if(*f_pos+count > dev->size)
count = dev->size - *f_pos;
/*find listitem,qset index,and offset in the quantum*//* 根据数组结构得出相应数据 */
item = (long)*f_pos/ itemsize; /*long???why???*/
rest = (long)*f_pos%itemsize;
s_pos = rest/quantum; /*no long ??why??*/
q_pos = rest%quantum;
/*follow the list up to the right position(defined elsewhere)*/
dptr = scull_follow(dev,item);
/*判断各个数值的合法性并做修正*/
if(dptr == NULL||!dptr->data||!dptr->data||!dptr->data[s_pos])
goto out;
/*read only up to the end of this quantum*/
if(count>quantum - q_pos)
count = quantum -q_pos;
/*将内核数据复制到用户区域*/
if(copy_to_user(buf, dptr->data[s_pos]+q_pos,count))/*发现自己对返回值还是不足*/
{
retval = -EFUALT; /*上面应该是成功返回0非成功返回非0即真*/
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos )
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum,qset = dev->qset;
int itemsize = quantum * qset; /*这句还是没懂*/
int item,s_pos,q_pos,rest;
ssize_t retval = -ENOMEM; /*no mem used in "goto out"*/
if(down_interruptible(&dev->sem))
return -ERESTART;
/*同read*/
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
/* follow the list up to the right position */
/*同read*/
dptr = scull_follow(dev, item);
if (dptr == NULL)
goto out;
if (!dptr->data)
{
dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data)
goto out;
memset(dptr->data, 0, qset * sizeof(char *));
}
if (!dptr->data[s_pos])
{
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
if (!dptr->data[s_pos])
goto out;
}
if (count > quantum - q_pos)
count = quantum - q_pos;
/*注意假如内核访问用户区域,由于user域存在分页机制,
进程有可能被休眠以等待不再当前页中的数据,所以需要
copy_from_user函数是可重入的.*/
if(copy_from_user(dptr->data[s_pos]+q_pos, buf, count))
{
retval = -EFAULT; /*bad address*/
goto out;
}
*f_pos +=count;
retval = count;
/*updata the size*/
/*实际上大多数情况读写函数是不能完全读写完的,所以做更新方便读写完的操作*/
if(dev->size *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
/*set file_operations*/
struct file_operations scull_fops =
{
.owner = THIS_MODULE,
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release,
};
/*init and exit*/
void scull_cleanup_module(void)
{
int i;
dev_t devno = MKDEV(scull_major,scull_minor);/*获取设备号*/
/*get rid of dev entries*/
if(scull_devices)
{
for(i = 0; iscull_nr_devs; i++ )
{
scull_trim(scull_devices +i);
cdev_del(&scull_devices.cdev);
}
kfree(scull_devices);
}
unregister_chrdev_region(devno, scull_nr_devs);
}
/*
*set up the char_dev structure for this device
*/
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err,devno = MKDEV(scull_major,scull_minor+index);
/*初始化cdev,主要是指定其fops */
cdev_init(&dev->cdev,&scull_fops); /*注册设备结构体*//*知识点5*/
dev->cdev.owner = THIS_MODULE; /*注意这里是先注册再初始化有些元素,原因有待查*/
dev->cdev.ops = &scull_fops; /*cdev->init里面已经初始化了 可省略*/
err = cdev_add(&dev->cdev, devno,1) /*告知内核该结构体信息,设备注册到这里才算真的激活*/
if(err)
printk(KERN_NOTICE "Error %d adding scull%d",err,index); /*这个失败的可能很小书上这么提到why??*/
}
int scull_init_module(void)
{
int result,i;
dev_t dev = 0;
if (scull_major) /*选择是否自动获取设备号*/
{
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
}
else
{
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull");
scull_major = MAJOR(dev);
}
if (result 0)
{
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
/*
*allocate the devices we can't have them static,as the number
*can be specified at load time
*/
scull_devices = kmalloc(scull_nr_devs*sizeof(struct scull_dev), GFP_KERNEL);/*分配nr个scull_dev的内存*/
if(!scull_devices)
{
result = -ENOMEM; /*NO enogh memory */
goto fail;
}
memset(scull_devices,0,scull_nr_devs*sizeof(struct scull_dev)); /*kmalloc不清空*/
/*init each device*/
for(i = 0; iscull_nr_devs;i++)
{
scull_devices.quantum = scull_quantum;
scull_devices.qset = scull_qset;
init_MUTEX(&scull_devices.sem); /*初始化旗帜变量 用于down_interruptible up(&xxx_dev->sem);*/
scull_setup_cdev(&scull_devices,i); /*这个一定记得放于最后*/
}
return 0; /*succeed*/
fail: /*知识点6 goto*/
scull_cleanup_module();
return result;
}
/*加载 注销*/
module_init(scull_init_module);
module_exit(scull_cleanup_module);
/*程序信息*/
MODULE_AUTHOR("小兽");
MODULE_LICENSE("Dual BSD/GPL"); /*听说不写这个会有警告,等下编译的时候试验下*/
/*knowledge 1 module_param
*我这样理解这个函数,它向当前模块传入参数
*而这些参数是经过该函数注册的,带有一定的属性
*具体属性可以跟踪moduleparam.h里面的具体函数,我想能
*理解好__attribute__这个家伙的性能应该就能理解这个函数的意义了。
*值得注意的是,注册数组之类的是用的别的类似于这个函数的函数
*
*这个知识点我犯错了,因为本身LDD3上介绍得很清楚了,由于是很久以前看书
*时候看到的,给忘记了.... *应该书它的作用是让insmod命令对那些参数可见,而这
*种使用只是在调试的时候应该
*用,至于平常视乎更多的是不允许这些参数被修改的。
*
*再次提醒自己,不要什么程序可以跑了基本能看懂就OK了,这样的路迟早走不远,更别说
*实现自己的理想。
****************************************************************************/
/*N0 2
* 关于指针释放的,说实在的这里发现自己的C基础很差,完全找不到
*指针释放的概念,在这里做个笔记 提醒下以后多注意下
*/
/*NO3
*分配并填写置于filp->private_data里的数据结构。
*将初始化过的struct scull_dev dev的指针传递到filp->private_data里,以备后用
*
*具体说法为我们不需要cdev结构体本身,但希望得到包含scull_dev结构体?
*于是将scull_dev结构体指针保存在private_data字段中,方便使用
*
*简介点说是.通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针
*识别需要被打开的设备
*/
/***
*NO4
*
*scull_trim的实现相当于遍历了链表,先用dptr指针作为游标,指向每一个
*scull_qset,再通过量子集的大小dev->qset,在for循环中对dptr->data中的每一个量
*子进行释放。dptr->data是一个指针数组,所以kfree的参数为dptr->data。把量
*子集释放后再释放dptr->data.接着通过next指针移动游标,再释放当前的dptr。
*完整的内存释放后再对scull_devices初始化。
*http://www.diybl.com/course/6_system/linux/Linuxjs/2008109/149527_2.html
*/
/*
*NO5 设备结构体注册详情查看LDD3 P59
*记得刚接触cdev这个家伙的时候是在跟着一本用老方法注册设备的书做完两个
*驱动之后的事了,为了接受这个结构体花了不少时间,现在都不知道为什么,
*但确实卡了我好久。呵呵 想想好笑
*这里用户自己定义的设备结构体scull_dev中嵌入了cdev注意接受这种嵌入的方法!!!!
在open函数里面
* 通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针识别需要被打开的设备
* dev = container_of(inode->i_cdev, struct scull_dev, cdev);
* 将获得的dev保存到filp->private_data filp->private_data = dev
*
这样就完成了整个自己定义设备结构体的嵌入工作
*/
/*
*NO6 goto
*大一接触C的时候这个印象特深,说什么容易把程序跳乱不要乱用,前几天看一些关于
*goto 的讨论才知道,是个什么大师发表了一篇关于goto毒害的文章后才被认为是毒瘤
*看了些资料后的整体认识是:1:向后跳的话没什么大碍,也不影响可读性(短距离)
* 2:这里用到的错误恢复机制视乎没有别的语句能比goto
* 更简介的实现所需要的功能 这点蛮好的!
* 3:goto 源于汇编,可以说是比较底层的操作,很多破解直接
* 一个goto语句越过注册码算法部分 跳到检测注册码后的
* 程序运行部分破解就完成,所以你不能说它不强大!!!
*/
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/90333/showart_1769534.html |
|