看本文前,先看着篇文章,Linux字符设备驱动。
insmod,大体上所做的事,有这么一些:
1、打开待安装模块并将其读入到用户空间。所谓“模块”就是经过编译但未经连接的.o文件。
2、模块中必定有一些在模块内部无法落实的符号(函数名或变量名),对这些符号的引用必须连接到内核中的相应符号。为此目的,需要通过系统调用query_module向内核询问这些符号在内核中的地址。如果内核允许"移出"这些符号的地址,就会返回有关的"符号表“。有些符号可能并不属于内核本身,而属于已经安装的其他模块。
至此在用户空间中完成了单向连接的模块镜像。后面init_module中参数mod_user,前半部分是module结构(里面的指针指向了后半部分的数据),后半部分是name大小+模块镜像(里面包含init_module和cleanup_module)+其他参数(例如deps)的集合体。
3、然后,通过系统调用create_module在内核中创建一个module数据结构,并且"预订"所需的系统(内核)空间。
4、最后,通过系统调用init_module,把用户空间中完成了单向连接的模块映像装入内核空间,再调用模块中一个名为init_module的函数(在Linux字符设备驱动一文中就是freg_init)。
接下来,我们解释query_module,代码如下:
asmlinkage long
sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,//name_user为查询对象所在模块的模块名,查询的结果通过缓冲区buf返回
size_t *ret)
{
struct module *mod;
int err;
lock_kernel();
if (name_user == NULL)
mod = &kernel_module;
else {
long namelen;
char *name;
if ((namelen = get_mod_name(name_user, &name)) < 0) {//把name从用户空间拷贝到系统空间
err = namelen;
goto out;
}
err = -ENOENT;
if (namelen == 0)//如果为0,则指向内核本身
mod = &kernel_module;
else if ((mod = find_module(name)) == NULL) {//在module_list中寻找相应的module结构
put_mod_name(name);
goto out;
}
put_mod_name(name);
}
switch (which)
{
case 0:
err = 0;
break;
case QM_MODULES:
err = qm_modules(buf, bufsize, ret);
break;
case QM_DEPS:
err = qm_deps(mod, buf, bufsize, ret);//所依赖模块的个数,buf为这些模块的模块名
break;
case QM_REFS:
err = qm_refs(mod, buf, bufsize, ret);
break;
case QM_SYMBOLS:
err = qm_symbols(mod, buf, bufsize, ret);
break;
case QM_INFO:
err = qm_info(mod, buf, bufsize, ret);
break;
default:
err = -EINVAL;
break;
}
out:
unlock_kernel();
return err;
}
struct module *
find_module(const char *name)
{
struct module *mod;
for (mod = module_list; mod ; mod = mod->next) {
if (mod->flags & MOD_DELETED)
continue;
if (!strcmp(mod->name, name))
break;
}
return mod;
}
static int
qm_deps(struct module *mod, char *buf, size_t bufsize, size_t *ret)
{
size_t i, space, l