linux内核情景分析之execve()
struct pt_regs {long ebx; //可执行文件路径的指针(regs.ebx中long ecx; //命令行参数的指针(regs.ecx中)long edx; //环境变量的指针(regs.edx中)。long esi;long edi;long ebp;long eax;int xds;int xes;long orig_eax;long eip;int xcs;long eflags;long esp;int xss;};
asmlinkage int sys_execve(struct pt_regs regs){int error;char * filename;- filename = getname((char *) regs.ebx);//ebx为"/bin/echo",把字符串从用户空间拷贝到系统空间
error = PTR_ERR(filename);//判断是否出错if (IS_ERR(filename))goto out;//文件名 参数 NULL 传入副本error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s);//ecx为args,edx为NULLif (error == 0)current->ptrace &= ~PT_DTRACE;putname(filename);//将之前为文件名分配的空间释放掉out:return error;}
/** This structure is used to hold the arguments that are used when loading binaries.*/struct linux_binprm {char buf[BINPRM_BUF_SIZE]; // 保存可执行文件的头128字节#ifdef CONFIG_MMUstruct vm_area_struct *vma;unsigned long vma_pages;#else# define MAX_ARG_PAGES 32struct page *page[MAX_ARG_PAGES];#endifstruct mm_struct *mm;unsigned long p; /* current top of mem , 当前内存页最高地址*/unsigned intcred_prepared:1,/* true if creds already prepared (multiple* preps happen for interpreters) */cap_effective:1;/* true if has elevated effective capabilities,* false if not; except for init which inherits* its parent's caps anyway */#ifdef __alpha__unsigned int taso:1;#endifunsigned int recursion_depth; /* only for search_binary_handler() */struct file * file; /* 要执行的文件 */struct cred *cred; /* new credentials */int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */unsigned int per_clear; /* bits to clear in current->personality */int argc, envc; /* 命令行参数和环境变量数目 */const char * filename; /* Name of binary as seen by procps, 要执行的文件的名称 */const char * interp; /* Name of the binary really executed. Mostof the time same as filename, but could bedifferent for binfmt_{misc,script} 要执行的文件的真实名称,通常和filename相同 */unsigned interp_flags;unsigned interp_data;unsigned long loader, exec;};
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs){struct linux_binprm bprm;//存储可执行文件信息struct file *file;int retval;int i;file = open_exec(filename);//打开目标文件 /bin/echoretval = PTR_ERR(file);if (IS_ERR(file))return retval;bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);//1024*32减去一个指针的大小,当前页最高地址memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); //page指针数组初始化为0bprm.file = file;bprm.filename = filename;bprm.sh_bang = 0;//可执行文件属性.0表示二进制bprm.loader = 0;bprm.exec = 0;//参数的起始地址(从上往下方向)if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {//对指针数组argv[]中参数的个数进行计数,而bprm.p / sizeof(void *)表示允许的最大值,由于agrv[]是在用户空间而不在系统空间allow_write_access(file);//防止其他进程在读入可执行文件期间通过内存映射改变它的内容fput(file);return bprm.argc;}if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {//同上环境变量allow_write_access(file);fput(file);return bprm.envc;}retval = prepare_binprm(&bprm);//见下面的代码,为bprm结构做准备,读取128个字节到缓冲区if (retval < 0)goto out;retval = copy_strings_kernel(1, &bprm.filename, &bprm);//由于bprm.filename已经在系统空间了,所以copy_strings_kernel从系统空间中拷贝if (retval < 0)goto out;bprm.exec = bprm.p;//参数的起始地址retval = copy_strings(bprm.envc, envp, &bprm);//从用户空间拷贝,参考copy_strings_kernelif (retval < 0)goto out;retval = copy_strings(bprm.argc, argv, &bprm);//从用户空间拷贝,参考copy_strings_kernelif (retval < 0)goto out;retval = search_binary_handler(&bprm,regs);//已经从可执行文件头部读入了128个字节存放在bprm的缓冲区,而且运行所需的参数和环境变量也已经搜集在bprm中,现在就由formats队列中的成员逐个来认领,谁要识别到它代表的可执行文件格式,运行的时候就交给它if (retval >= 0)/* execve success */return retval;out:/*出错,返回inode和释放argv的页 Something went wrong, return the inode and free the argument pages*/allow_write_access(bprm.file);if (bprm.file)fput(bprm.file);//释放文件对象for (i = 0 ; i < MAX_ARG_PAGES ; i++) {struct page * page = bprm.page[i];if (page)__free_page(page);}return retval;}
int prepare_binprm(struct linux_binprm *bprm){int mode;struct inode * inode = bprm->file->f_dentry->d_inode;//获取打开的文件节点mode = inode->i_mode;//类型/* Huh? We had already checked for MAY_EXEC, WTF do we check this? */if (!(mode & 0111)) /* with at least _one_ execute bit set最少可执行状态 */return -EACCES;if (bprm->file->f_op == NULL)//针对文件操作不允许为空return -EACCES;bprm->e_uid = current->euid;//继承uidbprm->e_gid = current->egid;//继承gidif(!IS_NOSUID(inode)) {//是否是SUID/* Set-uid? */if (mode & S_ISUID)//如果有SUID.那就设置bprm->e_uid = inode->i_uid;/* Set-gid? *//** If setgid is set but no group execute bit then this* is a candidate for mandatory locking, not a setgid* executable.*/if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))bprm->e_gid = inode->i_gid;}/* We don't have VFS support for capabilities yet */cap_clear(bprm->cap_inheritable);cap_clear(bprm->cap_permitted);cap_clear(bprm->cap_effective);/* To support inheritance of root-permissions and suid-root* executables under compatibility mode, we raise all three* capability sets for the file.** If only the real uid is 0, we only raise the inheritable* and permitted sets of the executable file.*/if (!issecure(SECURE_NOROOT)) {if (bprm->e_uid == 0 || current->uid == 0) {cap_set_full(bprm->cap_inheritable);cap_set_full(bprm->cap_permitted);}if (bprm->e_uid == 0)cap_set_full(bprm->cap_effective);}memset(bprm->buf,0,BINPRM_BUF_SIZE);//从可执行文件中读入开头的128个字节到linux_binprm结构bprm中的缓冲区return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);}
struct linux_binfmt {struct list_head lh;struct module *module;int (*load_binary)(struct linux_binprm *);//通过读存放在可执行文件中的信息为当前进程建立一个新的执行环境int (*load_shlib)(struct file *);//用于动态的把一个共享库捆绑到一个已经在运行的进程, 这是由uselib()系统调用激活的int (*core_dump)(struct coredump_params *cprm);//在名为core的文件中, 存放当前进程的执行上下文. 这个文件通常是在进程接收到一个缺省操作为”dump”的信号时被创建的, 其格式取决于被执行程序的可执行类型unsigned long min_coredump; /* minimal dump size */};
int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs){int try,retval=0;struct linux_binfmt *fmt;......//一共2层循环,外出循环式为了安装了模块后再试一次for (try=0; try<2; try++) {read_lock(&binfmt_lock);//所有的linux_binfmt对象都处于一个链表中, 第一个元素的地址存放在formats变量中for (fmt = formats ; fmt ; fmt = fmt->next) {int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;if (!fn)//是否是可执行文件continue;if (!try_inc_mod_count(fmt->module))//是否是动态安装库continue;read_unlock(&binfmt_lock);retval = fn(bprm, regs);//加载,如果谁都不认识,返回-ENOEXEC表示对不上号if (retval >= 0) {put_binfmt(fmt);allow_write_access(bprm->file);if (bprm->file)fput(bprm->file);bprm->file = NULL;current->did_exec = 1;return retval;}read_lock(&binfmt_lock);put_binfmt(fmt);if (retval != -ENOEXEC)//只要不是对不上号,就退出循环break;if (!bprm->file) {read_unlock(&binfmt_lock);return retval;}}read_unlock(&binfmt_lock);if (retval != -ENOEXEC) {break;#ifdef CONFIG_KMOD}else{#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))char modname[20];if (printable(bprm->buf[0]) &&printable(bprm->buf[1]) &&printable(bprm->buf[2]) &&printable(bprm->buf[3]))break; /* -ENOEXEC */sprintf(modname, "binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));request_module(modname);//试着将相应的模块装入,一共进行两次循环,正是为了在安装了模块以后再来试一次#endif}}return retval;}
struct exec{unsigned long a_info; /* Use macros N_MAGIC, etc for access */unsigned a_text; /* length of text, in bytes */unsigned a_data; /* length of data, in bytes */unsigned a_bss; /* length of uninitialized data area for file, in bytes */unsigned a_syms; /* length of symbol table data in file, in bytes */unsigned a_entry; /* start address */unsigned a_trsize; /* length of relocation info for text, in bytes */unsigned a_drsize; /* length of relocation info for data, in bytes */};
static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs){struct exec ex;unsigned long error;unsigned long fd_offset;unsigned long rlim;int retval;ex = *((struct exec *) bprm->buf); //128字节可执行文件头部if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||N_TRSIZE(ex) || N_DRSIZE(ex) ||bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {return -ENOEXEC;//匹配不会返回-ENOEXEC}fd_offset = N_TXTOFF(ex);//根据代码的特性取得代码段在目标文件中的起始位置/* Check initial limits. This avoids letting people circumvent* size limits imposed on them by creating programs with large* arrays in the data or bss.*/rlim = current->rlim[RLIMIT_DATA].rlim_cur;//获取限制条件if (rlim >= RLIM_INFINITY)rlim = ~0;if (ex.a_data + ex.a_bss > rlim)//代码段和bss段的总和不能超过限制return -ENOMEM;/* Flush all traces of the currently running executable */retval = flush_old_exec(bprm);//到了"与过去告别"的时候了,这种"告别过去"意味着放弃从父进程"继承"下来的全部用户空间,不管是通过复制还是通过指针共享继承下来的if (retval)return retval;/* OK, This is the point of no return */#if !defined(__sparc__)set_personality(PER_LINUX);#elseset_personality(PER_SUNOS);#if !defined(__sparc_v9__)memcpy(current->thread.core_exec, &ex, sizeof(struct exec));#endif#endifcurrent->mm->end_code = ex.a_text +(current->mm->start_code = N_TXTADDR(ex));//代码段起始和结束地址,详细请看紧邻本代码段的相关代码current->mm->end_data = ex.a_data +(current->mm->start_data = N_DATADDR(ex));//数据段起始和结束地址,数据段起始地址就是代码段结束地址current->mm->brk = ex.a_bss +(current->mm->start_brk = N_BSSADDR(ex));//bss段起始和结束地址,bss起始地址就是数据段结束地址current->mm->rss = 0;current->mm->mmap = NULL;compute_creds(bprm);//确定是否具有其权限current->flags &= ~PF_FORKNOEXEC;#ifdef __sparc__if (N_MAGIC(ex) == NMAGIC) {//其他格式loff_t pos = fd_offset;//elf代码段偏移地址/* Fuck me plenty... */error = do_brk(N_TXTADDR(ex), ex.a_text);bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),ex.a_text, &pos);error = do_brk(N_DATADDR(ex), ex.a_data);bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),ex.a_data, &pos);goto beyond_if;}#endifif (N_MAGIC(ex) == OMAGIC) {//如果魔术是OMAGICunsigned long text_addr, map_size;loff_t pos;text_addr = N_TXTADDR(ex);//代码段起始地址#if defined(__alpha__) || defined(__sparc__)pos = fd_offset;//根据代码的特性取得代码段在目标文件中的起始位置map_size = ex.a_text+ex.a_data + PAGE_SIZE - 1;//代码段和数据段的大小#elsepos = 32;map_size = ex.a_text+ex.a_data;#endiferror = do_brk(text_addr & PAGE_MASK, map_size);//为正文段和数据段合在一起分配空间if (error != (text_addr & PAGE_MASK)) {//出错send_sig(SIGKILL, current, 0);//杀死当前进程return error;}error = bprm->file->f_op->read(bprm->file, (char *)text_addr,ex.a_text+ex.a_data, &pos);//然后就把这两部分从文件中读进来,这样就有了vm_struct结构,对应的页目录表项,页表项,页面,而且页面已经放入了文件中的代码段和数据段if (error < 0) {send_sig(SIGKILL, current, 0);return error;}flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data);} else {//如果魔术不是OMAGICstatic unsigned long error_time, error_time2;if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&(N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ){printk(KERN_NOTICE "executable not page aligned\n");error_time2 = jiffies;}if ((fd_offset & ~PAGE_MASK) != 0 &&(jiffies-error_time) > 5*HZ){printk(KERN_WARNING"fd_offset is not page aligned. Please convert program: %s\n",bprm->file->f_dentry->d_name.name);error_time = jiffies;}if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {//如果没有mmap函数或者代码段及数据段的长度不与页面大小对齐loff_t pos = fd_offset;do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);//和上面的方式一样,正文段和数据段合在一起分配空间bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),ex.a_text+ex.a_data, &pos);//然后就把这两部分从文件中读进来,这样就有了vm_struct结构,对应的页目录表项,页表项,页面,而且页面已经放入了文件中的代码段和数据段flush_icache_range((unsigned long) N_TXTADDR(ex),(unsigned long) N_TXTADDR(ex) +ex.a_text+ex.a_data);goto beyond_if;}down(current->mm->mmap_sem);error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,//如果有mmap,将文件的代码段映射到进程的用户空间PROT_READ | PROT_EXEC,MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,fd_offset);up(current->mm->mmap_sem);if (error != N_TXTADDR(ex)) {send_sig(SIGKILL, current, 0);return error;}down(current->mm->mmap_sem);error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,///如果有mmap,将文件的数据段映射到进程的用户空间PROT_READ | PROT_WRITE | PROT_EXEC,MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,fd_offset + ex.a_text);//数据段在文件中的偏移是fd_offset + ex.a_textup(current->mm->mmap_sem);if (error != N_DATADDR(ex)) {send_sig(SIGKILL, current, 0);return error;}}beyond_if:set_binfmt(&aout_format);//current->binfmt = aout_format;set_brk(current->mm->start_brk, current->mm->brk);//为可执行文件的bss段分配空间并建立起页面映射retval = setup_arg_pages(bprm); //在用户空间的堆栈区顶部为进程建立起一个虚拟区间,并将执行参数以及环境变量所占的物理页面与此虚拟区间建立起映射if (retval < 0) {/* Someone check-me: is this error path enough? */send_sig(SIGKILL, current, 0);return retval;}current->mm->start_stack =(unsigned long) create_aout_tables((char *) bprm->p, bprm);//把一些变量放入用户堆栈中,返回的是STACK_TOP减去图中参数所占空间,也就是堆栈开始的虚拟地址#ifdef __alpha__regs->gp = ex.a_gpvalue;#endif//起始地址 ,栈指针start_thread(regs, ex.a_entry, current->mm->start_stack);//设置系统堆栈中的eip,espif (current->ptrace & PT_PTRACED)send_sig(SIGTRAP, current, 0);return 0;}
int flush_old_exec(struct linux_binprm * bprm){char * name;int i, ch, retval;struct signal_struct * oldsig;/** Make sure we have a private signal table*/oldsig = current->sig;//获取当前进程信号处理函数retval = make_private_signals();//如果子进程通过指针来共享父进程的信号处理表,就要把它复制过来if (retval) goto flush_failed;/** Release all of the old mmap stuff*/retval = exec_mmap();//从父进程继承下来的用户空间就是在这里放弃的if (retval) goto mmap_failed;/* This is the point of no return */release_old_signals(oldsig);//如果子进程通过指针来共享父进程的信号处理表,那么就要减少oldsig->count的计数current->sas_ss_sp = current->sas_ss_size = 0;if (current->euid == current->uid && current->egid == current->gid)current->dumpable = 1;name = bprm->filename;for (i=0; (ch = *(name++)) != '\0';) {if (ch == '/')i = 0;elseif (i < 15)current->comm[i++] = ch;}current->comm[i] = '\0';//comm[],用于保存进程所执行的程序名,所以还要把bprm->filename的目标程序路径名的最后一段抄过去flush_thread();//不用关心de_thread(current);//如果当前进程是一个线程,从线程组中脱离出来if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||permission(bprm->file->f_dentry->d_inode,MAY_READ))current->dumpable = 0;/* An exec changes our domain. We are no longer part of the threadgroup */current->self_exec_id++;flush_signal_handlers(current);//原来指向用户空间子程序的,现在设为SIG_DEL,表示采取预设的响应方式flush_old_files(current->files);//对原有已打开文件的处理return 0;mmap_failed:flush_failed:spin_lock_irq(current->sigmask_lock);if (current->sig != oldsig)kfree(current->sig);current->sig = oldsig;spin_unlock_irq(current->sigmask_lock);return retval;}
static inline int make_private_signals(void){struct signal_struct * newsig;if (atomic_read(current->sig->count) <= 1)//如果只是把父进程的信号处理表指针复制过来,而通过这指针来共享父进程的信号处理表,那么current->sig->count大于1return 0;newsig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL);//自立门户,和copy_sighand()基本相同if (newsig == NULL)return -ENOMEM;spin_lock_init(&newsig->siglock);atomic_set(&newsig->count, 1);memcpy(newsig->action, current->sig->action, sizeof(newsig->action));//拷贝spin_lock_irq(current->sigmask_lock);current->sig = newsig;//获得新的spin_unlock_irq(current->sigmask_lock);return 0;}
static int exec_mmap(void){struct mm_struct * mm, * old_mm;old_mm = current->mm;if (old_mm && atomic_read(&old_mm->mm_users) == 1) {//如果共享计数为1时,表明对此空间的使用是独占的,也就是说这是从父进程复制过来的flush_cache_mm(old_mm);//释放缓存mm_release();//针对vfork.父进程减少信号量休眠,以后让子进程up将其唤醒exit_mmap(old_mm);//释放mm_struct数据结构以下的所有vm_area_struct数据结构(但是不包括mm_struct结构本身),并将页面表项设置为0flush_tlb_mm(old_mm);//释放tlb的缓存return 0;}mm = mm_alloc();//如果只是将指向mm_struct数据结构的指针复制给子进程,让子进程通过这个指针来共享父进程的用户空间,那就可以跳过释放用户空间这一步,直接就为子进程分配新的用户空间if (mm) {struct mm_struct *active_mm = current->active_mm;//if (init_new_context(current, mm)) {//空语句mmdrop(mm);return -ENOMEM;}/* Add it to the list of mm's */spin_lock(&mmlist_lock);list_add(&mm->mmlist, &init_mm.mmlist);spin_unlock(&mmlist_lock);task_lock(current);current->mm = mm;//新分配空户空间初始化current->active_mm = mm;//新分配的空户空间task_unlock(current);activate_mm(active_mm, mm);//切换到新的的用户空间mm_release();//将父进程唤醒if (old_mm) {if (active_mm != old_mm) BUG();//如果不一致,说明线程成为了进程mmput(old_mm);//减少mm->mm_users计数,因为已经不共享了return 0;}mmdrop(active_mm);//如果已经没有进程使用这空间.将旧的空间给释放掉return 0;}return -ENOMEM;}
static inline void flush_old_files(struct files_struct * files){long j = -1;write_lock(&files->file_lock);for (;;) {unsigned long set, i;j++;i = j * __NFDBITS;if (i >= files->max_fds || i >= files->max_fdset)break;set = files->close_on_exec->fds_bits[j];if (!set)continue;files->close_on_exec->fds_bits[j] = 0;write_unlock(&files->file_lock);for ( ; set ; i++,set >>= 1) {if (set & 1) {sys_close(i);}}write_lock(&files->file_lock);}write_unlock(&files->file_lock);}
void mmput(struct mm_struct *mm){if (atomic_dec_and_lock(&mm->mm_users, &mmlist_lock)) {//减去1,是否为0list_del(&mm->mmlist);spin_unlock(&mmlist_lock);exit_mmap(mm);//将页表设置为0mmdrop(mm);//将页表项与页表,本身mm_struct全部释放}}
int setup_arg_pages(struct linux_binprm *bprm){unsigned long stack_base;struct vm_area_struct *mpnt;int i;stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;//STACK_TOP为3GBbprm->p += stack_base;//bprm->p原来是MAX_ARG_PAGES*PAGE_SIZE-参数的数量,现在加上STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE,最后是STACK_TOP - 参数的数量if (bprm->loader)bprm->loader += stack_base;bprm->exec += stack_base;mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);//从slab中获取一页if (!mpnt)return -ENOMEM;down(¤t->mm->mmap_sem);{mpnt->vm_mm = current->mm;//内存描述符mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;//堆栈段开始地址mpnt->vm_end = STACK_TOP;//堆栈段结束地址mpnt->vm_page_prot = PAGE_COPY;mpnt->vm_flags = VM_STACK_FLAGS;mpnt->vm_ops = NULL;mpnt->vm_pgoff = 0;mpnt->vm_file = NULL;mpnt->vm_private_data = (void *) 0;insert_vm_struct(current->mm, mpnt);//插入current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;}for (i = 0 ; i < MAX_ARG_PAGES ; i++) {struct page *page = bprm->page[i];//从page[0]开始if (page) {bprm->page[i] = NULL;current->mm->rss++;put_dirty_page(current,page,stack_base);//建立从堆栈虚拟空间到页面之前的映射}stack_base += PAGE_SIZE;//加一个页面的长度,4096个字节}up(¤t->mm->mmap_sem);return 0;}
static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm){char **argv, **envp;unsigned long * sp;int argc = bprm->argc;//程序参数个数int envc = bprm->envc;//环境字符串个数- //sp指向当前堆栈页面的栈顶指针
- sp = (unsigned long *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p);
#ifdef __sparc__/* This imposes the proper stack alignment for a new process. */sp = (unsigned long *) (((unsigned long) sp) & ~7);if ((envc+argc+3)&1) --sp;#endif#ifdef __alpha__/* whee.. test-programs are so much fun. */put_user(0, --sp);put_user(0, --sp);if (bprm->loader) {put_user(0, --sp);put_user(0x3eb, --sp);put_user(bprm->loader, --sp);put_user(0x3ea, --sp);}put_user(bprm->exec, --sp);put_user(0x3e9, --sp);#endif//sp减去envc+1作为环境字符串指针数组的起始地址,最后一个数组元素为0sp -= envc+1;envp = (char **) sp;//指向环境字符串数组的起始地址sp -= argc+1;//同上argv = (char **) sp;#if defined(__i386__) || defined(__mc68000__) || defined(__arm__)//栈顶sp继续向低地址放扩展,存放envp,argv,argcput_user((unsigned long) envp,--sp);put_user((unsigned long) argv,--sp);#endifput_user(argc,--sp);current->mm->arg_start = (unsigned long) p;//循环对堆栈中的环境字符串指针数组初始化while (argc-->0) {char c;put_user(p,argv++);do {get_user(c,p++);} while (c);}put_user(NULL,argv);current->mm->arg_end = current->mm->env_start = (unsigned long) p;//env_start和arg_endwhile (envc-->0) {char c;put_user(p,envp++);do {get_user(c,p++);} while (c);}put_user(NULL,envp);current->mm->env_end = (unsigned long) p;//env_endreturn sp;//返回的是STACK_TOP减去图中参数所占空间}
linux内核情景分析之execve()的更多相关文章
- Linux内核情景分析之消息队列
早期的Unix通信只有管道与信号,管道的缺点: 所载送的信息是无格式的字节流,不知道分界线在哪,也没通信规范,另外缺乏控制手段,比如保温优先级,管道机制的大小只有1页,管道很容易写满而读取没有及时,发 ...
- Linux内核情景分析的alloc_pages
NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCON ...
- Linux内核情景分析之异常访问,用户堆栈的扩展
情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...
- linux内核情景分析之exit与Wait
//第一层系统调用 asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } 其主体是 ...
- linux内核情景分析之内核中的互斥操作
信号量机制: struct sempahore是其结构,定义如下 struct semaphore { atomic_t count;//资源数目 int sleepers;//等待进程数目 wait ...
- linux内核情景分析之命名管道
管道是一种"无名","无形文件,只可以近亲进程使用,不可以再任意两个进程通信使用,所以只能实现"有名","有形"的文件来实现就可以 ...
- linux内核情景分析之信号实现
信号在进程间通信是异步的,每个进程的task_struct结构有一个sig指针,指向一个signal_struct结构 定义如下 struct signal_struct { atomic_t cou ...
- linux内核情景分析之强制性调度
从系统调用返回到用户空间是否调度,从ret_with_reschedule可看出,是否真正调度,取决于当前进程的pcb中的need_resched是否设置为1,那如何设置为1取决于以下几种情况: 时间 ...
- linux内核情景分析之匿名管道
管道的机制由pipe()创建,由pipe()所建立的管道两端都在同一进程.所以必须在fork的配合下,才可以在具有亲缘关系的进程通信 /* * sys_pipe() is the normal C c ...
随机推荐
- Error:Java home supplied via 'org.gradle.java.home' is invalid
Finally i found my solution. In the project root i found gradle.properties configure this java home ...
- Python 高级 I/O 多路复用
Table of Contents 前言 select selectors 结语 参考链接 前言 第一次接触和 I/O 多路复用相关的概念是在书 CSAPP1 的并发编程章节,当时在了解了这个概念后只 ...
- 浅谈==和equals的区别
java中的==和equals的区别? 看上面的代码,输出的结果是: true false true true 1.为什么会有上面的区别呢?==和equals比较的到底是什么呢? 搞清楚两者的区别其实 ...
- 孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2
孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第九天. 今天继续学习mongoDB的简单操作, ...
- 孤荷凌寒自学python那些事第二天
孤荷凌寒自学python第二天 Python的变量声明 (完整学习过程屏幕记录视频在文末,手写笔记在文末) Python的变量声明不必要显式指定变量类型 甚至也不需要进行显式的声明 比javascri ...
- Oz 创建Windows2008R2镜像
此tdl和auto文件只可定义windows disk bus以ide模式启动,不支持virtio. <template> <name>Windows-gushiren< ...
- Spring Cloud Config 搭建Config 服务
配置中心: open API 配置生效监控 一致性的K-V存储 统一配置的实时推送 配置全局恢复.备份.历史版本 高可用集群 通过config 获取配置,流程: 下面介绍,基于spring cloud ...
- CSLA多语言设置
1.在程序运行文件夹例如“\Bin\Debug\”中包含csla生成的资源文件: 2.在程序运行时,设置CSLA的当前语言为你想要的语言,例如:Csla.Properties.Resources.Cu ...
- ComboBox列表自定义类保存数据
之前没弄明白ComboBox还可以这样用. 先建一个ComboBox子项类,然后可以获取该项类做一些判断,关键是要重写ToString()方法. public class ComboItem { pu ...
- PAT 甲级 1037 Magic Coupon
https://pintia.cn/problem-sets/994805342720868352/problems/994805451374313472 The magic shop in Mars ...