说明:
1. 此文档基于 linux 2.6.32,TQ2440上测试通过,
2. arm-linux-gcc版本
Thread model: posix
gcc version 4.3.3 (Sourcery G++ Lite 2009q1-203)
一. 问题描述及追踪分析
使用 rmmod时候发现如下错误
rmmod chdir no such file or directory
追踪该错误信息,发现busybox rmmod.c 位置 busybox-1.xxx/modutils/rmmod.c
函数框架如下
int rmmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int rmmod_main(int argc UNUSED_PARAM, char **argv)
{
//.......
if (!*argv)
bb_show_usage();
//......
while (*argv) {
char modname[MODULE_NAME_LEN];
const char *bname;
bname = bb_basename(*argv++);
if (n)
safe_strncpy(modname, bname, MODULE_NAME_LEN);
else
filename2modname(bname, modname);
if (bb_delete_module(modname, flags))
bb_error_msg_and_die('can't unload '%s': %s',
modname, moderror(errno));
}
return EXIT_SUCCESS;
}
研究 这个函数,我们发现删除模块的 核心函数 是 bb_delete_module,追踪这个函数,在 /modutils/modutils.c中,我们得到如下函数
int FAST_FUNC bb_delete_module(const char *module, unsigned int flags)
{
errno = 0;
delete_module(module, flags);
return errno;
}
这个表明rmmod实现是依靠 delete_module
继续跟踪delete_module,
#ifdef __UCLIBC__
extern int delete_module(const char *module, unsigned int flags);
#else
# include
# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
#endif
这个 是什么意思呢?
如果定义了 __UCLIBC__ 则使用uClibc中的 delete_module(const char *module, unsigned int flags)函数
否则,则用系统调用内核的 sys_delete_module函数 该函数原型如下
asmlinkage long sys_delete_module(const char __user * name_user, unsigned int flags);
我们 再来看 safe_strncpy(modname, bname, MODULE_NAME_LEN); 函数和 filename2modname(bname, modname);函数
这俩个函数 的作用都是获得 模块名称 ,使用的核心实现方式是 strrchr(name, '/');
char *strchr(const char *s, int c);
The strrchr() function returns a pointer to the last occurrence of the character c in the string s.
这个意思时说strrchr函数返回字符C在 字符串S中最后一次出现的位置。
假如我们使用如下指令
#rmmod /lib/modules/2.6.32/adc.ko
经过函数处理后, modname 将变回 adc.ko
值得注意的是 bb_basename() 函数如下
const char* FAST_FUNC bb_basename(const char *name)
{
const char *cp = strrchr(name, '/');
if (cp)
return cp + 1;
return name;
} 该函数 已经进行了一次处理
另外, filename2modname 函数
char * FAST_FUNC filename2modname(const char *filename, char *modname)
{
int i;
char *from;
if (filename == NULL)
return NULL;
if (modname == NULL)
modname = xmalloc(MODULE_NAME_LEN);
from = bb_get_last_path_component_nostrip(filename);
for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '' &&from[i] != '.'; i++)
modname[i] = (from[i] == '-') ? '_' : from[i];
modname[i] = '';
return modname;
}
注意
from[i] != '.' ,这个意味着 adc.ko 会被解析成adc
from[i] == '-'? '_' : from[i]这个意味着 adc-xyz会被解析成adc_xyz
分析到这里,我们也就知道了rmmod的全过程,核心在于利用sys_delete_module函数卸载驱动模块
下面给出解决方法
二. 解决方法
建立rmmod.c ,内容如下
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int ret = -1;
int left_time =5;
int i=0;
if(!argv[1])
{
printf('usage: rmmod modenamen');
return -1;
}
char *modname = argv[1];
while(argv[1][i]!='')
{
//如下这个 是为了 rmmod adc.ko 与 rmmod adc效果一致
if(argv[1][i]=='.'){argv[1][i]='';break;}
i++;
};
while (left_time-- > 0) {
ret = delete_module(modname, O_NONBLOCK | O_EXCL);//系统调用sys_delete_module
if (ret < 0 && errno == EAGAIN)
sleep(1);
else
break;
}
if (ret != 0) printf('Error when rmmod module %s: %sn',modname, strerror(errno));
return 0;
}
在PC中
#arm-linux-gcc rmmod.c -o rmmod
在板子中
#mv /sbin/rmmod /sbin/rmmod2
#mv rmmod /sbin/rmmod