【UNIX】从一个可执行文件的生成到进程在内存中分布 (中)/文件到进程的转变

从一个可执行文件的生成到进程在内存中分布 (中)

                                                                            文件到进程的转变


一个可执行文件是通过什么方式从ELF格式文件加装从而成为进程来执行的?

 

      首先,而一个伪终端始终处于等待执行的状态,它是一个进程,当我们在命令行输入LS时,它会解析命令并执行相应的命令,其实这些命令都是可执行文件,它们被存储在不同的文件目录中,一般这些文件都具有全局变量的属性。

       在shell脚本中的变量只具有当前shell使用,那么怎么把自己编写的sh脚本用在别的shell命令中并能够解析到?这里就要引入export命令,我们可以通过cat /etc/exports查看当前sh所能运行的环境变量,那么我们也可以通过export命令将自己设定的变量导出到环境中,那样就可以是变量具有全局可用性,在不同的环境中都可以使用,添加环境变量的一般方法是export PATH=$PATH:tools/bin/ 这条命令是在已有的环境变量追加的方法添加,不覆盖原先的环境变量,之后我们可以通过 echo &PATH 查看自己设置的环境变量是否成功添加到环境中。

          在这里为什么我会先提出环境变量的概念,其实仔细想想,你会发现,那些LS CP MV 等经常使用的命令,其实就是可执行文件,对于一个静态的指令的有序集合(可执行文件)没有任何执行的概念,他们只是被视为常用程序预先编译好存放在Linux文件系统中某个目录中,为了要使这些命令在每个文件目录下都可使用,就会预先设置它们的环境变量,以便我们可以在任意目录使用。那么我们通过相同方法来实现可执行文件转变成进程的过程。

        Linux系统中,一个可以通过fork()函数来创建进程,但是创建的子进程完全复制父进程的资源,当我们在子进程中可以使用exec系统调用来创建一个自己进程,它可以根据指定的文件名或者路径名找到可执行文件,。并用来取代原调用进程的数据段,代码段和堆栈段,在执行完后,原调用的进程内容除了进程号外,其他全部被替换了,最终执行可执行文件变为进程的概念。而我们输入的常用指令就是可执行文件,此时会有点疑问,为什么在输入命令的时候没有输入路径,是因为在之前就设置为全局的环境变量了,另一方面对于exec系统调用有几种形式,可以通过传入不同的参数来实现可执行文件到进程的转变。

下面是exec()系统调用的几种形式:


int execl(const charp a t* h n a m e, const char a* rg 0, ... /* (char *) 0 */);

int execv(const charp a t* h n a m e, char *consta rgv [] );

int execle(const charp a t* h n a m e, const char a* rg 0, ...

/* (char *)0, char *cones nt v p [] */);

int execve(const charp a t* h n a m e, char *consta rgv [], char *consten vp [] );

int execlp(const charf i l e* n a m e, const char a* rg 0, ... /* (char *) 0 */);

int execvp(const charf i l e* n a m e, char *consta rgv [] );

深入内核代码来研究一下可执行文件的加载过程.execve()系统调用的入口是sys_execve().代码如下:

asmlinkage int sys_execve(struct pt_regs regs)

{

    int error;

    char * filename;

 

    //将用户空间的第一个参数(也就是可执行文件的路径)复制到内核

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

    error = do_execve(filename,

            (char __user * __user *) regs.ecx,

            (char __user * __user *) regs.edx,

            ®s);

    return error;

}

系统调用的时候,把参数依次放在:ebx,ecx,edx,esi,edi,ebp寄存器.第一个参数为可执行文件路径,第二个参数为参数的个数,第三个参数为可执行文件对应的参数.do_execve()是这个系统调用的核心。

 

过程分析

 

进程装载:

    覆盖装入(Overlay)和页映射(Paging)是两种典型的动态装载方法。现在前者已经不用了。   

    创建一个进程,然后装载相应的可执行文件并且执行。上述过程最开始只需要做三件事情:

①创建一个独立的虚拟地址空间。主要是分配一个页目录(Page Directory)。

②读取可执行文件的头,并且建立虚拟空间和可执行文件的映射关系。主要是把可执行文件映射到虚拟地址空间,即做虚拟页和物理页的映射,以便“缺页”时载入。

③将CPU的指令寄存器设置成可执行文件的入口地址,启动运行。从ELF文件中的入口地址开始执行程序。


   bash调用fork()系统调用创建一个新的进程,然后新的进程调用execve()系统调用执行指定的ELF文件。 bash进程继续返回等待新进程执行结束,然后重新等待用户输入命令。execve()系统调用被定义在unistd.h,它的原型如下:
    int execve(const char *filenarne, char *const argv[], char *const envp[]);
    它的三个参数分别是被执行的程序文件名、执行参数和环境变最。Glibc对execvp()系统调用进行了包装,提供了execl(), execlp(), execle(), execv()和execvp()等5个不同形式的exec系列API,它们只是在调用的参数形式上有所区别,但最终都会调用到execve()这个系统中。

    调用execve()系统调用之后,再调用内核的入口sys_execve()。 sys_execve()进行一些参数的检查复制之后,调用do_execve()。 因为可执行文件不止ELF一种,还有java程序和以“#!”开始的脚本程序等, 所以do_execve()会首先检查被执行文件,读取前128个字节,特别是开头4个字节的魔数,用以判断可执行文件的格式。 如果是解释型语言的脚本,前两个字节“#!"就构成了魔数,系统一旦判断到这两个字节,就对后面的字符串进行解析,以确定程序解释器的路径

当do_execve()读取了这128个字节的文件头部之后,然后调用search_binary_handle()去搜索和匹配合适的可执行文件装载处理过程。Linux中所有被支持的可执行文件格式都有相应的装载处理过程,search_binary_handle()会通过判断文件头部的魔数确定文件的格式,并且调用相应的装载处理过程。如ELF用load_elf_binary(),a.out用load_aout_binary(),脚本用load_script()。其中ELF装载过程的主要步骤是:
    ①检查ELF可执行文件格式的有效性,比如魔数、程序头表中段(Segment)的数量。
    ②寻找动态链接的”.interp”段(该段保存可执行文件所需要的动态链接器的路径),设置动态链接器路径。
    ③根据ELF可执行文件的程序头表的描述,对ELF文件进行映射,比如代码、数据、只读数据。
    ④初始化ELF进程环境,比如进程启动时EDX寄存器的地址应该是DT_FINI的地址(结束代码地址)。
    ⑤将系统调用的返回地址修改成ELF可执行文件的入口点,这个入口点取决于程序的链接方式,对于静态链接的ELF可执行文件,这个程序入口就是ELF文件的文件头中e_enEry所指的地址;对于动态链接的ELF可执行文件,程序入口点是动态链接器。


    当ELF被load_elf_binary()装载完成后,函数返回至do_execve()在返回至sys_execve()。在load_elf_binary()中(第5步)系统调用的返回地址已经被改成ELF程序的入口地址了。 所以当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到了ELF程序的入口地址,于是新的程序开始执行,ELF可执行文件装载完成。


 

 

 

 

 


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值