用来描述用户态的cpu寄存器在内核栈中保存情况.可以获取用户空间的信息
    1. struct pt_regs {
    1. long ebx; //可执行文件路径的指针(regs.ebx中
    1. long ecx; //命令行参数的指针(regs.ecx中)
    1. long edx; //环境变量的指针(regs.edx中)。
    1. long esi;
    1. long edi;
    1. long ebp;
    1. long eax;
    1. int xds;
    1. int xes;
    1. long orig_eax;
    1. long eip;
    1. int xcs;
    1. long eflags;
    1. long esp;
    1. int xss;
    1. };


    1. asmlinkage int sys_execve(struct pt_regs regs)
    1. {
    1. int error;
    1. char * filename;
    1. filename = getname((char *) regs.ebx);//ebx为"/bin/echo",把字符串从用户空间拷贝到系统空间
    1. error = PTR_ERR(filename);//判断是否出错
    1. if (IS_ERR(filename))
    1. goto out;
    1. //文件名 参数 NULL 传入副本
    1. error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);//ecx为args,edx为NULL
    1. if (error == 0)
    1. current->ptrace &= ~PT_DTRACE;
    1. putname(filename);//将之前为文件名分配的空间释放掉
    1. out:
    1. return error;
    1. }

linux内核每种被注册的可执行程序格式都用linux_bin_fmt来存储,其中记录了可执行程序的加载和执行函数,同时我们需要一种方法来保存可执行程序的信息, 比如可执行文件的路径, 运行的参数和环境变量等信息,即linux_bin_prm结构
    1. /*
    1. * This structure is used to hold the arguments that are used when loading binaries.
    1. */
    1. struct linux_binprm {
    1. char buf[BINPRM_BUF_SIZE]; // 保存可执行文件的头128字节
    1. #ifdef CONFIG_MMU
    1. struct vm_area_struct *vma;
    1. unsigned long vma_pages;
    1. #else
    1. # define MAX_ARG_PAGES 32
    1. struct page *page[MAX_ARG_PAGES];
    1. #endif
    1. struct mm_struct *mm;
    1. unsigned long p; /* current top of mem , 当前内存页最高地址*/
    1. unsigned int
    1. cred_prepared:1,/* true if creds already prepared (multiple
    1. * preps happen for interpreters) */
    1. cap_effective:1;/* true if has elevated effective capabilities,
    1. * false if not; except for init which inherits
    1. * its parent's caps anyway */
    1. #ifdef __alpha__
    1. unsigned int taso:1;
    1. #endif
    1. unsigned int recursion_depth; /* only for search_binary_handler() */
    1. struct file * file; /* 要执行的文件 */
    1. struct cred *cred; /* new credentials */
    1. int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
    1. unsigned int per_clear; /* bits to clear in current->personality */
    1. int argc, envc; /* 命令行参数和环境变量数目 */
    1. const char * filename; /* Name of binary as seen by procps, 要执行的文件的名称 */
    1. const char * interp; /* Name of the binary really executed. Most
    1. of the time same as filename, but could be
    1. different for binfmt_{misc,script} 要执行的文件的真实名称,通常和filename相同 */
    1. unsigned interp_flags;
    1. unsigned interp_data;
    1. unsigned long loader, exec;
    1. };

    1. int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
    1. {
    1. struct linux_binprm bprm;//存储可执行文件信息
    1. struct file *file;
    1. int retval;
    1. int i;
    1. file = open_exec(filename);//打开目标文件 /bin/echo
    1. retval = PTR_ERR(file);
    1. if (IS_ERR(file))
    1. return retval;
    1. bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);//1024*32减去一个指针的大小,当前页最高地址
    1. memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); //page指针数组初始化为0
    1. bprm.file = file;
    1. bprm.filename = filename;
    1. bprm.sh_bang = 0;//可执行文件属性.0表示二进制
    1. bprm.loader = 0;
    1. bprm.exec = 0;//参数的起始地址(从上往下方向)
    1. if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {//对指针数组argv[]中参数的个数进行计数,而bprm.p / sizeof(void *)表示允许的最大值,由于agrv[]是在用户空间而不在系统空间
    1. allow_write_access(file);//防止其他进程在读入可执行文件期间通过内存映射改变它的内容
    1. fput(file);
    1. return bprm.argc;
    1. }
    1. if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {//同上环境变量
    1. allow_write_access(file);
    1. fput(file);
    1. return bprm.envc;
    1. }
    1. retval = prepare_binprm(&bprm);//见下面的代码,为bprm结构做准备,读取128个字节到缓冲区
    1. if (retval < 0)
    1. goto out;
    1. retval = copy_strings_kernel(1, &bprm.filename, &bprm);//由于bprm.filename已经在系统空间了,所以copy_strings_kernel从系统空间中拷贝
    1. if (retval < 0)
    1. goto out;
    1. bprm.exec = bprm.p;//参数的起始地址
    1. retval = copy_strings(bprm.envc, envp, &bprm);//从用户空间拷贝,参考copy_strings_kernel
    1. if (retval < 0)
    1. goto out;
    1. retval = copy_strings(bprm.argc, argv, &bprm);//从用户空间拷贝,参考copy_strings_kernel
    1. if (retval < 0)
    1. goto out;
    1. retval = search_binary_handler(&bprm,regs);//已经从可执行文件头部读入了128个字节存放在bprm的缓冲区,而且运行所需的参数和环境变量也已经搜集在bprm中,现在就由formats队列中的成员逐个来认领,谁要识别到它代表的可执行文件格式,运行的时候就交给它
    1. if (retval >= 0)
    1. /* execve success */
    1. return retval;
    1. out:
    1. /*出错,返回inode和释放argv的页 Something went wrong, return the inode and free the argument pages*/
    1. allow_write_access(bprm.file);
    1. if (bprm.file)
    1. fput(bprm.file);//释放文件对象
    1. for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
    1. struct page * page = bprm.page[i];
    1. if (page)
    1. __free_page(page);
    1. }
    1. return retval;
    1. }
分析prepare_binprm
    1. int prepare_binprm(struct linux_binprm *bprm)
    1. {
    1. int mode;
    1. struct inode * inode = bprm->file->f_dentry->d_inode;//获取打开的文件节点
    1. mode = inode->i_mode;//类型
    1. /* Huh? We had already checked for MAY_EXEC, WTF do we check this? */
    1. if (!(mode & 0111)) /* with at least _one_ execute bit set最少可执行状态 */
    1. return -EACCES;
    1. if (bprm->file->f_op == NULL)//针对文件操作不允许为空
    1. return -EACCES;
    1. bprm->e_uid = current->euid;//继承uid
    1. bprm->e_gid = current->egid;//继承gid
    1. if(!IS_NOSUID(inode)) {//是否是SUID
    1. /* Set-uid? */
    1. if (mode & S_ISUID)//如果有SUID.那就设置
    1. bprm->e_uid = inode->i_uid;
    1. /* Set-gid? */
    1. /*
    1. * If setgid is set but no group execute bit then this
    1. * is a candidate for mandatory locking, not a setgid
    1. * executable.
    1. */
    1. if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
    1. bprm->e_gid = inode->i_gid;
    1. }
    1. /* We don't have VFS support for capabilities yet */
    1. cap_clear(bprm->cap_inheritable);
    1. cap_clear(bprm->cap_permitted);
    1. cap_clear(bprm->cap_effective);
    1. /* To support inheritance of root-permissions and suid-root
    1. * executables under compatibility mode, we raise all three
    1. * capability sets for the file.
    1. *
    1. * If only the real uid is 0, we only raise the inheritable
    1. * and permitted sets of the executable file.
    1. */
    1. if (!issecure(SECURE_NOROOT)) {
    1. if (bprm->e_uid == 0 || current->uid == 0) {
    1. cap_set_full(bprm->cap_inheritable);
    1. cap_set_full(bprm->cap_permitted);
    1. }
    1. if (bprm->e_uid == 0)
    1. cap_set_full(bprm->cap_effective);
    1. }
    1. memset(bprm->buf,0,BINPRM_BUF_SIZE);//从可执行文件中读入开头的128个字节到linux_binprm结构bprm中的缓冲区
    1. return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
    1. }
inux内核对所支持的每种可执行的程序类型都有个struct linux_binfmt的数据结构,定义如下 
search_binary_handler,已经从可执行文件头部读入了128个字节存放在bprm的缓冲区,而且运行所需的参数和环境变量也已经搜集在bprm中,现在就由formats队列中的成员逐个来认领,谁要识别到它代表的可执行文件格式,运行的时候就交给它,代码如下
    1. struct linux_binfmt {
    1. struct list_head lh;
    1. struct module *module;
    1. int (*load_binary)(struct linux_binprm *);//通过读存放在可执行文件中的信息为当前进程建立一个新的执行环境
    1. int (*load_shlib)(struct file *);//用于动态的把一个共享库捆绑到一个已经在运行的进程, 这是由uselib()系统调用激活的
    1. int (*core_dump)(struct coredump_params *cprm);//在名为core的文件中, 存放当前进程的执行上下文. 这个文件通常是在进程接收到一个缺省操作为”dump”的信号时被创建的, 其格式取决于被执行程序的可执行类型
    1. unsigned long min_coredump; /* minimal dump size */
    1. };


    1. int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
    1. {
    1. int try,retval=0;
    1. struct linux_binfmt *fmt;
    1. ......
    1. //一共2层循环,外出循环式为了安装了模块后再试一次
    1. for (try=0; try<2; try++) {
    1. read_lock(&binfmt_lock);
    1. //所有的linux_binfmt对象都处于一个链表中, 第一个元素的地址存放在formats变量中
    1. for (fmt = formats ; fmt ; fmt = fmt->next) {
    1. int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
    1. if (!fn)//是否是可执行文件
    1. continue;
    1. if (!try_inc_mod_count(fmt->module))//是否是动态安装库
    1. continue;
    1. read_unlock(&binfmt_lock);
    1. retval = fn(bprm, regs);//加载,如果谁都不认识,返回-ENOEXEC表示对不上号
    1. if (retval >= 0) {
    1. put_binfmt(fmt);
    1. allow_write_access(bprm->file);
    1. if (bprm->file)
    1. fput(bprm->file);
    1. bprm->file = NULL;
    1. current->did_exec = 1;
    1. return retval;
    1. }
    1. read_lock(&binfmt_lock);
    1. put_binfmt(fmt);
    1. if (retval != -ENOEXEC)//只要不是对不上号,就退出循环
    1. break;
    1. if (!bprm->file) {
    1. read_unlock(&binfmt_lock);
    1. return retval;
    1. }
    1. }
    1. read_unlock(&binfmt_lock);
    1. if (retval != -ENOEXEC) {
    1. break;
    1. #ifdef CONFIG_KMOD
    1. }else{
    1. #define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
    1. char modname[20];
    1. if (printable(bprm->buf[0]) &&
    1. printable(bprm->buf[1]) &&
    1. printable(bprm->buf[2]) &&
    1. printable(bprm->buf[3]))
    1. break; /* -ENOEXEC */
    1. sprintf(modname, "binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
    1. request_module(modname);//试着将相应的模块装入,一共进行两次循环,正是为了在安装了模块以后再来试一次
    1. #endif
    1. }
    1. }
    1. return retval;
    1. }
     
    1. struct exec
    1. {
    1. unsigned long a_info; /* Use macros N_MAGIC, etc for access */
    1. unsigned a_text; /* length of text, in bytes */
    1. unsigned a_data; /* length of data, in bytes */
    1. unsigned a_bss; /* length of uninitialized data area for file, in bytes */
    1. unsigned a_syms; /* length of symbol table data in file, in bytes */
    1. unsigned a_entry; /* start address */
    1. unsigned a_trsize; /* length of relocation info for text, in bytes */
    1. unsigned a_drsize; /* length of relocation info for data, in bytes */
    1. };

    我们假设/bin/echo是a.out格式。所以fn(bprm, regs),执行的代码是load_aout_binary。
    1. static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
    1. {
    1. struct exec ex;
    1. unsigned long error;
    1. unsigned long fd_offset;
    1. unsigned long rlim;
    1. int retval;
    1. ex = *((struct exec *) bprm->buf); //128字节可执行文件头部
    1. if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
    1. N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
    1. N_TRSIZE(ex) || N_DRSIZE(ex) ||
    1. bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
    1. return -ENOEXEC;//匹配不会返回-ENOEXEC
    1. }
    1. fd_offset = N_TXTOFF(ex);//根据代码的特性取得代码段在目标文件中的起始位置
    1. /* Check initial limits. This avoids letting people circumvent
    1. * size limits imposed on them by creating programs with large
    1. * arrays in the data or bss.
    1. */
    1. rlim = current->rlim[RLIMIT_DATA].rlim_cur;//获取限制条件
    1. if (rlim >= RLIM_INFINITY)
    1. rlim = ~0;
    1. if (ex.a_data + ex.a_bss > rlim)//代码段和bss段的总和不能超过限制
    1. return -ENOMEM;
    1. /* Flush all traces of the currently running executable */
    1. retval = flush_old_exec(bprm);//到了"与过去告别"的时候了,这种"告别过去"意味着放弃从父进程"继承"下来的全部用户空间,不管是通过复制还是通过指针共享继承下来的
    1. if (retval)
    1. return retval;
    1. /* OK, This is the point of no return */
    1. #if !defined(__sparc__)
    1. set_personality(PER_LINUX);
    1. #else
    1. set_personality(PER_SUNOS);
    1. #if !defined(__sparc_v9__)
    1. memcpy(current->thread.core_exec, &ex, sizeof(struct exec));
    1. #endif
    1. #endif
    1. current->mm->end_code = ex.a_text +
    1. (current->mm->start_code = N_TXTADDR(ex));//代码段起始和结束地址,详细请看紧邻本代码段的相关代码
    1. current->mm->end_data = ex.a_data +
    1. (current->mm->start_data = N_DATADDR(ex));//数据段起始和结束地址,数据段起始地址就是代码段结束地址
    1. current->mm->brk = ex.a_bss +
    1. (current->mm->start_brk = N_BSSADDR(ex));//bss段起始和结束地址,bss起始地址就是数据段结束地址
    1. current->mm->rss = 0;
    1. current->mm->mmap = NULL;
    1. compute_creds(bprm);//确定是否具有其权限
    1. current->flags &= ~PF_FORKNOEXEC;
    1. #ifdef __sparc__
    1. if (N_MAGIC(ex) == NMAGIC) {//其他格式
    1. loff_t pos = fd_offset;//elf代码段偏移地址
    1. /* Fuck me plenty... */
    1. error = do_brk(N_TXTADDR(ex), ex.a_text);
    1. bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
    1. ex.a_text, &pos);
    1. error = do_brk(N_DATADDR(ex), ex.a_data);
    1. bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
    1. ex.a_data, &pos);
    1. goto beyond_if;
    1. }
    1. #endif
    1. if (N_MAGIC(ex) == OMAGIC) {//如果魔术是OMAGIC
    1. unsigned long text_addr, map_size;
    1. loff_t pos;
    1. text_addr = N_TXTADDR(ex);//代码段起始地址
    1. #if defined(__alpha__) || defined(__sparc__)
    1. pos = fd_offset;//根据代码的特性取得代码段在目标文件中的起始位置
    1. map_size = ex.a_text+ex.a_data + PAGE_SIZE - 1;//代码段和数据段的大小
    1. #else
    1. pos = 32;
    1. map_size = ex.a_text+ex.a_data;
    1. #endif
    1. error = do_brk(text_addr & PAGE_MASK, map_size);//为正文段和数据段合在一起分配空间
    1. if (error != (text_addr & PAGE_MASK)) {//出错
    1. send_sig(SIGKILL, current, 0);//杀死当前进程
    1. return error;
    1. }
    1. error = bprm->file->f_op->read(bprm->file, (char *)text_addr,
    1. ex.a_text+ex.a_data, &pos);//然后就把这两部分从文件中读进来,这样就有了vm_struct结构,对应的页目录表项,页表项,页面,而且页面已经放入了文件中的代码段和数据段
    1. if (error < 0) {
    1. send_sig(SIGKILL, current, 0);
    1. return error;
    1. }
    1. flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data);
    1. } else {//如果魔术不是OMAGIC
    1. static unsigned long error_time, error_time2;
    1. if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
    1. (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ)
    1. {
    1. printk(KERN_NOTICE "executable not page aligned\n");
    1. error_time2 = jiffies;
    1. }
    1. if ((fd_offset & ~PAGE_MASK) != 0 &&
    1. (jiffies-error_time) > 5*HZ)
    1. {
    1. printk(KERN_WARNING
    1. "fd_offset is not page aligned. Please convert program: %s\n",
    1. bprm->file->f_dentry->d_name.name);
    1. error_time = jiffies;
    1. }
    1. if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {//如果没有mmap函数或者代码段及数据段的长度不与页面大小对齐
    1. loff_t pos = fd_offset;
    1. do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);//和上面的方式一样,正文段和数据段合在一起分配空间
    1. bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
    1. ex.a_text+ex.a_data, &pos);//然后就把这两部分从文件中读进来,这样就有了vm_struct结构,对应的页目录表项,页表项,页面,而且页面已经放入了文件中的代码段和数据段
    1. flush_icache_range((unsigned long) N_TXTADDR(ex),
    1. (unsigned long) N_TXTADDR(ex) +
    1. ex.a_text+ex.a_data);
    1. goto beyond_if;
    1. }
    1. down(current->mm->mmap_sem);
    1. error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,//如果有mmap,将文件的代码段映射到进程的用户空间
    1. PROT_READ | PROT_EXEC,
    1. MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
    1. fd_offset);
    1. up(current->mm->mmap_sem);
    1. if (error != N_TXTADDR(ex)) {
    1. send_sig(SIGKILL, current, 0);
    1. return error;
    1. }
    1. down(current->mm->mmap_sem);
    1. error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,///如果有mmap,将文件的数据段映射到进程的用户空间
    1. PROT_READ | PROT_WRITE | PROT_EXEC,
    1. MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
    1. fd_offset + ex.a_text);//数据段在文件中的偏移是fd_offset + ex.a_text
    1. up(current->mm->mmap_sem);
    1. if (error != N_DATADDR(ex)) {
    1. send_sig(SIGKILL, current, 0);
    1. return error;
    1. }
    1. }
    1. beyond_if:
    1. set_binfmt(&aout_format);//current->binfmt = aout_format;
    1. set_brk(current->mm->start_brk, current->mm->brk);//为可执行文件的bss段分配空间并建立起页面映射
    1. retval = setup_arg_pages(bprm); //在用户空间的堆栈区顶部为进程建立起一个虚拟区间,并将执行参数以及环境变量所占的物理页面与此虚拟区间建立起映射
    1. if (retval < 0) {
    1. /* Someone check-me: is this error path enough? */
    1. send_sig(SIGKILL, current, 0);
    1. return retval;
    1. }
    1. current->mm->start_stack =
    1. (unsigned long) create_aout_tables((char *) bprm->p, bprm);//把一些变量放入用户堆栈中,返回的是STACK_TOP减去图中参数所占空间,也就是堆栈开始的虚拟地址
    1. #ifdef __alpha__
    1. regs->gp = ex.a_gpvalue;
    1. #endif
    1. //起始地址 ,栈指针
    1. start_thread(regs, ex.a_entry, current->mm->start_stack);//设置系统堆栈中的eip,esp
    1. if (current->ptrace & PT_PTRACED)
    1. send_sig(SIGTRAP, current, 0);
    1. return 0;
    1. }

复制信号处理函数指针数组,放弃用户空间或者不跟父进程共享内存空间,如果此进程实际为一个线程组的线程,那就从线程组中脱离,改变信号处理模式为默认处理,关闭file文件对象
    1. int flush_old_exec(struct linux_binprm * bprm)
    1. {
    1. char * name;
    1. int i, ch, retval;
    1. struct signal_struct * oldsig;
    1. /*
    1. * Make sure we have a private signal table
    1. */
    1. oldsig = current->sig;//获取当前进程信号处理函数
    1. retval = make_private_signals();//如果子进程通过指针来共享父进程的信号处理表,就要把它复制过来
    1. if (retval) goto flush_failed;
    1. /*
    1. * Release all of the old mmap stuff
    1. */
    1. retval = exec_mmap();//从父进程继承下来的用户空间就是在这里放弃的
    1. if (retval) goto mmap_failed;
    1. /* This is the point of no return */
    1. release_old_signals(oldsig);//如果子进程通过指针来共享父进程的信号处理表,那么就要减少oldsig->count的计数
    1. current->sas_ss_sp = current->sas_ss_size = 0;
    1. if (current->euid == current->uid && current->egid == current->gid)
    1. current->dumpable = 1;
    1. name = bprm->filename;
    1. for (i=0; (ch = *(name++)) != '\0';) {
    1. if (ch == '/')
    1. i = 0;
    1. else
    1. if (i < 15)
    1. current->comm[i++] = ch;
    1. }
    1. current->comm[i] = '\0';//comm[],用于保存进程所执行的程序名,所以还要把bprm->filename的目标程序路径名的最后一段抄过去
    1. flush_thread();//不用关心
    1. de_thread(current);//如果当前进程是一个线程,从线程组中脱离出来
    1. if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
    1. permission(bprm->file->f_dentry->d_inode,MAY_READ))
    1. current->dumpable = 0;
    1. /* An exec changes our domain. We are no longer part of the thread
    1. group */
    1. current->self_exec_id++;
    1. flush_signal_handlers(current);//原来指向用户空间子程序的,现在设为SIG_DEL,表示采取预设的响应方式
    1. flush_old_files(current->files);//对原有已打开文件的处理
    1. return 0;
    1. mmap_failed:
    1. flush_failed:
    1. spin_lock_irq(current->sigmask_lock);
    1. if (current->sig != oldsig)
    1. kfree(current->sig);
    1. current->sig = oldsig;
    1. spin_unlock_irq(current->sigmask_lock);
    1. return retval;
    1. }
flush_old_exec中的make_private_signal
    1. static inline int make_private_signals(void)
    1. {
    1. struct signal_struct * newsig;
    1. if (atomic_read(current->sig->count) <= 1)//如果只是把父进程的信号处理表指针复制过来,而通过这指针来共享父进程的信号处理表,那么current->sig->count大于1
    1. return 0;
    1. newsig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL);//自立门户,和copy_sighand()基本相同
    1. if (newsig == NULL)
    1. return -ENOMEM;
    1. spin_lock_init(&newsig->siglock);
    1. atomic_set(&newsig->count, 1);
    1. memcpy(newsig->action, current->sig->action, sizeof(newsig->action));//拷贝
    1. spin_lock_irq(current->sigmask_lock);
    1. current->sig = newsig;//获得新的
    1. spin_unlock_irq(current->sigmask_lock);
    1. return 0;
    1. }
flush_old_exec的exec_mmap函数
    1. static int exec_mmap(void)
    1. {
    1. struct mm_struct * mm, * old_mm;
    1. old_mm = current->mm;
    1. if (old_mm && atomic_read(&old_mm->mm_users) == 1) {//如果共享计数为1时,表明对此空间的使用是独占的,也就是说这是从父进程复制过来的
    1. flush_cache_mm(old_mm);//释放缓存
    1. mm_release();//针对vfork.父进程减少信号量休眠,以后让子进程up将其唤醒
    1. exit_mmap(old_mm);//释放mm_struct数据结构以下的所有vm_area_struct数据结构(但是不包括mm_struct结构本身),并将页面表项设置为0
    1. flush_tlb_mm(old_mm);//释放tlb的缓存
    1. return 0;
    1. }
    1. mm = mm_alloc();//如果只是将指向mm_struct数据结构的指针复制给子进程,让子进程通过这个指针来共享父进程的用户空间,那就可以跳过释放用户空间这一步,直接就为子进程分配新的用户空间
    1. if (mm) {
    1. struct mm_struct *active_mm = current->active_mm;//
    1. if (init_new_context(current, mm)) {//空语句
    1. mmdrop(mm);
    1. return -ENOMEM;
    1. }
    1. /* Add it to the list of mm's */
    1. spin_lock(&mmlist_lock);
    1. list_add(&mm->mmlist, &init_mm.mmlist);
    1. spin_unlock(&mmlist_lock);
    1. task_lock(current);
    1. current->mm = mm;//新分配空户空间初始化
    1. current->active_mm = mm;//新分配的空户空间
    1. task_unlock(current);
    1. activate_mm(active_mm, mm);//切换到新的的用户空间
    1. mm_release();//将父进程唤醒
    1. if (old_mm) {
    1. if (active_mm != old_mm) BUG();//如果不一致,说明线程成为了进程
    1. mmput(old_mm);//减少mm->mm_users计数,因为已经不共享了
    1. return 0;
    1. }
    1. mmdrop(active_mm);//如果已经没有进程使用这空间.将旧的空间给释放掉
    1. return 0;
    1. }
    1. return -ENOMEM;
    1. }

 根据close_on_exec位图,里面存储着表示哪些文件在执行一个新目标程序时应予关闭的信息。根据这个位图的指示将这些文件关闭,并且将此位图清成全0。
    1. static inline void flush_old_files(struct files_struct * files)
    1. {
    1. long j = -1;
    1. write_lock(&files->file_lock);
    1. for (;;) {
    1. unsigned long set, i;
    1. j++;
    1. i = j * __NFDBITS;
    1. if (i >= files->max_fds || i >= files->max_fdset)
    1. break;
    1. set = files->close_on_exec->fds_bits[j];
    1. if (!set)
    1. continue;
    1. files->close_on_exec->fds_bits[j] = 0;
    1. write_unlock(&files->file_lock);
    1. for ( ; set ; i++,set >>= 1) {
    1. if (set & 1) {
    1. sys_close(i);
    1. }
    1. }
    1. write_lock(&files->file_lock);
    1. }
    1. write_unlock(&files->file_lock);
    1. }

    1. void mmput(struct mm_struct *mm)
    1. {
    1. if (atomic_dec_and_lock(&mm->mm_users, &mmlist_lock)) {//减去1,是否为0
    1. list_del(&mm->mmlist);
    1. spin_unlock(&mmlist_lock);
    1. exit_mmap(mm);//将页表设置为0
    1. mmdrop(mm);//将页表项与页表,本身mm_struct全部释放
    1. }
    1. }


    1. int setup_arg_pages(struct linux_binprm *bprm)
    1. {
    1. unsigned long stack_base;
    1. struct vm_area_struct *mpnt;
    1. int i;
    1. stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;//STACK_TOP为3GB
    1. bprm->p += stack_base;//bprm->p原来是MAX_ARG_PAGES*PAGE_SIZE-参数的数量,现在加上STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE,最后是STACK_TOP - 参数的数量
    1. if (bprm->loader)
    1. bprm->loader += stack_base;
    1. bprm->exec += stack_base;
    1. mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);//从slab中获取一页
    1. if (!mpnt)
    1. return -ENOMEM;
    1. downt->mm->mmap_sem);
    1. {
    1. mpnt->vm_mm = current->mm;//内存描述符
    1. mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;//堆栈段开始地址
    1. mpnt->vm_end = STACK_TOP;//堆栈段结束地址
    1. mpnt->vm_page_prot = PAGE_COPY;
    1. mpnt->vm_flags = VM_STACK_FLAGS;
    1. mpnt->vm_ops = NULL;
    1. mpnt->vm_pgoff = 0;
    1. mpnt->vm_file = NULL;
    1. mpnt->vm_private_data = (void *) 0;
    1. insert_vm_struct(current->mm, mpnt);//插入
    1. current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
    1. }
    1. for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
    1. struct page *page = bprm->page[i];//从page[0]开始
    1. if (page) {
    1. bprm->page[i] = NULL;
    1. current->mm->rss++;
    1. put_dirty_page(current,page,stack_base);//建立从堆栈虚拟空间到页面之前的映射
    1. }
    1. stack_base += PAGE_SIZE;//加一个页面的长度,4096个字节
    1. }
    1. up(&current->mm->mmap_sem);
    1. return 0;
    1. }


    1. static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
    1. {
    1. char **argv, **envp;
    1. unsigned long * sp;
    1. int argc = bprm->argc;//程序参数个数
    1. int envc = bprm->envc;//环境字符串个数
    1. //sp指向当前堆栈页面的栈顶指针
    1. sp = (unsigned long *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p);
    1. #ifdef __sparc__
    1. /* This imposes the proper stack alignment for a new process. */
    1. sp = (unsigned long *) (((unsigned long) sp) & ~7);
    1. if ((envc+argc+3)&1) --sp;
    1. #endif
    1. #ifdef __alpha__
    1. /* whee.. test-programs are so much fun. */
    1. put_user(0, --sp);
    1. put_user(0, --sp);
    1. if (bprm->loader) {
    1. put_user(0, --sp);
    1. put_user(0x3eb, --sp);
    1. put_user(bprm->loader, --sp);
    1. put_user(0x3ea, --sp);
    1. }
    1. put_user(bprm->exec, --sp);
    1. put_user(0x3e9, --sp);
    1. #endif
    1. //sp减去envc+1作为环境字符串指针数组的起始地址,最后一个数组元素为0
    1. sp -= envc+1;
    1. envp = (char **) sp;//指向环境字符串数组的起始地址
    1. sp -= argc+1;//同上
    1. argv = (char **) sp;
    1. #if defined(__i386__) || defined(__mc68000__) || defined(__arm__)
    1. //栈顶sp继续向低地址放扩展,存放envp,argv,argc
    1. put_user((unsigned long) envp,--sp);
    1. put_user((unsigned long) argv,--sp);
    1. #endif
    1. put_user(argc,--sp);
    1. current->mm->arg_start = (unsigned long) p;
    1. //循环对堆栈中的环境字符串指针数组初始化
    1. while (argc-->0) {
    1. char c;
    1. put_user(p,argv++);
    1. do {
    1. get_user(c,p++);
    1. } while (c);
    1. }
    1. put_user(NULL,argv);
    1. current->mm->arg_end = current->mm->env_start = (unsigned long) p;//env_start和arg_end
    1. while (envc-->0) {
    1. char c;
    1. put_user(p,envp++);
    1. do {
    1. get_user(c,p++);
    1. } while (c);
    1. }
    1. put_user(NULL,envp);
    1. current->mm->env_end = (unsigned long) p;//env_end
    1. return sp;//返回的是STACK_TOP减去图中参数所占空间
    1. }



linux内核情景分析之execve()的更多相关文章

  1. Linux内核情景分析之消息队列

    早期的Unix通信只有管道与信号,管道的缺点: 所载送的信息是无格式的字节流,不知道分界线在哪,也没通信规范,另外缺乏控制手段,比如保温优先级,管道机制的大小只有1页,管道很容易写满而读取没有及时,发 ...

  2. Linux内核情景分析的alloc_pages

    NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCON ...

  3. Linux内核情景分析之异常访问,用户堆栈的扩展

    情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...

  4. linux内核情景分析之exit与Wait

    //第一层系统调用 asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } 其主体是 ...

  5. linux内核情景分析之内核中的互斥操作

    信号量机制: struct sempahore是其结构,定义如下 struct semaphore { atomic_t count;//资源数目 int sleepers;//等待进程数目 wait ...

  6. linux内核情景分析之命名管道

    管道是一种"无名","无形文件,只可以近亲进程使用,不可以再任意两个进程通信使用,所以只能实现"有名","有形"的文件来实现就可以 ...

  7. linux内核情景分析之信号实现

    信号在进程间通信是异步的,每个进程的task_struct结构有一个sig指针,指向一个signal_struct结构 定义如下 struct signal_struct { atomic_t cou ...

  8. linux内核情景分析之强制性调度

    从系统调用返回到用户空间是否调度,从ret_with_reschedule可看出,是否真正调度,取决于当前进程的pcb中的need_resched是否设置为1,那如何设置为1取决于以下几种情况: 时间 ...

  9. linux内核情景分析之匿名管道

    管道的机制由pipe()创建,由pipe()所建立的管道两端都在同一进程.所以必须在fork的配合下,才可以在具有亲缘关系的进程通信 /* * sys_pipe() is the normal C c ...

随机推荐

  1. Flume是什么

    分布式流式实时收集日志文件系统,便于实时在线的流式计算,常配合 Storm 和 spark streming 使用. Flume is a distributed分布式的, reliable可靠的, ...

  2. CodeForces 522D Closest Equals 树状数组

    题意: 给出一个序列\(A\),有若干询问. 每次询问某个区间中值相等且距离最短的两个数,输出该距离,没有则输出-1. 分析: 令\(pre_i = max\{j| A_j = A_i, j < ...

  3. Android log 引发的血案

    今天调试代码,我打印了一个东西: Log.d("WelcomeActivity", res.str); 结果总是代码执行不到这一行的下一行,程序也没有挂掉.后来,我自己去想各种可能 ...

  4. 局域网&广域网&Internet&计算机通信过程

    1.局域网 覆盖范围小(100m以内),自己花钱购买,自己单位来维护,带宽固定的(10M,100M,1000M) 2.Internet ISP,有自己的机房,对网民提供访问Internet连接 3.广 ...

  5. jpg、png、gif图片格式的浅析

    原文地址:图片格式与设计那点事儿 之前面试时被面试官问到了jpg.gif.png格式的区别,当时就扯了一些,感觉都是扯淡,上网搜了下,分享一篇文章 第一次写技术博客,有不尽如人意的地方,还请见谅和指正 ...

  6. linux下给开启端口

    首先在这里要推荐一篇博文 http://blog.csdn.net/zht666/article/details/17505789 这篇文章写的很详细,里面包含了操作端口一些命令,我们操作端口其实就是 ...

  7. ACM基础算法入门及题目列表

    对于刚进入大学的计算机类同学来说,算法与程序设计竞赛算是不错的选择,因为我们每天都在解决问题,锻炼着解决问题的能力. 这里以TZOJ题目为例,如果为其他平台题目我会标注出来,同时我的主页也欢迎大家去访 ...

  8. 第一次软件工程作业补充plus

    一.代码的coding地址:coding地址. 二.<构建之法>读后问题以及感言(补充): 1.对于7.3MSF团队模型,7.2.6保持敏捷,预期和适应变化,中的"我们是预期变化 ...

  9. Android记事本11

    昨天: Activity的启动模式. 今天: 分析了一些网上的例子的源码. 遇到问题: 无.

  10. ocrosoft Contest1316 - 信奥编程之路~~~~~第三关 问题 I: 寻找大富翁

    http://acm.ocrosoft.com/problem.php?cid=1316&pid=8 题目描述 浙江杭州某镇共有n个人,请找出该镇上的前m个大富翁. 输入 输入包含多组测试用例 ...