程序员的自我修养-进程

1. 进程

"""
1. 只能使用那些操作系统分配给进程的地址, 如果访问未经允许的空间, 那么操作系统就会捕获到这些访问, 
将进程的这种访问当作非法操作, 强制结束进程.

2. 从硬件层面上来讲, 原先的32位地址线只能访问最多4GB的物理内存. 但是自从扩展至36位地址线之后, 
Intel 修改了页映射的方式, 使得新的映射方式可以访问到更多的物理内存. Intel把这个地址扩展方式叫做PAE.

3. 创建一个进程, 然后装载相应的可执行文件并且执行.
  - 创建一个独立的虚拟地址空间.
  - 读取可执行文件头, 并且建立虚拟空间与可执行文件的映射关系.
  - 将CPU的指令寄存器设置成可执行文件的入口地址, 启动运行.
"""

2. VMA区域

"""
操作系统通过给进程空间划分出一个个VMA来管理进程的虚拟空间; 基本原则是将相同权限属性的, 有相同映像文件的映射成一个VMA;
一个进程基本上可以分为如下几种VMA区域:
  - 代码VMA, 权限只读, 可执行, 有映像文件
  - 数据VMA, 权限可读写, 可执行, 有映像文件
  - 堆VMA, 权限可读写, 可执行, 无映像文件, 匿名, 可向上扩展
  - 栈VMA, 权限可读写, 不可执行, 无映像文件, 匿名, 可向下扩展
  - 查看内存映射:  ./section.elf &    cat /proc/59320/maps
"""

3. 装载过程

"""
在Linux系统的bash下输入一个命令执行某个ELF程序时, Linux装载这个ELF文件并且执行的过程.
- 首先在用户层面, bash进程会调用fork()系统调用创建一个新的进程, 然后新的进程调用execve()系统调用执行指定的ELF文件, 
原先的bash进程继续返回等待刚才启动的新进程结束, 然后继续等待用户输入命令.

- 原型:  int execve(const char* filename, char* const argv[], char* const envp[]);

- 三个参数分别是被执行的程序文件名, 执行参数和环境变量. Glibc 对execvp()系列调用进行了包装, 提供了execl(), execlp() 
execle(), execv()和execvp()等5个不同形式的exec系列API, 它们只是在调用的参数形式上有所区别, 
但最终都会调用到execve()这个系统调用函数.

- 在进入 execve() 系统调用之后, Linux内核就开始进行真正的装载工作. 在内核中, execve()系统调用相应的入口是 
sys_execve(). sys_execve()进行一些参数的检查复制之后, 调用do_execve(). do_execve()读取文件的前128个字节
的文件头部之后, 然后调用 search_binary_handle().search_binary_handle()会通过判断文件头部的魔数确定文件的格式, 
并且调用相应的装载处理过程. ELF可执行文件的装载处理过程叫做 load_elf_binary().
"""

4. load_elf_binary()被定义在 fs/Binfmt_elf.c

"""
- 检查elf可执行文件格式的有效性, 比如魔数, 程序头表中段的数量.

- 寻找动态链接的"interp"段, 设置动态链接器路径.

- 根据ELF可执行文件的程序头表的描述, 对ELF文件进行映射, 比如代码, 数据, 只读数据.

- 初始化ELF进程环境, 比如进程启动时EDX寄存器的地址应该是DT_FINI的地址.

- 将系统调用的返回地址修改成ELF可执行文件的入口点, 这个入口点取决于程序的链接方式, 对于静态链接的ELF可执行文件, 
这个程序入口就是ELF文件的文件头中e_entry所指的地址; 对于动态链接的ELF可执行文件, 程序入口点是动态链接器.
"""

5. 装载一个PE可执行文件

"""
- 先读取文件的第一个页, 在这个页中, 包含了DOS头, PE文件头和段表.
- 检查进程地址空间中, 目标地址是否可用, 如果不可用, 则另外选一个装载地址.
- 使用段表中提供的信息, 将PE文件中所有的段一一映射到地址空间中相应的位置.
- 如果装载地址不是目标地址, 则进行rebasing.
- 装载所有PE文件所需要的DLL文件.
- 对PE文件中的所有导入符号进行解析.
- 根据PE头中指定的参数, 建立初始化栈和堆.
- 建立主线程并且启动进程.
"""

6. 段

// 单独设立 .rodata 段, 不光是在语义上支持了C++的const关键字, 而且操作系统在加载的时候可以将 
// .rodata 段的属性映射成只读, 这样对于这个段的任何修改操作都会作为非法操作确定, 保证了程序的安全性.

// 正常情况下, GCC编译出来的目标文件中, 代码会被放到 .text 段, 全局变量和静态变量会被放到 .data 和 .bss 段. 
// GCC 提供了一个扩展机制, 使得程序员可以指定变量所处的段.

__attribute__((section("FOO"))) int global = 42
__attribute__((section("BAR"))) void foo() {}

// objdump -x -s -d hello.o
// readelf -S hello.o 

7. ELF 文件结构描述

// ELF的文件头中定义了ELF魔数, 文件机器字节长度, 数据存储方式, 版本, 运行平台, ABI版本, ELF重定位类型, 
// 硬件平台, 硬件平台版本, 入口地址, 程序头入口和长度, 段表的位置和长度以及段的数量等.

// - ELF32 文件头结构体
typedef struct {
    unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
    Elf32_Half    e_type;                 /* Object file type */
    Elf32_Half    e_machine;              /* Architecture */
    Elf32_Word    e_version;              /* Object file version */
    Elf32_Addr    e_entry;                /* Entry point virtual address */
    Elf32_Off     e_phoff;                /* Program header table file offset */
    Elf32_Off     e_shoff;                /* Section header table file offset */
    Elf32_Word    e_flags;                /* Processor-specific flags */
    Elf32_Half    e_ehsize;               /* ELF header size in bytes */
    Elf32_Half    e_phentsize;            /* Program header table entry size */
    Elf32_Half    e_phnum;                /* Program header table entry count */
    Elf32_Half    e_shentsize;            /* Section header table entry size */
    Elf32_Half    e_shnum;                /* Section header table entry count */
    Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

// ELF 魔数    Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
// - 最开始的四个字节是所有ELF文件都必须相同的标识码, 分别为0x7F, 0x4F, 0x4c, 0x46. 
// 第一个字节对应ASCII字符里面的DEL控制符, 后面3个字节恰好是ELF这3个字母的ASCII码. 这四个字节被称为ELF文件的魔数.
// - 第五个字节用来标识ELF的文件类型, 0x01表示32位的, 0x02表示是64位的.
// - 第六个字节是字节序, 规定该ELF文件是大端的还是小端的(1 -> 小端格式; 2 -> 大端格式).
// - 文件类型:  EX_REL -> 可重定位文件   EX_EXEC -> 可执行文件   EX_DYN -> 共享目标文件

8. 段表

typedef struct {
    Elf32_Word    sh_name;                /* Section name (string tbl index) */
    Elf32_Word    sh_type;                /* Section type */
    Elf32_Word    sh_flags;               /* Section flags */
    Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
    Elf32_Off     sh_offset;              /* Section file offset */
    Elf32_Word    sh_size;                /* Section size in bytes */
    Elf32_Word    sh_link;                /* Link to another section */
    Elf32_Word    sh_info;                /* Additional section information */
    Elf32_Word    sh_addralign;           /* Section alignment */
    Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值