Linux提供了execl、execlp、execle、execv、execvp和execve等六个用以执行一个可执行文件的函数(统称为exec函数,其间的差异在于对命令行参数和环境变量参数的传递方式不同)。这些函数的第一个参数都是要被执行的程序的路径,第二个参数则向程序传递了命令行参数,第三个参数则向程序传递环境变量。以上函数的本质都是调用在arch/i386/kernel/process.c文件中实现的系统调用sys_execve来执行一个可执行文件,该函数代码如下:

asmlinkage int sys_execve(struct pt_regs regs)

{

int error;

char * filename;

// 将可执行文件的名称装入到一个新分配的页面中

filename = getname((char __user *) regs.ebx);

error = PTR_ERR(filename);

if (IS_ERR(filename))

goto out;

// 执行可执行文件

error = do_execve(filename,

(char __user * __user *) regs.ecx,

(char __user * __user *) regs.edx,

&regs);

if (error == 0) {

task_lock(current);

current->ptrace &= ~PT_DTRACE;

task_unlock(current);

/* Make sure we don't return using sysenter.. */

set_thread_flag(TIF_IRET);

}

putname(filename);

out:

return error;

}

该系统调用所需要的参数pt_regs在include/asm-i386/ptrace.h文件中定义:

struct pt_regs {

long ebx;

long ecx;

long 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;

};

该参数描述了在执行该系统调用时,用户态下的CPU寄存器在核心态的栈中的保存情况。通过这个参数,sys_execve可以获得保存在用户空间的以下信息:可执行文件路径的指针(regs.ebx中)、命令行参数的指针(regs.ecx中)和环境变量的指针(regs.edx中)。

真正执行程序的功能则是在fs/exec.c文件中的do_execve函数中实现的:

int do_execve(char * filename, char __user *__user *argv,

char __user *__user *envp,     struct pt_regs * regs)

{

struct linux_binprm *bprm;        // 保存和要执行的文件相关的数据

struct file *file;

int retval;

int i;

retval = -ENOMEM;

bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);

if (!bprm)

goto out_ret;

// 打开要执行的文件,并检查其有效性(这里的检查并不完备)

file = open_exec(filename);

retval = PTR_ERR(file);

if (IS_ERR(file))

goto out_kfree;

// 在多处理器系统中才执行,用以分配负载最低的CPU来执行新程序

// 该函数在include/linux/sched.h文件中被定义如下:

// #ifdef CONFIG_SMP

// extern void sched_exec(void);

// #else

// #define sched_exec() {}

// #endif

sched_exec();

// 填充linux_binprm结构

bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);

bprm->file = file;

bprm->filename = filename;

bprm->interp = filename;

bprm->mm = mm_alloc();

retval = -ENOMEM;

if (!bprm->mm)

goto out_file;

// 检查当前进程是否在使用LDT,如果是则给新进程分配一个LDT

retval = init_new_context(current, bprm->mm);

if (retval  0)

goto out_mm;

// 继续填充linux_binprm结构

bprm->argc = count(argv, bprm->p / sizeof(void *));

if ((retval = bprm->argc)  0)

goto out_mm;

bprm->envc = count(envp, bprm->p / sizeof(void *));

if ((retval = bprm->envc)  0)

goto out_mm;

retval = security_bprm_alloc(bprm);

if (retval)

goto out;

// 检查文件是否可以被执行,填充linux_binprm结构中的e_uid和e_gid项

// 使用可执行文件的前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;

// 查询能够处理该可执行文件格式的处理函数,并调用相应的load_library方法进行处理

retval = search_binary_handler(bprm,regs);

if (retval >= 0) {

free_arg_pages(bprm);

// 执行成功

security_bprm_free(bprm);

acct_update_integrals(current);

kfree(bprm);

return retval;

}

out:

// 发生错误,返回inode,并释放资源

for (i = 0 ; i  MAX_ARG_PAGES ; i++) {

struct page * page = bprm->page;

if (page)

__free_page(page);

}

if (bprm->security)

security_bprm_free(bprm);

out_mm:

if (bprm->mm)

mmdrop(bprm->mm);

out_file:

if (bprm->file) {

allow_write_access(bprm->file);

fput(bprm->file);

}

out_kfree:

kfree(bprm);

out_ret:

return retval;

}

该函数用到了一个类型为linux_binprm的结构体来保存要要执行的文件相关的信息,该结构体在include/linux/binfmts.h文件中定义:

struct linux_binprm{

char buf[BINPRM_BUF_SIZE];    // 保存可执行文件的头128字节

struct page *page[MAX_ARG_PAGES];

struct mm_struct *mm;

unsigned long p;    // 当前内存页最高地址

int sh_bang;

struct file * file;    // 要执行的文件

int e_uid, e_gid;    // 要执行的进程的有效用户ID和有效组ID

kernel_cap_t cap_inheritable, cap_permitted, cap_effective;

void *security;

int argc, envc;    // 命令行参数和环境变量数目

char * filename;    // 要执行的文件的名称

char * interp;        // 要执行的文件的真实名称,通常和filename相同

unsigned interp_flags;

unsigned interp_data;

unsigned long loader, exec;

};

在该函数的最后,又调用了fs/exec.c文件中定义的search_binary_handler函数来查询能够处理相应可执行文件格式的处理器,并调用相应的load_library方法以启动进程。这里,用到了一个在include/linux/binfmts.h文件中定义的linux_binfmt结构体来保存处理相应格式的可执行文件的函数指针如下:

struct linux_binfmt {

struct linux_binfmt * next;

struct module *module;

// 加载一个新的进程

int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);

// 动态加载共享库

int (*load_shlib)(struct file *);

// 将当前进程的上下文保存在一个名为core的文件中

int (*core_dump)(long signr, struct pt_regs * regs, struct file * file);

unsigned long min_coredump;

};

Linux内核允许用户通过调用在include/linux/binfmt.h文件中定义的register_binfmt和unregister_binfmt函数来添加和删除linux_binfmt结构体链表中的元素,以支持用户特定的可执行文件类型。 在调用特定的load_binary函数加载一定格式的可执行文件后,程序将返回到sys_execve函数中继续执行。该函数在完成最后几步的清理工作后,将会结束处理并返回到用户态中,最后,系统将会将CPU分配给新加载的程序。

execve系统调用_execve系统调用分析相关推荐

  1. Linux内存管理 brk(),mmap()系统调用源码分析2:brk()的内存释放流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存释放流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  2. Linux内存管理 brk(),mmap()系统调用源码分析1:基础部分

    Linux内存管理 brk(),mmap(),munmap()系统调用源码分析 基础部分 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.c ...

  3. 系统调用捕获和分析—Ring3层LD_PRELOAD机制进行库函数劫持

    本文为毕业设计过程中学习相关知识.动手实践记录下来的完整笔记,通过阅读本系列文章,您可以从零基础了解系统调用的底层原理并对系统调用进行拦截.由于本人能力有限,文章中可能会出现部分错误信息,如有错误欢迎 ...

  4. Linux brk(),mmap()系统调用源码分析3:brk()的内存申请流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存申请流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  5. linux 系统调用_Linux系统调用初学者指南

    linux 系统调用 在过去的几年中,我一直在使用容器进行大量工作. 早些时候,我看到了朱利安·弗里德曼 ( Julien Friedman)的一次有趣的演讲,他在几行Go语言中编写了一个准容器. 它 ...

  6. 操作系统原理,系统调用,系统调用与库函数API等函数之间的调用关系,功能与机制设计,系统调用的执行过程与Linux系统调用执行示例,不同操作系统下的PCB

    操作系统原理,系统调用,功能与机制设计,系统调用的执行过程与Linux系统调用执行示例,不同操作系统下的PCB 一.系统调用:操作系统功能调用,用户在编程时可以调用的操作系统功能. 1.系统调用是操作 ...

  7. execve系统调用_execve()函数 Unix/Linux

    execve - 执行程序 内容简介 #include int execve(const char *filename, char *const argv[], char *const envp[]) ...

  8. 【Linux 内核】进程管理 ( 进程相关系统调用源码分析 | fork() 源码 | vfork() 源码 | clone() 源码 | _do_fork() 源码 | do_fork() 源码 )

    文章目录 一.fork 系统调用源码 二.vfork 系统调用源码 三.clone 系统调用源码 四._do_fork 函数源码 五.do_fork 函数源码 Linux 进程相关 " 系统 ...

  9. execve系统调用_系统调用execve的入口sys_execve() | 学步园

    /* * sys_execve() executes a new program. */ long sys_execve(const char __user *name, //需要执行的文件的绝对路径 ...

最新文章

  1. 邬贺铨院士:认识工业互联网
  2. 设计模式 — 行为型模式 — 策略模式
  3. How should I set up tag files for a multi-level directory hierarchy? kiss snow
  4. Android Paint、Path详解
  5. 代码实现中文命名实体识别(包括多种模型:HMM,CRF,BiLSTM,BiLSTM+CRF)
  6. 一个用于SAP UI5学习的脚手架应用,没有任何后台API的依赖
  7. 熟悉java环境实验报告_2018-2019-2 20175324实验二面向对象程序设计《Java开发环境的熟悉》实验报告...
  8. java 实现真正的随机数_关于java:SecureRandom的Android实现是否产生真正的随机数?...
  9. 叶金荣mysql教程_mysql优化--叶金荣老师讲座笔记
  10. android流量显示插件,[android]仿360状态,种流量监控桌面浮动显示
  11. 如果你是面试官,如何判断一个面试者的深度学习水平?
  12. c语言 有关文件读/写函数 详解
  13. 百度指数查关键词(惊到我啦)
  14. 程序员面试智力题总结
  15. 完全用计算机制作的三维动画,通过四个步骤告诉你三维动画怎么制作
  16. 8421码,5421码,2421码,余3码的区别
  17. 受制裁,即 Github 之后,Adobe 也开始大量封禁账号和服务了!
  18. CS3825EO 2X25W D类功放芯片 耐压5V-26V 可替换AD52068
  19. Java Map(hashmap)
  20. 用vim编辑时怎么复制粘贴外部内容(在ubuntu操作系统中)

热门文章

  1. TYVJ P1172 自然数拆分Lunatic版
  2. 《游戏脚本的设计与开发》-(RPG部分)3.8 通过脚本来自由控制游戏(一)
  3. 郭炜-C语言程序设计-程序设计与算法(一)-第一周
  4. 2019年全国大学生电子设计大学(D 题)简易电路特性测试仪(2)基础部分电路与代码
  5. 32位计算机中内存地址如何表示,内存地址是什么
  6. 单相半波可控整流电路仿真设计(任务书+lunwen+MATLAB仿真源文件)
  7. 熔断器Hystrix作用
  8. C语言函数嵌套定义问题
  9. 2021年诺贝尔物理学奖揭晓,复杂科学获得重视
  10. ImageView设置纯色图片颜色