您的位置:首页 > 运维架构 > Linux

linux可执行文件的加载和运行之一(1)

2009-06-10 14:00 274 查看
可执行文件的加载和运行 Execve系统调用可以调用一个可执行文件完全代替当前的进程,它在libc中的封装有几个API:
int execl(const charp a t* h n a m e, const char a* rg 0, ... /* (char *) 0 */);
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 [] );
我们深入内核代码来研究一下可执行文件的加载过程.execve()系统调用的入口是sys_execve().代码如下:
asmlinkage int sys_execve(struct pt_regs regs)
{
  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;
}
系统调用的时候,把参数依次放在:ebx,ecx,edx,esi,edi,ebp寄存器.详情请参阅本站<< Linux中断处理之系统调用>>.第一个参数为可执行文件路径,第二个参数为参数的个数,第三个参数为可执行文件对应的参数.do_execve()是这个系统调用的核心,它的代码如下:
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机制分析>>曾经描述过.我们再次把进程的空间结构图列出,如下如示:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: