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

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

2009-06-10 14:02 246 查看



  在这里为bprm->mm的初始化下了这么多功夫是为什么呢?它跟进程的mm有什么关系?不急,继续耐着性子看代码,我们会看到它的用途的.

  继续分析do_execve()中所调用的子函数.Count()来用计算可执行文件的参数或者环境变量的个数.它的代码如下:
static int count(char __user * __user * argv, int max)
{
  int i = 0;
  if (argv != NULL) {
    for (;;) {
      char __user * p;
      //在内核空间中取argv的值
      //取值失败
      if (get_user(p, argv))
        return -EFAULT;
      //如果为空。说明已经取到了NULL。结束了
      if (!p)
        break;
      argv++;
      //参数个数超过了允许的最大值
      if(++i > max)
        return -E2BIG;
      cond_resched();
    }
  }
  return i;
}
这个函数的原理是利用参数后面是以NULL结尾的,不懂的请回个头去看下上面的分析.疑问:在取参数个数的时候,会进行用户空间到内核空间的copy.但是这里仅仅是得知它的个数,在后面的操作中,还会继续去取参数值放到bprm->mm表示的空间中.这里有两次拷copy.可不可把这两个过程放在一起.省掉一次从用户空间到内核空间的COPY呢?prepare_binprm()会将文件的前128字节copy到bprm->buf.代码片段如下所示:
int prepare_binprm(struct linux_binprm *bprm)
{
  ……
  ……
  memset(bprm->buf,0,BINPRM_BUF_SIZE);
  //#define BINPRM_BUF_SIZE 128
  return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
}
将具体的参数COPY到bprm->mm所表示的存储空间中是由copy_strings()完成的.它的代码有一点繁锁.如下示:
/*
  参数含义:
    argc:参数个数
    argv:参数数组
*/
static int copy_strings(int argc, char __user * __user * argv,
      struct linux_binprm *bprm)
{
  struct page *kmapped_page = NULL;
  char *kaddr = NULL;
  unsigned long kpos = 0;
  int ret;
  while (argc-- > 0) {
    char __user *str;
    int len;
    unsigned long pos;
    //取数组相应项,将其放至str中
    //COPY失败,或者参数长度非法
    if (get_user(str, argv+argc) ||
        !(len = strnlen_user(str, MAX_ARG_STRLEN))) {
      ret = -EFAULT;
      goto out;
    }
    //判断参数长度是否超过允许的最大值
    if (!valid_arg_len(bprm, len)) {
      ret = -E2BIG;
      goto out;
    }
    /* We're going to work our way backwords. */
    //当前的位置
    pos = bprm->p;
    str += len;
    bprm->p -= len;
    while (len > 0) {
      int offset, bytes_to_copy;
      offset = pos % PAGE_SIZE;
      if (offset == 0)
        offset = PAGE_SIZE;
      bytes_to_copy = offset;
      if (bytes_to_copy > len)
        bytes_to_copy = len;
      offset -= bytes_to_copy;
      pos -= bytes_to_copy;
      str -= bytes_to_copy;
      len -= bytes_to_copy;
      if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
        struct page *page;
        //根据映射关系得到pos地址在bprm->mm中所映射的页面
        page = get_arg_page(bprm, pos, 1);
        if (!page) {
          ret = -E2BIG;
          goto out;
        }
        if (kmapped_page) {
          flush_kernel_dcache_page(kmapped_page);
          //断开临时映射
          kunmap(kmapped_page);
          //减少引用计数
          put_arg_page(kmapped_page);
        }
        kmapped_page = page;
        //将临时映射到内核
        kaddr = kmap(kmapped_page);
        kpos = pos & PAGE_MASK;
        flush_arg_page(bprm, kpos, kmapped_page);
      }
      //copy参数至刚才映射的页面
      if (copy_from_user(kaddr+offset, str, bytes_to_copy)) {
        ret = -EFAULT;
        goto out;
      }
    }
  }
  ret = 0;
out:
  if (kmapped_page) {
    flush_kernel_dcache_page(kmapped_page);
    kunmap(kmapped_page);
    put_arg_page(kmapped_page);
  }
  return ret;
}
我们在前面看到,并没有给VM映射实际的内存,在这里COPY参数的时候,必然会引起缺页异常,再由缺页异常程序处理缺页的情况.经过上面的过程之后,bprm->mm表示的存储空间如下所示:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: