进程管理(五)execve系统调用

951阅读 0评论2012-02-15 小布丁姐姐
分类:

execve系统调用的入口点是体系结构相关的sys_execve函数,该函数将其工作委托给do_execve函数:
  1. int do_execve(const char * filename,
  2.     const char __user *const __user *argv,
  3.     const char __user *const __user *envp,
  4.     struct pt_regs * regs)
其中filename是可执行文件名,argv和envp分别是传入的参数和环境变量,regs是寄存器集合
其中的argv喝envp是指针数组,并且它们指向的两个数组自身的指针,以及数组中的指针都是位于虚拟地址空间的用户空间部分。
__user这个宏是供代码分析工具来检查该变量是否指向用户空间的
  1. # define __user        __attribute__((noderef, address_space(1)))

当编译内核代码的时候,使用make C=1或C=2的时候,会调用一个叫Sparse的工具,这个工具对内核代码进行检查,怎么检查呢,就是靠对那些声明过Sparse这个工具所能识别的特性的内核函数或是变量进行检查。在调用Sparse这个工具的同时,在Sparse代码里,会加上#define __CHECKER__ 1的字样。换句话说,就是,如果使用Sparse对代码进行检查,那么内核代码就会定义__CHECKER__宏,否则就不定义。具体解释请访问:
例如:
# define __user  __attribute__((noderef, address_space(1)))
      
这个宏是重点,用来检查是否属于用户空间!这里就能看出来,类似于__attribute__((noderef, address_space(1)))这样的属性就是Sparse这个工具所能识别的了。
       
其他的那些个属性是用来检查什么的呢,我一个个地做介绍。
__user 这个特性,即__attribute__((noderef, address_space(1))),是用来修饰一个变量的,这个变量必须是非解除参考(__attribute__((noderef))——no dereference)的,即这个变量地址必须是有效的,而且变量所在的地址空间必须是1(__attribute__((address_space(1)))),即用户程序空间的。这里Sparse工具把程序空间分成了3个部分,0表示normal space,即普通地址空间,对内核代码来说,当然就是内核空间地址了。1表示用户地址空间,这个不用多讲,还有一个2,表示是设备地址映射空间,例如硬件设备的寄存器在内核里所映射的地址空间。
                     ——引用自

接着讲解do_execve

do_execve完成的工作主要如下:
1、打开可执行文件
  1. file = open_exec(filename);
  2.     retval = PTR_ERR(file);
  3.     if (IS_ERR(file))
  4.         goto out_unmark;

2、bprm_init

  1) mm_alloc:生成一个新的mm_struct实例来管理进程地址空间
   
  1. bprm->mm = mm = mm_alloc();
  2)init_new_context:初始化mm_struct实例
  
  1. err = init_new_context(current, mm);
  3)__bprm_mm_init:建立初始的栈
  
  1. err = __bprm_mm_init(bprm);
要仔细了解这部分的工作,就需要了解struct linux_binprm和struct mm_struct的结构和具体作用,这个后面会进行讲解。

3、prepare_binprm:新进程的很多参数,例如euid,egid,参数列表等,会被后面的函数用到,未来方便起见,会将这些信息打包在linux_binprm里,因此prepare_binprm的作用是将父进程的一些相关的值(特别是euid和gid)复制到linux_binprm这个结构里。
  1. bprm->cred->euid = current_euid();
  2.     bprm->cred->egid = current_egid();

  3.     if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
  4.         /* Set-uid? */
  5.         if (mode & S_ISUID) {
  6.             bprm->per_clear |= PER_CLEAR_ON_SETID;
  7.             bprm->cred->euid = inode->i_uid;
  8.         }

  9.         /* Set-gid? */
  10.         /*
  11.          * If setgid is set but no group execute bit then this
  12.          * is a candidate for mandatory locking, not a setgid
  13.          * executable.
  14.          */
  15.         if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
  16.             bprm->per_clear |= PER_CLEAR_ON_SETID;
  17.             bprm->cred->egid = inode->i_gid;
  18.         }
  19.     }
 

4、复制环境变量和参数数组内容
  1. retval = copy_strings_kernel(1, &bprm->filename, bprm);
  2.     if (retval < 0)
  3.         goto out;

  4.     bprm->exec = bprm->p;
  5.     retval = copy_strings(bprm->envc, envp, bprm);
  6.     if (retval < 0)
  7.         goto out;

  8.     retval = copy_strings(bprm->argc, argv, bprm);
  9.     if (retval < 0)


5、search_binary_handler:遍历所有的二进制格式,查找合适的handler。
这个函数比较复制后面会仔细讲解。



上一篇:为什么 12306 不需要排队系统
下一篇:基于MFC的OpenGL编程