Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)

5.execve()到底干了啥? 导语 很多童鞋有分析阅读Linux源代码的强烈愿望,可是Linux内核代码量庞大,大部分人不知道如何下手,以下是我分析Linux源代码的一些经验,仅供参考,有不实之处请大神指正! 1.要想阅读内核首先要进入内核,其中用户态程序进入内核态的主要方式是int 0x80中断,搞懂这条指令的执行过程是我们学习内核的第一步; 2.Linux中 阅读详情

execve系统调用
execve系统调用

我们前面提到了, fork, vfork等复制出来的进程是父进程的一个副本, 那么如何我们想加载新的程序, 可以通过execve来加载和启动新的程序。

x86架构下, 其实还实现了一个新的exec的系统调用叫做execveat(自linux-3.19后进入内核)

syscalls,x86: Add execveat() system call

exec()函数族

exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数。

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
                  ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

ELF文件格式以及可执行程序的表示
ELF可执行文件格式

Linux下标准的可执行文件格式是ELF.ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。它自最早在 System V 系统上出现后,被 xNIX 世界所广泛接受,作为缺省的二进制文件格式来使用。

但是linux也支持其他不同的可执行程序格式, 各个可执行程序的执行方式不尽相同, 因此linux内核每种被注册的可执行程序格式都用linux_bin_fmt来存储, 其中记录了可执行程序的加载和执行函数

同时我们需要一种方法来保存可执行程序的信息, 比如可执行文件的路径, 运行的参数和环境变量等信息,即linux_bin_prm结构

struct linux_bin_prm结构描述一个可执行程序

linux_binprm是定义在include/linux/binfmts.h中, 用来保存要要执行的文件相关的信息, 包括可执行程序的路径, 参数和环境变量的信息

/*
* 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_MMU
    struct vm_area_struct *vma;
    unsigned long vma_pages;
#else
# define MAX_ARG_PAGES  32
    struct page *page[MAX_ARG_PAGES];
#endif
    struct mm_struct *mm;
    unsigned long p; /* current top of mem , 当前内存页最高地址*/
    unsigned int
            cred_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;
#endif
    unsigned 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. Most
                               of the time same as filename, but could be
                               different for binfmt_{misc,script} 要执行的文件的真实名称,通常和filename相同  */
    unsigned interp_flags;
    unsigned interp_data;
    unsigned long loader, exec;
};

struct linux_binfmt可执行程序的结构

linux支持其他不同格式的可执行程序, 在这种方式下, linux能运行其他操作系统所编译的程序, 如MS-DOS程序, 活BSD Unix的COFF可执行格式, 因此linux内核用struct linux_binfmt来描述各种可执行程序。

linux内核对所支持的每种可执行的程序类型都有个struct linux_binfmt的数据结构,定义如下

linux_binfmt定义在include/linux/binfmts.h中

/*
  * This structure defines the functions that are used to load the binary formats that
  * linux accepts.
  */
struct linux_binfmt {
    struct list_head lh;
    struct module *module;
    int (*load_binary)(struct linux_binprm *);
    int (*load_shlib)(struct file *);
    int (*core_dump)(struct coredump_params *cprm);
    unsigned long min_coredump;     /* minimal dump size */
 };

其提供了3种方法来加载和执行可执行程序

  • load_binary

通过读存放在可执行文件中的信息为当前进程建立一个新的执行环境

  • load_shlib

用于动态的把一个共享库捆绑到一个已经在运行的进程, 这是由uselib()系统调用激活的

  • core_dump

在名为core的文件中, 存放当前进程的执行上下文. 这个文件通常是在进程接收到一个缺省操作为”dump”的信号时被创建的, 其格式取决于被执行程序的可执行类型

所有的linux_binfmt对象都处于一个链表中, 第一个元素的地址存放在formats变量中, 可以通过调用register_binfmt()和unregister_binfmt()函数在链表中插入和删除元素, 在系统启动期间, 为每个编译进内核的可执行格式都执行registre_fmt()函数. 当实现了一个新的可执行格式的模块正被装载时, 也执行这个函数, 当模块被卸载时, 执行unregister_binfmt()函数.

当我们执行一个可执行程序的时候, 内核会list_for_each_entry遍历所有注册的linux_binfmt对象, 对其调用load_binrary方法来尝试加载, 直到加载成功为止.

execve加载可执行程序的过程

内核中实际执行execv()或execve()系统调用的程序是do_execve(),这个函数先打开目标映像文件,并从目标文件的头部(第一个字节开始)读入若干(当前Linux内核中是128)字节(实际上就是填充ELF文件头,下面的分析可以看到),然后调用另一个函数search_binary_handler(),在此函数里面,它会搜索我们上面提到的Linux支持的可执行文件类型队列,让各种可执行程序的处理程序前来认领和处理。如果类型匹配,则调用load_binary函数指针所指向的处理函数来处理目标映像文件。在ELF文件格式中,处理函数是load_elf_binary函数,下面主要就是分析load_elf_binary函数的执行过程(说明:因为内核中实际的加载需要涉及到很多东西,这里只关注跟ELF文件的处理相关的代码):

sys_execve() > do_execve() > do_execveat_common > search_binary_handler() > load_elf_binary()

execve的入口函数sys_execve


系统调用号(体系结构相关)    类似与如下的形式
#define __NR_execve 117
__SYSCALL(117, sys_execve, 3)    arch/对应体系结构/include/uapi/asm/unistd.h, line 265

入口函数声明    

asmlinkage long sys_execve(const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp);    include/linux/syscalls.h, line 843

系统调用实现    SYSCALL_DEFINE3(execve,
const char __user , filename,
const char __user *const __user , argv,
const char __user const __user , envp)
{
return do_execve(getname(filename), argv, envp);
}    fs/exec.v 1710

execve系统调用的的入口点是体系结构相关的sys_execve, 该函数很快将工作委托给系统无关的do_execve函数

SYSCALL_DEFINE3(execve,
                const char __user *, filename,
                const char __user *const __user *, argv,
                const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}

通过参数传递了寄存集合和可执行文件的名称(filename), 而且还传递了指向了程序的参数argv和环境变量envp的指针
filename       可执行程序的名称
argv             程序的参数
envp            环境变量

指向程序参数argv和环境变量envp两个数组的指针以及数组中所有的指针都位于虚拟地址空间的用户空间部分。因此内核在当问用户空间内存时, 需要多加小心, 而__user注释则允许自动化工具来检测时候所有相关事宜都处理得当

do_execve函数

do_execve的定义在fs/exec.c中,参见 http://lxr.free-electrons.com/source/fs/exec.c?v=4.5#L1628

  • 更早期实现linux-2.4
代码过长, 没有经过do_execve_common的封装   
  •  linux-3.18引入execveat之前do_execve实现 
int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}   
  •  linux-3.19~至今引入execveat之后do_execve实现
 int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}   
  •     do_execveat的实现
int do_execveat(int fd, struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp,
int flags)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execveat_common(fd, filename, argv, envp, flags);
}

我们可以看到不同时期的演变, 早期的代码 do_execve就直接完成了自己的所有工作, 后来do_execve会调用更加底层的do_execve_common函数, 后来x86架构下引入了新的系统调用execveat, 为了使代码更加通用, do_execveat_common替代了原来的do_execve_common函数

早期的do_execve流程如下, 基本无差别, 可以作为参考

do_execve彿°çæµç¨

程序的加载do_execve_common和do_execveat_common

早期linux-2.4中直接由do_execve实现程序的加载和运行

linux-3.18引入execveat之前do_execve调用do_execve_common来完成程序的加载和运行

linux-3.19~至今引入execveat之后do_execve调用do_execveat_common来完成程序的加载和运行

在Linux中提供了一系列的函数,这些函数能用可执行文件所描述的新上下文代替进程的上下文。这样的函数名以前缀exec开始。所有的exec函数都是调用了execve()系统调用。

sys_execve接受参数:1.可执行文件的路径 2.命令行参数字符串 3.环境变量字符串

sys_execve是调用do_execve实现的。do_execve则是调用do_execveat_common实现的,依次执行以下操作:

  1. 调用unshare_files()为进程复制一份文件表
  2. 调用kzalloc()分配一份structlinux_binprm结构体
  3. 调用open_exec()查找并打开二进制文件
  4. 调用sched_exec()找到最小负载的CPU,用来执行该二进制文件
  5. 根据获取的信息,填充structlinux_binprm结构体中的file、filename、interp成员
  6. 调用bprm_mm_init()创建进程的内存地址空间,为新程序初始化内存管理.并调用init_new_context()检查当前进程是否使用自定义的局部描述符表;如果是,那么分配和准备一个新的LDT
  7. 填充structlinux_binprm结构体中的argc、envc成员
  8. 调用prepare_binprm()检查该二进制文件的可执行权限;最后,kernel_read()读取二进制文件的头128字节(这些字节用于识别二进制文件的格式及其他信息,后续会使用到)
  9. 调用copy_strings_kernel()从内核空间获取二进制文件的路径名称
  10. 调用copy_string()从用户空间拷贝环境变量和命令行参数
  11. 至此,二进制文件已经被打开,struct linux_binprm结构体中也记录了重要信息, 内核开始调用exec_binprm执行可执行程序
  12. 释放linux_binprm数据结构,返回从该文件可执行格式的load_binary中获得的代码

定义在fs/exec.c

/*
 * sys_execve() executes a new program.
 */
static int do_execveat_common(int fd, struct filename *filename,
                          struct user_arg_ptr argv,
                          struct user_arg_ptr envp,
                          int flags)
{
    char *pathbuf = NULL;
    struct linux_binprm *bprm;  /* 这个结构当然是非常重要的,下文,列出了这个结构体以便查询各个成员变量的意义   */
    struct file *file;
    struct files_struct *displaced;
    int retval;

    if (IS_ERR(filename))
            return PTR_ERR(filename);

    /*
     * We move the actual failure in case of RLIMIT_NPROC excess from
     * set*uid() to execve() because too many poorly written programs
     * don't check setuid() return code.  Here we additionally recheck
     * whether NPROC limit is still exceeded.
     */
    if ((current->flags & PF_NPROC_EXCEEDED) &&
        atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
            retval = -EAGAIN;
            goto out_ret;
    }

    /* We're below the limit (still or again), so we don't want to make
     * further execve() calls fail. */
    current->flags &= ~PF_NPROC_EXCEEDED;

    //  1.  调用unshare_files()为进程复制一份文件表;
    retval = unshare_files(&displaced);
    if (retval)
            goto out_ret;

    retval = -ENOMEM;

    //  2、调用kzalloc()在堆上分配一份structlinux_binprm结构体;
    bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
    if (!bprm)
            goto out_files;

    retval = prepare_bprm_creds(bprm);
    if (retval)
            goto out_free;

    check_unsafe_exec(bprm);
    current->in_execve = 1;

    //  3、调用open_exec()查找并打开二进制文件;
    file = do_open_execat(fd, filename, flags);
    retval = PTR_ERR(file);
    if (IS_ERR(file))
            goto out_unmark;

    //  4、调用sched_exec()找到最小负载的CPU,用来执行该二进制文件;
    sched_exec();

    //  5、根据获取的信息,填充structlinux_binprm结构体中的file、filename、interp成员;
    bprm->file = file;
    if (fd == AT_FDCWD || filename->name[0] == '/') {
            bprm->filename = filename->name;
    } else {
            if (filename->name[0] == '\0')
                    pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d", fd);
            else
                    pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d/%s",
                                        fd, filename->name);
            if (!pathbuf) {
                    retval = -ENOMEM;
                    goto out_unmark;
            }
            /*
             * Record that a name derived from an O_CLOEXEC fd will be
             * inaccessible after exec. Relies on having exclusive access to
             * current->files (due to unshare_files above).
             */
            if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
                    bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
            bprm->filename = pathbuf;
    }
    bprm->interp = bprm->filename;

    //  6、调用bprm_mm_init()创建进程的内存地址空间,并调用init_new_context()检查当前进程是否使用自定义的局部描述符表;如果是,那么分配和准备一个新的LDT;
    retval = bprm_mm_init(bprm);
    if (retval)
            goto out_unmark;

    //  7、填充structlinux_binprm结构体中的命令行参数argv,环境变量envp
    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;

    //  8、调用prepare_binprm()检查该二进制文件的可执行权限;最后,kernel_read()读取二进制文件的头128字节(这些字节用于识别二进制文件的格式及其他信息,后续会使用到);
    retval = prepare_binprm(bprm);
    if (retval < 0)
            goto out;

    //  9、调用copy_strings_kernel()从内核空间获取二进制文件的路径名称;
    retval = copy_strings_kernel(1, &bprm->filename, bprm);
    if (retval < 0)
            goto out;

    bprm->exec = bprm->p;

    //  10.1、调用copy_string()从用户空间拷贝环境变量
    retval = copy_strings(bprm->envc, envp, bprm);
    if (retval < 0)
            goto out;

    //  10.2、调用copy_string()从用户空间拷贝命令行参数;
    retval = copy_strings(bprm->argc, argv, bprm);
    if (retval < 0)
            goto out;
    /*
        至此,二进制文件已经被打开,struct linux_binprm结构体中也记录了重要信息;

        下面需要识别该二进制文件的格式并最终运行该文件
    */
    retval = exec_binprm(bprm);
    if (retval < 0)
            goto out;

    /* execve succeeded */
    current->fs->in_exec = 0;
    current->in_execve = 0;
    acct_update_integrals(current);
    task_numa_free(current);
    free_bprm(bprm);
    kfree(pathbuf);
    putname(filename);
    if (displaced)
            put_files_struct(displaced);
    return retval;

out:
    if (bprm->mm) {
            acct_arg_size(bprm, 0);
            mmput(bprm->mm);
    }

out_unmark:
    current->fs->in_exec = 0;
    current->in_execve = 0;

out_free:
    free_bprm(bprm);
    kfree(pathbuf);

out_files:
    if (displaced)
            reset_files_struct(displaced);
out_ret:
    putname(filename);
    return retval;
}


exec_binprm识别并加载二进程程序

每种格式的二进制文件对应一个struct linux_binprm结构体,load_binary成员负责识别该二进制文件的格式;

内核使用链表组织这些structlinux_binfmt结构体,链表头是formats。

接着do_execveat_common()继续往下看:

调用search_binary_handler()函数对linux_binprm的formats链表进行扫描,并尝试每个load_binary函数,如果成功加载了文件的执行格式,对formats的扫描终止。

static int exec_binprm(struct linux_binprm *bprm)
{
    pid_t old_pid, old_vpid;
    int ret;

    /* Need to fetch pid before load_binary changes it */
    old_pid = current->pid;
    rcu_read_lock();
    old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
    rcu_read_unlock();

    ret = search_binary_handler(bprm);
    if (ret >= 0) {
            audit_bprm(bprm);
            trace_sched_process_exec(current, old_pid, bprm);
            ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
            proc_exec_connector(current);
    }

    return ret;
}


search_binary_handler识别二进程程序

这里需要说明的是,这里的fmt变量的类型是struct linux_binfmt *, 但是这一个类型与之前在do_execveat_common()中的bprm是不一样的,

定义在fs/exec.c

/* 
* cycle the list of binary formats handler, until one recognizes the image 
*/ 
int search_binary_handler(struct linux_binprm *bprm) 
{ 
bool need_retry = IS_ENABLED(CONFIG_MODULES); 
struct linux_binfmt *fmt; 
int retval;

/* This allows 4 levels of binfmt rewrites before failing hard. */
if (bprm->recursion_depth > 5)
        return -ELOOP;

retval = security_bprm_check(bprm);
if (retval)
        return retval;

retval = -ENOENT;

retry: 
read_lock(&binfmt_lock);

//  遍历formats链表
list_for_each_entry(fmt, &formats, lh) {
        if (!try_module_get(fmt->module))
                continue;
        read_unlock(&binfmt_lock);
        bprm->recursion_depth++;

        // 遍历formats链表
        retval = fmt->load_binary(bprm);
        read_lock(&binfmt_lock);
        put_binfmt(fmt);
        bprm->recursion_depth--;
        if (retval < 0 && !bprm->mm) {
                /* we got to flush_old_exec() and failed after it */
                read_unlock(&binfmt_lock);
                force_sigsegv(SIGSEGV, current);
                return retval;
        }
        if (retval != -ENOEXEC || !bprm->file) {
                read_unlock(&binfmt_lock);
                return retval;
        }
}
read_unlock(&binfmt_lock);

if (need_retry) {
        if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
            printable(bprm->buf[2]) && printable(bprm->buf[3]))
                return retval;
        if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
                return retval;
        need_retry = false;
        goto retry;
}

return retval;
}

load_binary加载可执行程序

我们前面提到了,linux内核支持多种可执行程序格式, 每种格式都被注册为一个linux_binfmt结构, 其中存储了对应可执行程序格式加载函数等

格式                               linux_binfmt定义             load_binary            load_shlib                                   core_dump
a.out                              aout_format                     load_aout_binary       load_aout_library                         aout_core_dump
flat style executables    flat_format                        load_flat_binary          load_flat_shared_library             flat_core_dump
script脚本                      script_format                    load_script                  无                                                 无
misc_format                  misc_format                     load_misc_binary        无                                                 无
em86                            em86_format                    load_format                  无                                                无
elf_fdpic                       elf_fdpic_format                load_elf_fdpic_binary    无                                                elf_fdpic_core_dump
elf                                 elf_format                          load_elf_binary            load_elf_binary                            elf_core_dump

 

快速刷通PWN的第三天 快速刷通PWN的第三天 计划 声明:因为是自己学习的一个计划,所以可能并不是适用于所有人。但是尽可能考虑到由浅入深,由易到难。 昨天大概完成了 Protostar 的 Stack4 部分,今天继续后面题目的练习。 笔记 Stack5 题目源码如下: #include <stdlib.h> #include <unistd.h> #include <stdio.h&gt... 阅读详情

相关推荐

Intel平台下Linux中ELF文件动态链接的加载、解析及实例分析(一): 加载

动态链接,一个经常被人提起的话题。但在这方面很少有文章来阐明这个重要的软件运行机制,只有一些关于动态链接库编程的文章。本系列文章就是要从动态链接库源代码的层次来探讨这个问题。当然从文章的题目就可以看出,intel平台下的linux ELF文件的动态链接。一则是因为这一方面的资料查找比较方便,二则也是这个讨论的意思比其它的动态链接要更为重要(毕竟现在是intel的天下)。当然,有了这么一个例子,

2270

Linux0.11 execve函数(六)

当一个程序使用fork函数创建了一个子进程时,通常会在子进程中调用exec()簇函数之一以加载执行另一个新程序。此时子进程的代码、数据段(包括堆、栈内容)将完全被新程序的替换掉,并在子进程中开始执行新程序。提示:以下是本篇文章正文内容,下面案例可供参考例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

Liuqz2009的专栏 2958

STM32_WS2811驱动

STM32的WS2811驱动

健身房 2839

Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)...

execve系统调用 execve系统调用 我们前面提到了, fork, vfork等复制出来的进程是父进程的一个副本, 那么如何我们想加载新的程序, 可以通过execve来加载和启动新的程序。 x86架构下, 其实还实现了一个新的exec的系统调用叫做execveat(自linux-3.19后进入内核) syscalls,x86: Add execveat() system cal...

weixin_30709809的博客 288

SAP FICO实操:如何修改已发生业务数据的成本中心公司代码(附RKACOR06程序运行指南)

本文详细解析了SAP FICO系统中使用RKACOR06程序修改已发生业务数据的成本中心和公司代码的实操方法。通过实战案例分享,提供从前期准备到后期验证的全流程指南,帮助企业应对组织架构重组或财务核算体系调整时的数据迁移挑战,特别适用于跨公司代码的成本中心划转场景。

weixin_27303545的博客 188

Intel Realsense windows安装与入门

realsense400系列介绍: https://www.intelrealsense.com/stereo-depth/?utm_source=intelcom_website&utm_medium=button&utm_campaign=day-to-day&utm_content=D400_learn-more_button realsense 435i介绍: ...

hanshuning的专栏 1万+

Linux进程管理与调度-之-目录导航

日期 内核版本 架构 作者 GitHub 优快云 2016-05-19 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 进程的描述 优快云 GitHub Linux进程描述符task_struct结构体详解–Linux进程的管理与调度(一) study/kernel/p

OSKernelLAB(gatieme) 3万+

PMSM无感foc控制(滑模-反正切-PLL)【仿真模型搭建教程】(附模型)

本文主要目的是教大家如何把文献中的公式转换成仿真模型。 首先介绍滑模控制的原理及如何搭建simulink模型。 1.1基于反电势估计位置原理 永磁同步电机在静止坐标系αβ下的电压方程: 扩展反电动势包含转子位置信息,并且αβ轴下扩展反电动势的反正切函数正好就等于位置角theta。 1.2 滑模控制原理(比较重要的点是以电机电流模型推导展开) 通过滑模观测得到鲁棒性比较高的αβ轴下扩展反电动势,并求其反正切函数,得到位置信息。 /*********************...

weixin_44769876的博客 1万+

Linux进程调度与管理:(二)进程的加载与启动

文章介绍: 介绍新进程如何获取可执行程序并运行的

abobob的博客 869

Camunda流程引擎 Modeler (二)

Camunda Modeler是Camunda官方提供的建模器: 独立安装的建模器([windows、linux、mac]

1310

西门子工业物联网云平台 Insights Hub 技术白皮书

Insights Hub是西门子公司推出的工业物联网云平台,它是一个使用开放技术的基于云架构的平台,能够为制造业企业提供物联网连接、数据分析、应用开发等一系列服务。Insights Hub的设计理念是“连接、分析和运营”,即通过连接物理设备、收集数据并进行分析,来优化工业生产的运营和管理。

可爱的刘三岁的博客 3332

理解Linux下的进程和程序,分析fork、execve和进程切换

从整体上理解进程创建、可执行文件的加载和进程执行进程切换,重点理解分析fork、execve和进程切换

不思量 1000

Android开发者必看:F2FS文件系统如何优化你的手机存储性能(附实测数据对比)

本文深入解析了F2FS文件系统如何针对闪存特性优化Android设备存储性能。通过对比ext4,F2FS采用追加写和冷热数据分离策略,将随机写转化为顺序写,显著提升写入速度并减少GC卡顿。实测数据显示,其在随机写入和SQLite事务等场景下性能提升显著,为开发者提供了优化应用IO性能的关键思路。

weixin_29248313的博客 324

Linux-进程的管理与调度11(基于6.1内核)

Linux-进程的管理与调度11(基于6.1内核)---Linux进程启动

ListQ的博客 694

人工智能大语言模型微调技术:SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法

人工智能大语言模型微调技术:SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法

丨汀、的博客 4万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【Linux 进程管理】Linux 可执行程序运行机制深度解析

本文深入剖析Linux系统下ELF可执行程序的运行机制,涵盖从文件加载到内存执行的全过程。首先解析ELF文件格式,包括文件头、程序头表和关键节区的作用;然后详细描述内存加载流程,包括系统调用链、页表转换和动态链接机制;接着分析进程执行流程和内存管理机制,如缺页异常和写时复制;最后通过实际案例展示动态链接函数调用的汇编实现。全文系统性地揭示了Linux如何将磁盘上的二进制文件转化为运行中的进程。

love131452098的博客 1240

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

《Linux操作系统分析》之分析Linux内核如何装载和启动一个可执行程序

本篇文章通过将execve系统调用添加到系统menu中,来说明在Linux系统中加载和执行一个可执行程序的流程。

WSYW126的博客 1355

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

可执行程序的加载过程暨exec函数的实现

文章目录fork前言exec族函数ELF可执行文件格式struct linux_bin_prm结构描述一个可执行程序execve加载可执行程序的过程 fork前言 对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执行一次却返回两个值。fork函数是Unix系统最杰出的成就之一,它是七十年代UNIX早期的开发者经过长期在理论和实践上的艰苦探索后取得的成果,一方面,它使操作系统在进程管理上付出了最小的代价,另一方面,又为程序员提供了一个简洁明了的多进程方法。 我们都知道for

二进制君 1357

从整理上理解进程创建、可执行文件的加载和进程执行进程切换,重点理解分析fork、execve和进程切换

学号243,原创作品转载请注明出处 实验内容来源 : https://github.com/mengning/linuxkernel/ 实验内容 阅读理解task_struct数据结构 http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235; 分析fork函数对应的内核处理过程do_fork,理解创建一个...

大叔的博客 282

linux内核-系统调用execve()

​ 读者在linux内核-系统调用fork、vfork与clone中已经看到,进程通常是按其父进程的原样复制出来的,在多数情况下,如果复制出来的子进程不能与父进程分道扬镳,走自己的路,那就没多大意义。所以,执行一个新的可执行程序时进程生命历程中关键性的一步。linux为此提供了一个系统调用execve,而在C语言的程序库中则又在此基础上向应用程序提供一整套的库函数,包括execl、execlp、execle、execlco、execve和execvp。此外,还有库函数system,也与execve有 ...

guoguangwu的专栏 5052

Linux操作系统分析-(2)进程的创建与可执行程序的加载

实验二:进程的创建与可执行程序的加载 学号:SA***424 姓名:**明 实验环境:VMware,ubuntu11.04 一.进程的创建 进程有内核态进程和用户态进程之分。所以进程的创建也就有两种方式:一是由操作系统创建 二是由父进程创建 在系统启动时,操作系统会创建一些进程,他们承担着管理和分配系统资源的任务,这些进程通常被称为系统进程。 系统允许一个进程创建新进程,新进程即为子进

菜鸟的专栏 2181

C Linux系统编程:解析基本的进程创建、控制与进程状态,详解system,execve、waitpid函数

在开始介绍基本的函数之前,首先需要理解什么是"进程"。可以把一个进程想象成一个正在执行的程序。Linux学习篇10——Linux 进程管理深度解析:进程基础、常用指令、服务管理与系统监控-优快云博客程序是静态的,是存储在磁盘上的可执行文件;而进程是动态的,是程序被加载到内存中并执行的一个实例。参数pathname:要执行的可执行文件的完整路径(如/bin/ls参数argv:传递给新程序的参数列表(argv[0]通常是程序名本身),必须以NULL结尾。参数envp:环境变量列表,必须以NULL结尾。

buyutang_的博客 1390

linux参考文献_Linux进程创建、可执行文件的加载和进程执行进程切换

383 + 原创作品转载请注明出处 + 中科大孟宁老师的linux操作系统分析:https://github.com/mengning/linuxkernel/实验:从整理上理解进程创建、可执行文件的加载和进程执行进程切换,重点理解分析fork、execve和进程切换实验内容:1、阅读理解task_struct数据结构2、分析fork函数对应的内核处理过程do_fork,理解创建一个新进程如何创建...

weixin_39564853的博客 360
上一篇: Linux内存描述之概述--Linux内存管理(一)
下一篇: Linux 内核第一宏
废言Pro
博客等级 码龄16年 1092粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值