可执行文件的加载和运行 Execve系统调用可以调用一个可执行文件完全代替当前的进程,它在libc中的封装有几个API:int execl(const charp a t* h n a m e, const char a* rg 0, ... /* (char *) 0 */);
我们深入内核代码来研究一下可执行文件的加载过程.execve()系统调用的入口是sys_execve().代码如下:
int execv(const charp a t* h n a m e, char *consta rgv [] );
int execle(const charp a t* h n a m e, const char a* rg 0, ...
/* (char *)0, char *cones nt v p [] */);
int execve(const charp a t* h n a m e, char *consta rgv [], char *consten vp [] );
int execlp(const charf i l e* n a m e, const char a* rg 0, ... /* (char *) 0 */);
int execvp(const charf i l e* n a m e, char *consta rgv [] );asmlinkage int sys_execve(struct pt_regs regs)
系统调用的时候,把参数依次放在:ebx,ecx,edx,esi,edi,ebp寄存器.详情请参阅本站<< Linux中断处理之系统调用>>.第一个参数为可执行文件路径,第二个参数为参数的个数,第三个参数为可执行文件对应的参数.do_execve()是这个系统调用的核心,它的代码如下:
{
int error;
char * filename;
//将用户空间的第一个参数(也就是可执行文件的路径)复制到内核
filename = getname((char __user *) regs.ebx);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename,
(char __user * __user *) regs.ecx,
(char __user * __user *) regs.edx,
®s);
if (error == 0) {
task_lock(current);
current->ptrace &= ~PT_DTRACE;
task_unlock(current);
/* Make sure we don't return using sysenter.. */
set_thread_flag(TIF_IRET);
}
//释放内存
putname(filename);
out:
return error;
}int do_execve(char * filename,
char __user *__user *argv,
char __user *__user *envp,
struct pt_regs * regs)
{
//linux_binprm:保存可执行文件的一些参数
struct linux_binprm *bprm;
struct file *file;
unsigned long env_p;
int retval;
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
goto out_ret;
//在内核中打开这个可执行文件
file = open_exec(filename);
retval = PTR_ERR(file);
//如果打开失败
if (IS_ERR(file))
goto out_kfree;
sched_exec();
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
//bprm初始化,主要是初始化bprm->mm
retval = bprm_mm_init(bprm);
if (retval)
goto out_file;
//计算参数个数
bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
goto out_mm;
//环境变量个数
bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
goto out_mm;
retval = security_bprm_alloc(bprm);
if (retval)
goto out;
//把要加载文件的前128 读入bprm->buf
retval = prepare_binprm(bprm);
if (retval < 0)
goto out;
//copy第一个参数filename
retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
goto out;
//bprm->exec:参数的起始地址(从上往下方向)
bprm->exec = bprm->p;
//copy环境变量
retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out;
//环境变量存放的起始地址
env_p = bprm->p;
//copy可执行文件所带参数
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
//环境变量的长度
bprm->argv_len = env_p - bprm->p;
//到链表中寻找合适的加载模块
retval = search_binary_handler(bprm,regs);
if (retval >= 0) {
/* execve success */
free_arg_pages(bprm);
security_bprm_free(bprm);
acct_update_integrals(current);
kfree(bprm);
return retval;
}
out:
free_arg_pages(bprm);
if (bprm->security)
security_bprm_free(bprm);
out_mm:
if (bprm->mm)
mmput (bprm->mm);
out_file:
if (bprm->file) {
allow_write_access(bprm->file);
fput(bprm->file);
}
out_kfree:
kfree(bprm);
out_ret:
return retval;
}
研究代码之前,我们先考虑一下进程的空间安排结构.在本站的<<uclibc中的malloc机制分析>>曾经描述过.我们再次把进程的空间结构图列出,如下如示: