• 操作系统第二次实验报告——Linux创建进程及可执行文件结构分析


    0 个人信息

    • 张樱姿
    • 201821121038
    • 计算1812

    1 实验目的

    • 熟练Linux创建进程fork操作。

    2 实验内容

    • 在服务器上用VIM编写一个程序:一个进程创建两个子进程。
    • 查看进程树
    • 查看进程相关信息

    3 实验报告

     3.1编写程序创建两个子进程

     1 #include<sys/types.h>
     2 #include<stdio.h>
     3 #include<unistd.h>
     4 
     5 int main(){
     6         pid_t cpid1 = fork();       //创建子进程1
     7 
     8         if(cpid1<0){
     9                 printf("fork cd1 failed
    ");
    10         }
    11         else if(cpid1==0){
    12                 printf("Child1:pid: %d, ppid: %d
    ",getpid(),getppid());
    13         }
    14         else{
    15                 pid_t cpid2 = fork();  //创建子进程2
    16                 if(cpid2<0){
    17                         printf("fork cd2 failed
    ");
    18                 }
    19                 else if(cpid2==0){
    20                         printf("Child2:pid: %d, ppid: %d
    ",getpid(),getppid());
    21                 }
    22                 else{
    23                         printf("Parent: pid :%d
    ",getpid());
    24                 }
    25         }
    26 }

       编译运行后的结果:

         3.2打印进程树

     添加sleep函数以挂起进程,方便打印进程树:

     1 #include<sys/types.h>
     2 #include<stdio.h>
     3 #include<unistd.h>
     4 
     5 int main(){
     6         pid_t cpid1 = fork();
     7 
     8         if(cpid1<0){
     9                 printf("fork cd1 failed
    ");
    10         }
    11         else if(cpid1==0){
    12                 printf("Child1:pid: %d, ppid: %d
    ",getpid(),getppid());
    13                 sleep(30);        //挂起30秒
    14         }
    15         else{
    16                 pid_t cpid2 = fork();
    17                 if(cpid2<0){
    18                         printf("fork cd2 failed
    ");
    19                 }
    20                 else if(cpid2==0){
    21                         printf("Child2:pid: %d, ppid: %d
    ",getpid(),getppid());
    22                         sleep(30);   //挂起30秒
    23                 }
    24                 else{
    25                         printf("Parent: pid :%d
    ",getpid());
    26                         sleep(60);   //挂起60秒
    27                 }
    28         }
    29 }
    pstree -p pid  #打印进程树

      3.3 解读进程相关信息

        3.3.1 解释执行ps -ef后返回结果中每个字段的含义

         ps -ef输出格式 :

    UID    PID    PPID    C    STIME    TTY    TIME  CMD
    • UID: User ID,用户ID。
    • PID: Process ID ,进程ID。
    • PPID: Parent Process Pid,父进程ID。
    • C: CPU使用的资源百分比。
    • STIME: Start Time,进程启动时间。
    • TTY: Controlling Tty,进程的控制终端。
    • TIME: 进程占用CPU的时间总和。如果运行时间达到 100 分钟,以 mm:ss 或 mmmm:ss 格式显示时间。
    • CMD: Command name/line,所下达的指令名。

        3.3.2 解释执行ps -aux后返回结果中每个字段的含义

          ps -au(x) 输出格式 :

    USER    PID    %CPU    %MEM    VSZ    RSS    TTY    STAT    START    TIME    COMMAND
    • USER: User Name,行程拥有者。
    • PID: Process ID ,进程ID。
    • %CPU: CPU usage,该进程占用的 CPU 资源百分比。
    • %MEM: Memory usage (RES),该进程所占用的物理内存百分比。
    • VSZ: Virtual Memory Size,该进程占用的虚拟内存大小,表明了该进程可以访问的所有内存,包括被交换的内存和共享库内存。
    • RSS: Resident Set Size,常驻内存集合大小,表示相应进程在RAM中占用了多少内存,并不包含在SWAP中占用的虚拟内存。
    • TTY: Controlling Tty,进程的控制终端。
    • STAT: Status,该进程程的状态:
    进程状态 含义
    D 不可中断 Uninterruptible sleep (usually IO)
    R 正在运行,或在队列中的进程
    S 处于休眠状态
    T 停止或被追踪
    Z 僵尸进程
    W 进入内存交换(从内核2.6开始无效)
    X 死掉的进程
    < 高优先级
    N 低优先级
    L 有些页被锁进内存
    s 包含子进程
    + 位于后台的进程组
    l 多线程,克隆线程
    • START: 行程开始时间
    • TIME: 执行的时间
    • COMMAND:所执行的指令

      3.4 分析Linux可执行文件构成

      首先写一个hello.c,

      3.4.1 使用file查看文件类型

      ①使用以下命令生成可重定位目标文件。即文件中的代码段和数据的地址         还没有最终确定。

    gcc -c hello.c -o hello.o

      ②使用以下命令生成一个可执行共享对象文件

    gcc -o hello.out hello.c

       Linux下可执行文件的格式主要是ELF格式,即可链接格式(Executable and Linkable Format)。

       ELF文件由四个部分组成:ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。包括三个索引表:

    • ELF头:ELF header,在文件开始处描述了整个文件的组织情况。ELF的文件头包含整个执行文件的控制结构。
    • 程序头表:program header table,用来告知系统如何创建进程映像。
    • 节头表:section header table,包含描述文件节区的信息,每个节区在表中都有一项,给出节区名称、大小等信息。

      上面生成的hello.o和hello.out有什么区别

      ①.o文件通常没有程序头表(program header table),而是由一个或多个.o文件链接编译后生成程序头表。

      ②.o文件用section来概括它的各个部分,而.out文件,编译器会将它的各个section进一步整合成各大的部分,称为segment。

      ③因此,对于目标代码文件(.o文件)来说,program header table是可选的,而对于可执行文件(.out文件)来说,section header table是可选的。

      所以目标文件并不是可执行的,其链接后才生成可执行文件。这里讨论的是可执行文件的内部结构,即hello.out。可见,绿色也代表了它的可执行性。

      3.4.2 vim查看该可执行文件(hello.out):

        发现这是个二进制文件,因此(在vim中)先把它转换成十六进制文件以便查看。之后要记得转换回来。

    :%!xxd     #将2进制格式文件转换为16进制格式
    :%!xxd -r  #转换回2进制格式

         转换为16进制后(小端地址存储,采取两个两个从右往左读的方法):

       3.4.3 readelf/objdump解析该可执行文件(hello.out):

      readelf命令本身也是一个elf类型的可执行文件,用来解析二进制的elf文件;另外objdump命令可以用来解析exe或elf文件,也更具一般性。

      readelf命令的使用方法:

    Usage: readelf <option(s)> elf-file(s)
     Display information about the contents of ELF format files
     Options are:
      -a --all               Equivalent to: -h -l -S -s -r -d -V -A -I
      -h --file-header       Display the ELF file header
      -l --program-headers   Display the program headers
         --segments          An alias for --program-headers
      -S --section-headers   Display the sections' header
         --sections          An alias for --section-headers
      -g --section-groups    Display the section groups
      -t --section-details   Display the section details
      -e --headers           Equivalent to: -h -l -S
      -s --syms              Display the symbol table
         --symbols           An alias for --syms
      --dyn-syms             Display the dynamic symbol table
      -n --notes             Display the core notes (if present)
      -r --relocs            Display the relocations (if present)
      -u --unwind            Display the unwind info (if present)
      -d --dynamic           Display the dynamic section (if present)
      -V --version-info      Display the version sections (if present)
      -A --arch-specific     Display architecture specific information (if any)
      -c --archive-index     Display the symbol/file index in an archive
      -D --use-dynamic       Use the dynamic section info when displaying symbols
      -x --hex-dump=<number|name>
                             Dump the contents of section <number|name> as bytes
      -p --string-dump=<number|name>
                             Dump the contents of section <number|name> as strings
      -R --relocated-dump=<number|name>
                             Dump the contents of section <number|name> as relocated bytes
      -z --decompress        Decompress section before dumping it
      -w[lLiaprmfFsoRtUuTgAckK] or
      --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
                   =frames-interp,=str,=loc,=Ranges,=pubtypes,
                   =gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
                   =addr,=cu_index,=links,=follow-links]
                             Display the contents of DWARF debug sections
      --dwarf-depth=N        Do not display DIEs at depth N or greater
      --dwarf-start=N        Display DIEs starting with N, at the same depth
                             or deeper
      -I --histogram         Display histogram of bucket list lengths
      -W --wide              Allow output width to exceed 80 characters
      @<file>                Read options from <file>
      -H --help              Display this information
      -v --version           Display the version number of readelf

      ①使用以下命令查看hello.out的ELF Header:

    readelf -h hello.out

        该可执行文件hello.out文件的程序头大小为56字节。

      64位系统的ELF头文件(位于/usr/include/elf.h中)定义:

    typedef struct
    {
      unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
      Elf64_Half    e_type;                 /* Object file type */
      Elf64_Half    e_machine;              /* Architecture */
      Elf64_Word    e_version;              /* Object file version */
      Elf64_Addr    e_entry;                /* Entry point virtual address */
      Elf64_Off     e_phoff;                /* Program header table file offset */
      Elf64_Off     e_shoff;                /* Section header table file offset */
      Elf64_Word    e_flags;                /* Processor-specific flags */
      Elf64_Half    e_ehsize;               /* ELF header size in bytes */
      Elf64_Half    e_phentsize;            /* Program header table entry size */
      Elf64_Half    e_phnum;                /* Program header table entry count */
      Elf64_Half    e_shentsize;            /* Section header table entry size */
      Elf64_Half    e_shnum;                /* Section header table entry count */
      Elf64_Half    e_shstrndx;             /* Section header string table index */
    } Elf64_Ehdr;

       第一行:

    • e_ident用16个字节表示为“7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00”,其中“7f45 4c46”表示“ELF”的ASCII码表。“0102”中前一个“02”表示是64位的机器,后一个“01”表示使用的是小端法。“0100”中的“01”表示的是版本号是01。剩下的0为默认填充。

      第二行:

    • e_type用2个字节表示,为“0003”,表示是一个动态共享目标文件。
    • e_machine用2个字节表示,为“003e”,表示Inter 80386的处理器体系结构(64位)。
    • e_version用4个字节表示,为“0000 0001”,表示的是当前版本。
    • e_entry用4个字节表示,为“0000 0530 ”表示没有入口地址为0x530。  

      第三行:

    • e_phoff用8个字节表示,为“0000 0000 0000 0040”表示程序头表(Program header table)在文件中的偏移量为64字节。
    • e_shoff用8个字节表示,为“0000 0000 0000 1930”表示段表(Section header table)在文件中的偏移量为6448字节。

      第四行:

    • e_flags用4个字节表示,为“0000 0000”表示未知处理器特定标志。
    • e_ehsize用2个字节表示,为“0040”表示elf文件头大小为64字节。
    • e_phentsize用2个字节表示,为“0038”表示程序头表中每一个条目的大小为56字节。
    • e_phnum用2个字节表示,为“0009”表示Program header table中有9个条目。
    • e_shentsize用2个字节表示,为“0040”,表示段头大小为64个字节(由此知道section header table里面每一个table的大小为64个字节)。
    • e_shnum用2个字节表示,为“001d”,表示段表入口有29个,即段有29个。
    • e_shstrndx用2个字节表示,为“001c”,表示段名串表在段表中的索引,(符号表的信息在段表的索引号是28)。

       ②使用以下命令查看hello.out的段表信息:

    readelf -S hello.out

      段表信息:其中.text section是可执行指令的集合,位偏移0x0000 0530,size=0x0000 01a2(即418字节),.data section是初始化后数据的集合,位偏移  0x0000 1000,size=0x0000 0010(即16字节),.symtab section存放所有section中定义的符号名字,.strtab section位偏移0x0000 1628,size=0x0000 0203(即515字节),.shstrtab section与.symtab section之间存储的是段表。

    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .interp           PROGBITS         0000000000000238  00000238
           000000000000001c  0000000000000000   A       0     0     1
      [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
           0000000000000020  0000000000000000   A       0     0     4
      [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
           0000000000000024  0000000000000000   A       0     0     4
      [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
           000000000000001c  0000000000000000   A       5     0     8
      [ 5] .dynsym           DYNSYM           00000000000002b8  000002b8
           00000000000000a8  0000000000000018   A       6     1     8
      [ 6] .dynstr           STRTAB           0000000000000360  00000360
           0000000000000082  0000000000000000   A       0     0     1
      [ 7] .gnu.version      VERSYM           00000000000003e2  000003e2
           000000000000000e  0000000000000002   A       5     0     2
      [ 8] .gnu.version_r    VERNEED          00000000000003f0  000003f0
           0000000000000020  0000000000000000   A       6     1     8
      [ 9] .rela.dyn         RELA             0000000000000410  00000410
           00000000000000c0  0000000000000018   A       5     0     8
      [10] .rela.plt         RELA             00000000000004d0  000004d0
           0000000000000018  0000000000000018  AI       5    22     8
      [11] .init             PROGBITS         00000000000004e8  000004e8
           0000000000000017  0000000000000000  AX       0     0     4
      [12] .plt              PROGBITS         0000000000000500  00000500
           0000000000000020  0000000000000010  AX       0     0     16
      [13] .plt.got          PROGBITS         0000000000000520  00000520
           0000000000000008  0000000000000008  AX       0     0     8
      [14] .text             PROGBITS         0000000000000530  00000530
           00000000000001a2  0000000000000000  AX       0     0     16
      [15] .fini             PROGBITS         00000000000006d4  000006d4
           0000000000000009  0000000000000000  AX       0     0     4
      [16] .rodata           PROGBITS         00000000000006e0  000006e0
           0000000000000010  0000000000000000   A       0     0     4
      [17] .eh_frame_hdr     PROGBITS         00000000000006f0  000006f0
           000000000000003c  0000000000000000   A       0     0     4
      [18] .eh_frame         PROGBITS         0000000000000730  00000730
           0000000000000108  0000000000000000   A       0     0     8
      [19] .init_array       INIT_ARRAY       0000000000200db8  00000db8
           0000000000000008  0000000000000008  WA       0     0     8
      [20] .fini_array       FINI_ARRAY       0000000000200dc0  00000dc0
           0000000000000008  0000000000000008  WA       0     0     8
      [21] .dynamic          DYNAMIC          0000000000200dc8  00000dc8
           00000000000001f0  0000000000000010  WA       6     0     8
      [22] .got              PROGBITS         0000000000200fb8  00000fb8
           0000000000000048  0000000000000008  WA       0     0     8
      [23] .data             PROGBITS         0000000000201000  00001000
           0000000000000010  0000000000000000  WA       0     0     8
      [24] .bss              NOBITS           0000000000201010  00001010
           0000000000000008  0000000000000000  WA       0     0     1
      [25] .comment          PROGBITS         0000000000000000  00001010
           000000000000002b  0000000000000001  MS       0     0     1
      [26] .symtab           SYMTAB           0000000000000000  00001040
           00000000000005e8  0000000000000018          27    43     8
      [27] .strtab           STRTAB           0000000000000000  00001628
           0000000000000203  0000000000000000           0     0     1
      [28] .shstrtab         STRTAB           0000000000000000  0000182b
           00000000000000fe  0000000000000000           0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
      L (link order), O (extra OS processing required), G (group), T (TLS),
      C (compressed), x (unknown), o (OS specific), E (exclude),
      l (large), p (processor specific)

      根据刚才在ELF头文件中读到的段表偏移为1930H字节,即6448字节。且段表存储在0x0000 0000 0000 1930~~~0x0000 0000 0000 2070,共40H*29=740H字节。以第二个段表(0x0000 0000 0000 1970~~~0x0000 0000 0000 19B0)作为例子分析,每个段表占40H字节(64字节)。

    64位系统的段表(位于/usr/include/elf.h中)定义:

    typedef struct
    {
      Elf64_Word    sh_name;                /* Section name (string tbl index) */
      Elf64_Word    sh_type;                /* Section type */
      Elf64_Xword   sh_flags;               /* Section flags */
      Elf64_Addr    sh_addr;                /* Section virtual addr at execution */
      Elf64_Off     sh_offset;              /* Section file offset */
      Elf64_Xword   sh_size;                /* Section size in bytes */
      Elf64_Word    sh_link;                /* Link to another section */
      Elf64_Word    sh_info;                /* Additional section information */
      Elf64_Xword   sh_addralign;           /* Section alignment */
      Elf64_Xword   sh_entsize;             /* Entry size if section holds table */
    } Elf64_Shdr;
    • sh_name用4个字节表示,为“0000 001b”,该值代表section header string table中的索引。
    • sh_type 用4个字节表示,为“0000 0001”,表示该段的类型是“SHT_PROGBITS”,程序节。
    • sh_flags 用8个字节表示,为“0000 0000 0000 0002”,指示该section在进程执行时的特性,这里表示表示该section在进程空间中必须要分配空间。
    • sh_addr 用8个字节表示,为“0000 0000 0000 0238”,表示该节在进程中的起始地址为“0x238”。
    • sh_offset用8个字节表示,为“0000 0000 0000 0238”表示该节在整个文件中的起始偏移量为“0x238”。
    • sh_size用8个字节表示,为“0000 0000 0000 001c”,表示该节的字节大小为0x1c。
    • sh_link用4个字节表示,为“0000 0000”,表示没有和该节相关联的节。
    • sh_info用4个字节表示,为“0000 0000”表示没有文件信息。
    • sh_addralign用8个字节表示,为“0000 0000 0000 0001”,该值用于表示地址对齐信息,值为1时表示不用地址对齐。
    • sh_entsize用8个字节表示,为“0000 0000 0000 0000”,对特定节(动态符号)才有意义,这里没有意义。

    使用以下命令查看hello.out的.text节(编号为14),即代码部分:

    readelf -x 14 hello.out

       同理,可打印.data节(编号为23)的内容:

    readelf -x 23 hello.out

       ③使用以下命令查看hello.out的重定位信息:

    readelf -r hello.out

    4 References

  • 相关阅读:
    PHP中的call_user_func()与call_user_func_array()简单理解
    PHP实现多继承
    PHP实现多继承 trait 语法
    PHP几种常见魔术方法与魔术变量解析
    tp5 的nginx配置
    PHP 扩展 trie-tree, swoole过滤敏感词方案
    PHP Ajax跨域问题解决办法
    附加个人作业
    学完软工的感受
    团队介绍
  • 原文地址:https://www.cnblogs.com/MilkoSilver/p/12612055.html
Copyright © 2020-2023  润新知