一、模塊使用計數(shù)的背景知識
模塊是一種可以在內(nèi)核運行過程中動態(tài)加載、卸載的內(nèi)核功能組件。2.6內(nèi)核中模塊的命名方式為*.ko。模塊在被使用時,是不允許被卸載的。編程時需要用“使用計數(shù)”來描述模塊是否在被使用。
二、2.4內(nèi)核使用計數(shù)的實現(xiàn)方法
2.4內(nèi)核中,模塊自身通過 MOD_INC_USE_COUNT, MOD_DEC_USE_COUNT宏來管理自己被使用的計數(shù)。通常我們在寫模塊時,會在open方法中加入MOD_INC_USE_COUNT,在close方法中加入MOD_DEC_USE_COUNT來實現(xiàn)使用計數(shù)。
三、2.6內(nèi)核使用計數(shù)的實現(xiàn)方法
2.6內(nèi)核提供了更健壯、靈活的模塊計數(shù)管理接口 try_module_get(&module), module_put(&module)取代2.4中的模塊使用計數(shù)管理宏。模塊的使用計數(shù)不必由自身管理,而且在管理模塊使用計數(shù)時考慮到SMP與PREEMPT機制的影響(參考module.h中try_module_get和module.c中module_put的實現(xiàn))。
int try_module_get(struct module *module); 用于增加模塊使用計數(shù);若返回為0,表示調(diào)用失敗,希望使用的模塊沒有被加載或正在被卸載中。
void module_put(struct module *module); 減少模塊使用計數(shù)。
try_module_get與module_put 的引入與使用與2.6內(nèi)核下的設(shè)備模型密切相關(guān)。2.6內(nèi)核為不同類型的設(shè)備定義了struct module *owner 域,用來指向管理此設(shè)備的模塊。如字符設(shè)備的定義:
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 *);
……
};
從設(shè)備使用的角度出發(fā),當需要打開、開始使用某個設(shè)備時,使用 try_module_get(dev-》owner)去增加管理此設(shè)備的 owner模塊的使用計數(shù);當關(guān)閉、不再使用此設(shè)備時,使用module_put(dev-》owner)減少對管理此設(shè)備的owner模塊的使用計數(shù)。這樣,當設(shè)備在使用時,管理此設(shè)備的模塊就不能被卸載;只有設(shè)備不再使用時模塊才能被卸載。
2.6內(nèi)核下,對于為具體設(shè)備寫驅(qū)動的開發(fā)人員而言,基本無需使用 try_module_get與module_put,因為此時開發(fā)人員所寫的驅(qū)動通常為支持某具體設(shè)備的owner模塊,對此設(shè)備owner模塊的計數(shù)管理由內(nèi)核里更底層的代碼如總線驅(qū)動或是此類設(shè)備共用的核心模塊來實現(xiàn),從而簡化了設(shè)備驅(qū)動開發(fā)。
四、舉例說明2.6內(nèi)核模塊使用計數(shù)的實現(xiàn)過程
舉一個2.6內(nèi)核下字符設(shè)備驅(qū)動編寫的例子來說明問題。在2.6內(nèi)核下編寫一個設(shè)備驅(qū)動時,初始化過程中大家都會看到如下的模板:
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 是什么呢?
內(nèi)核源碼目錄下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這個符號是在加載到內(nèi)核后才產(chǎn)生的。insmod命令執(zhí)行后,會調(diào)用kernel/module.c里的一個系統(tǒng)調(diào)用 sys_init_module,它會調(diào)用load_module函數(shù),將用戶空間傳入的整個內(nèi)核模塊文件創(chuàng)建成一個內(nèi)核模塊,并返回一個struct module結(jié)構(gòu)體,從此,內(nèi)核中便以這個結(jié)構(gòu)體代表這個內(nèi)核模塊。THIS_MODULE類似進程的CURRENT。
struct module
{……
struct module_ref ref[NR_CPUS];
}
struct module_ref
{
local_t count;//記錄模塊使用計數(shù)
} ____cacheline_aligned;
現(xiàn)在咱們就看看內(nèi)核是如何幫助我們完成使用計數(shù)的。
在2.4內(nèi)核中,我們是通過在open方法中增加引用計數(shù),在close方法中減少引用計數(shù)。在2.6內(nèi)核中,內(nèi)核肯定也是要在open、close時幫助我們實現(xiàn)同樣功能的。
打開字符設(shè)備的大體流程如下:
sys_open()-》do_sys_open()-》do_filp_open()-》nameidata_to_filp() -》__dentry_open()-》chrdev_open()-》open()
2.6內(nèi)核中并不要求模塊在open中顯示的實現(xiàn)使用計數(shù),真正使用模塊使用計數(shù)是在chrdev_open()中完成的。
內(nèi)核源碼fs/char_dev.c
static int chrdev_open(struct inode *inode, struct file *filp)
{
struct cdev *p;
……
cdev_get(p); //增加cdev中owner指向的module的使用計數(shù)
……
filp-》f_op = fops_get(p-》ops);}// 增加file_operations中owner指向的module的使用計數(shù)
if (filp-》f_op-》open) {
lock_kernel();
ret = filp-》f_op-》open(inode,filp);//調(diào)用到設(shè)備驅(qū)動中的open
unlock_kernel();
}
}
static struct kobject *cdev_get(struct cdev *p)
{
struct module *owner = p-》owner;
……
if (owner && !try_module_get(owner))
return NULL;
……
}
內(nèi)核源碼Include/linux/fs.h
#define fops_get(fops) /
(((fops) && try_module_get((fops)-》owner) ? (fops) : NULL))
關(guān)閉設(shè)備的大體流程
sys_close()-》filp_close()-》fput()-》__fput()-》release()
2.6內(nèi)核中并不要求模塊在release中顯示的實現(xiàn)使用計數(shù),真正使用模塊使用計數(shù)是在__fput()中完成的。
void __fput(struct file *file)
{
struct dentry *dentry = file-》f_path.dentry;
struct vfsmount *mnt = file-》f_path.mnt;
struct inode *inode = dentry-》d_inode;
if (file-》f_op && file-》f_op-》release)
file-》f_op-》release(inode, file);//調(diào)用到設(shè)備驅(qū)動中的release
……
cdev_put(inode-》i_cdev); //減少cdev中owner指向的module的使用計數(shù)
……
fops_put(file-》f_op);// 減少file_operations中owner指向的module的使用計數(shù)
……
}
評論
查看更多