unsigned long min_coredump; /* minimal dump size */ }; 每一种可执行文件类型被添加进内核时,都通过函数register_binfmt将该类可执行文对应的linux_binfmt结构件注册到内核。 static inline void register_binfmt(struct linux_binfmt *fmt) { __register_binfmt(fmt, 0); } 函数register_binfmt只是__register_binfmt的一个前端,真正完成注册操作的函数是__register_binfmt。可执行文件的注册就是将其对应的linux_binfmt结构链接到全局链表formats中。 void __register_binfmt(struct linux_binfmt * fmt, int insert) { BUG_ON(!fmt); write_lock(&binfmt_lock); insert ? list_add(&fmt->lh, &formats) : list_add_tail(&fmt->lh, &formats); write_unlock(&binfmt_lock); } 2 程序的运行函数 sys_execve是linux处理程序执行的系统调用,函数接收的参数含义: filenamei:可执行文件在用户空间路径名的地址; argv:以空字符结束的指针数组,每个指针指向一个命令行参数; envp:以空字符结束的指针数组,每个字符串表示一个环境变量; regs:指向通用寄存器组的指针。 asmlinkage int sys_execve(const char __user *filenamei,const char __user *const __user *argv,const char __user *const __user *envp, struct pt_regs *regs) { int error; char * filename; 将可执行文件路径名从用户空间拷到内核空间 filename = getname(filenamei); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; error = do_execve(filename, argv, envp, regs); putname(filename); out: return error; } 【sys_execve--->do_execve--->do_execve_common】 static int do_execve_common(const char *filename, struct user_arg_ptr argv, struct user_arg_ptr envp,struct pt_regs *regs) { struct linux_binprm *bprm; struct file *file; struct files_struct *displaced; bool clear_in_exec; int retval; const struct cred *cred = current_cred(); ...... current->flags &= ~PF_NPROC_EXCEEDED; 当进程刚创建还没用exec运行新程序时,子进程和父进程共享地址空间,函数unshare_files为子进程创建一个新的文件管理结构 retval = unshare_files(&displaced); if (retval) goto out_ret; 上面讲述的linux_binfmt结构描述了一种可执行文件格式,结构体linux_binprm描述了一个运行的可执行文件。动态分配一个linux_binprm结构,将用新的可执行文件填充该结构。 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) goto out_files; 结构struct cred描述用户id、组id等于安全和权能相关的结构,函数prepare_bprm_creds将为新进程创建一个新的struct cred,以脱离与父进程的共享 retval = prepare_bprm_creds(bprm); if (retval) goto out_free; 检测是否是一个安全的可执行文件 retval = check_unsafe_exec(bprm); if (retval < 0) goto out_free; clear_in_exec = retval; current->in_execve = 1; 打开可执行文件并返回一个文件描述结构指针 file = open_exec(filename); retval = PTR_ERR(file); if (IS_ERR(file)) goto out_unmark; 在用exec系统调用启动一个新进程时,是调度器跨越CPU移动该进程的一个很好的时机。这时候,该进程尚未执行,因此将其移动到另一个CPU不会带来对CPU高速缓存的负面效应。exec系统调用会调用函数sched_exec挑选当前负荷最少的CPU(而且进程得允许在该CPU上运行)。如果不是当前CPU,那么会使用migration_cpu_stop,向迁移线程发送一个迁移请求。 sched_exec(); bprm->file = file; bprm->filename = filename; bprm->interp = filename; 为新进程初始化一些内存管理信息 retval = bprm_mm_init(bprm); if (retval) goto out_file; 计算命令行参数个数 bprm->argc = count(argv, MAX_ARG_STRINGS); if ((retval = bprm->argc) < 0) goto out; 计算环境变量个数 bprm->envc = count(envp, MAX_ARG_STRINGS); if ((retval = bprm->envc) < 0) goto out; 初始化linux_binprm结构的euid和egid字段。用可执行文件的前128字节填充linux_binprm的buf字段,这些字节包含用于识别可执行文件格式的一个魔数和其他信息。 retval = prepare_binprm(bprm); if (retval < 0) goto out; 将文件路径名拷到新进程栈中 retval = copy_strings_kernel(1, &bprm->filename, bprm); if (retval < 0) goto out; bprm->exec = bprm->p; 将环境变量拷到进程栈中 retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) goto out; 将命令行参数拷到栈中 retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out; 函数search_binary_handler对可执行文件格式种类链表formats进行扫描,并调用每种可执行文件结构的load_binary方法对可执行文件进行操作,如果成功就停止扫描,表示找到了对应的可执行文件格式。 retval = search_binary_handler(bprm,regs); if (retval < 0) goto out; ...... |
|