linux获取⽂件引⽤计数,linux-2.6内核模块引⽤计数的实现⼀、模块使⽤计数的背景知识
模块是⼀种可以在内核运⾏过程中动态加载、卸载的内核功能组件。2.6内核中模块的命名⽅式为*.ko。模块在被使⽤时,是不允许被卸载的。编程时需要⽤“使⽤计数”来描述模块是否在被使⽤。
⼆、2.4内核使⽤计数的实现⽅法
2.4内核中,模块⾃⾝通过 MOD_INC_USE_COUNT, MOD_DEC_USE_COUNT宏来管理⾃⼰被使⽤的计数。通常我们在写模块时,会在open⽅法中加⼊MOD_INC_USE_COUNT,在close⽅法中加⼊MOD_DEC_USE_COUNT来实现使⽤计数。
三、2.6内核使⽤计数的实现⽅法
2.6内核提供了更健壮、灵活的模块计数管理接⼝ try_module_get(&module), module_put(&module)取代2.4中的模块使⽤计数管理宏。模块的使⽤计数不必由⾃⾝管理,⽽且在管理模块使⽤计数时考虑到SMP与PREEMPT机制的影响(参考module.h中try_module_get 和module.c中module_put的实现)。
int try_module_get(struct module *module); ⽤于增加模块使⽤计数;若返回为0,表⽰调⽤失败,希望使⽤的模块没有被加载或正在被卸载中。
void module_put(struct module *module); 减少模块使⽤计数。
try_module_get与module_put 的引⼊与使⽤与2.6内核下的设备模型密切相关。2.6内核为不同类型的设备定义了struct module
*owner 域,⽤来指向管理此设备的模块。如字符设备的定义:
struct cdev
{
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
……
};
从设备使⽤的⾓度出发,当需要打开、开始使⽤某个设备时,使⽤ try_module_get(dev->owner)去增加管理此设备的 owner模块的使⽤计数;当关闭、不再使⽤此设备时,使⽤module_put(dev->owner)减少对管理此设备的owner模块的使⽤计数。这样,当设备在使⽤时,管理此设备的模块就不能被卸载;只有设备不再使⽤时模块才能被卸载。
2.6内核下,对于为具体设备写驱动的开发⼈员⽽⾔,基本⽆需使⽤ try_module_get与module_put,因
为此时开发⼈员所写的驱动通常为⽀持某具体设备的owner模块,对此设备owner模块的计数管理由内核⾥更底层的代码如总线驱动或是此类设备共⽤的核⼼模块来实现,从⽽简化了设备驱动开发。
四、举例说明2.6内核模块使⽤计数的实现过程
举⼀个2.6内核下字符设备驱动编写的例⼦来说明问题。在2.6内核下编写⼀个设备驱动时,初始化过程中⼤家都会看到如下的模板:
static struct file_operations simple_remap_ops = {
.owner = THIS_MODULE,
.open = simple_open,
……
};
static void simple_setup_cdev(struct cdev *dev, int minor,
struct file_operations *fops)
{
int err, devno = MKDEV(simple_major, minor);
cdev_init(dev, fops);
dev->owner = THIS_MODULE;
err = cdev_add (dev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk (KERN_NOTICE "Error %d adding simple%d", err, minor);
}
⽆论是cdev还是file_operations都将⾃⼰的struct module *owner成员指向了THIS_MODULE。那么这个THIS_MODULE 是什么呢?
内核源码⽬录下include/linux/module.h
#ifdef MODULE
#define MODULE_GENERIC_TABLE(gtype,name) \
extern const struct gtype##_id __mod_##gtype##_table \
__attribute__ ((unused, alias(__stringify(name))))
extern struct module __this_module;
#define THIS_MODULE (&__this_module)
#else /* !MODULE */
#define MODULE_GENERIC_TABLE(gtype,name)
#define THIS_MODULE ((struct module *)0)
#endif
__this_module这个符号是在加载到内核后才产⽣的。insmod命令执⾏后,会调⽤kernel/module.c⾥的⼀个系统调⽤
sys_init_module,它会调⽤load_module函数,将⽤户空间传⼊的整个内核模块⽂件创建成⼀个内核模块,并返回⼀个struct module结构体,从此,内核中便以这个结构体代表这个内核模块。THIS_MODULE类似进程的CURRENT。
struct module
{……
struct module_ref ref[NR_CPUS];
}
struct module_ref
{
local_t count;//记录模块使⽤计数
} ____cacheline_aligned;
现在咱们就看看内核是如何帮助我们完成使⽤计数的。
在2.4内核中,我们是通过在open⽅法中增加引⽤计数,在close⽅法中减少引⽤计数。在2.6内核中,内核肯定也是要在open、close时帮助我们实现同样功能的。
打开字符设备的⼤体流程如下:
sys_open()->do_sys_open()->do_filp_open()->nameidata_to_filp() ->__dentry_open()->chrdev_open()->open()
2.6内核中并不要求模块在open中显⽰的实现使⽤计数,真正使⽤模块使⽤计数是在chrdev_open()中完成的。
内核源码fs/char_dev.cdefine的基本用法
static int chrdev_open(struct inode *inode, struct file *filp)
{
struct cdev *p;
……
cdev_get(p); //增加cdev中owner指向的module的使⽤计数
……
filp->f_op = fops_get(p->ops);}// 增加file_operations中owner指向的module的使⽤计数
if (filp->f_op->open) {
lock_kernel();
ret = filp->f_op->open(inode,filp);//调⽤到设备驱动中的open
unlock_kernel();
}
}
static struct kobject *cdev_get(struct cdev *p)
{
struct module *owner = p->owner;
……
if (owner && !try_module_get(owner))
return NULL;
……
}
内核源码Include/linux/fs.h
#define fops_get(fops) \
(((fops) && try_module_get((fops)->owner) ? (fops) : NULL))
关闭设备的⼤体流程
sys_close()->filp_close()->fput()->__fput()->release()
2.6内核中并不要求模块在release中显⽰的实现使⽤计数,真正使⽤模块使⽤计数是在__fput()中完成的。void __fput(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->;
struct inode *inode = dentry->d_inode;
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);//调⽤到设备驱动中的release
……
cdev_put(inode->i_cdev); //减少cdev中owner指向的module的使⽤计数
……
fops_put(file->f_op);// 减少file_operations中owner指向的module的使⽤计数
……
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。