计算机系统大作业——程序人生

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业   计算机科学与技术                    

学     号    2021112500                   

班     级   2103102                    

学       生   李星尧                  

指 导 教 师  刘宏伟                     

计算机科学与技术学院

2022年5月

摘  要

一个简单的hello.c是所有程序员的出发点。但是就是这样一个简单的程序想让它运行也会涉及到预处理,编译,汇编,链接以及运行时的进程管理,与机器进行交互的等等过程。本作业将会按照hello程序在计算机的生命周期过程逐一讨论程序生成和运行进行的各个步骤的详细原理,同时比较全方面的涉及了Hello程序在其生命周期中可能出现的特殊情况以及处理方法等。

关键词:预处理;编译;汇编;链接;进程管理;进程 ;                          

目  录

第1章 概述................................................................................................................ - 4 -

1.1 Hello简介......................................................................................................... - 4 -

1.2 环境与工具........................................................................................................ - 4 -

1.3 中间结果............................................................................................................ - 4 -

1.4 本章小结............................................................................................................ - 4 -

第2章 预处理............................................................................................................ - 5 -

2.1 预处理的概念与作用........................................................................................ - 5 -

2.2在Ubuntu下预处理的命令............................................................................. - 5 -

2.3 Hello的预处理结果解析................................................................................. - 5 -

2.4 本章小结............................................................................................................ - 5 -

第3章 编译................................................................................................................ - 6 -

3.1 编译的概念与作用............................................................................................ - 6 -

3.2 在Ubuntu下编译的命令................................................................................ - 6 -

3.3 Hello的编译结果解析..................................................................................... - 6 -

3.4 本章小结............................................................................................................ - 6 -

第4章 汇编................................................................................................................ - 7 -

4.1 汇编的概念与作用............................................................................................ - 7 -

4.2 在Ubuntu下汇编的命令................................................................................ - 7 -

4.3 可重定位目标elf格式.................................................................................... - 7 -

4.4 Hello.o的结果解析......................................................................................... - 7 -

4.5 本章小结............................................................................................................ - 7 -

第5章 链接................................................................................................................ - 8 -

5.1 链接的概念与作用............................................................................................ - 8 -

5.2 在Ubuntu下链接的命令................................................................................ - 8 -

5.3 可执行目标文件hello的格式....................................................................... - 8 -

5.4 hello的虚拟地址空间..................................................................................... - 8 -

5.5 链接的重定位过程分析.................................................................................... - 8 -

5.6 hello的执行流程............................................................................................. - 8 -

5.7 Hello的动态链接分析..................................................................................... - 8 -

5.8 本章小结............................................................................................................ - 9 -

第6章 hello进程管理...................................................................................... - 10 -

6.1 进程的概念与作用.......................................................................................... - 10 -

6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -

6.3 Hello的fork进程创建过程........................................................................ - 10 -

6.4 Hello的execve过程.................................................................................... - 10 -

6.5 Hello的进程执行........................................................................................... - 10 -

6.6 hello的异常与信号处理............................................................................... - 10 -

6.7本章小结.......................................................................................................... - 10 -

第7章 hello的存储管理................................................................................... - 11 -

7.1 hello的存储器地址空间............................................................................... - 11 -

7.2 Intel逻辑地址到线性地址的变换-段式管理............................................... - 11 -

7.3 Hello的线性地址到物理地址的变换-页式管理.......................................... - 11 -

7.4 TLB与四级页表支持下的VA到PA的变换................................................ - 11 -

7.5 三级Cache支持下的物理内存访问............................................................. - 11 -

7.6 hello进程fork时的内存映射..................................................................... - 11 -

7.7 hello进程execve时的内存映射................................................................. - 11 -

7.8 缺页故障与缺页中断处理.............................................................................. - 11 -

7.9动态存储分配管理.......................................................................................... - 11 -

7.10本章小结........................................................................................................ - 12 -

第8章 hello的IO管理.................................................................................... - 13 -

8.1 Linux的IO设备管理方法............................................................................. - 13 -

8.2 简述Unix IO接口及其函数.......................................................................... - 13 -

8.3 printf的实现分析........................................................................................... - 13 -

8.4 getchar的实现分析....................................................................................... - 13 -

8.5本章小结.......................................................................................................... - 13 -

结论............................................................................................................................ - 14 -

附件............................................................................................................................ - 15 -

参考文献.................................................................................................................... - 16 -

第1章 概述

1.1 Hello简介

P2P:即from program to process:在Linux中,通过gcc编译器驱动程序读取程序文件hello.c,然后经过预处理器(cpp)的预处理,修改该程序获得hello.i文件;通过编译器(cll)的编译,将文本文件hello.i翻译成文本文件形式的汇编程序hello.s;再通过汇编器(cs)将hello.s翻译成机器语言指令,将指令打包成可重定位的目标文件hello.o;然后通过链接器(ld)的链接合并获得可执行目标文件hello。Linux系统中,在shell中输入./hello启动命令后,shell为其fork,产生子进程,于是hello从 Program 变成 Process。至此,hello.c完成了P2P的过程

020:即from zero to zero:Shell通过execve在fork产生的子进程中加载hello,先删除当前虚拟地址的用户部分已存在的数据结构,为hello的代码、数据、bss和栈区域创建新的区域结构,然后映射共享区域,设置程序计数器,使之指向代码区域的入口点,进入main函数,CPU为hello分配时间片执行逻辑控制流。hello通过Unix I/O管理来控制输出。hello执行完成后shell会回收hello进程,并且内核会从系统中删除hello所有痕迹,至此,hello完成O2O的 过程。

1.2 环境与工具

硬件环境:AMD R7

软件环境:Window 11;

开发工具:visual studio 2022; Vmware Workstation 16 player;Ubuntu 20.04 64位

1.3 中间结果

hello.c: 源代码

hello.i: 预处理后的文本文件

hello.s: 编译之后的汇编文件

hello.o: 汇编之后的可重定位目标执行文件

hello: 链接之后的可执行文件

hello.o.elf: hello.o的ELF格式

hello.elf: hello的ELF格式

hello.o.txt: hello.o反汇编代码

hello.txt: hello的反汇编代码

1.4 本章小结

本章介绍了P2P和O2O的概念,概述了hello程序的生命周期,记录了本次作业的环境和工具,记录了作业过程中生成的中间文件。

第2章 预处理

2.1 预处理的概念与作用

1.概念:预处理是编译器在编译开始之前调用预处理器来执行以#开头的命令(读取头文件、执行宏替代等)、删除注释、包含其他文件、执行条件编译、布局控制等修改原始的C程序,生成以.i结尾的文本文件。

2.作用:

1)执行原文件包括。#include命令告诉预处理器(cpp)读取系统头文件,并将其插入程序文本中。

2)执行宏替换。宏是对一段重复文字的简短描写,例如#define maxlength 100在预处理中会把所有maxlength代替为100

3)条件编译。是根据实际宏定义(某个条件)进行代码静态编译的手段。可根据表达式的值或者特指的宏是否被定义来确认编译条件。例如,#ifdef#ifndef可以判断某个宏是否被定义,如果已经定义,则执行接下来的语句。#endif 是#if,#ifdef,#ifndef这些条件命令的结束标志。

2.2在Ubuntu下预处理的命令

命令:gcc hello.c -E -o hello.i

图2.1使用gcc命令生成hello.i文件

图2.2 hello.i文件

2.3 Hello的预处理结果解析

图2.3 hello.i中main函数位置

打开hello.i发现整个文件有3000多行,main函数存在于文件的最后十几行。

解析:查看hello.i,在原有代码的基础上,将头文件stdio.h的内容引入,通过结果分析可以发现,预处理实现了在编译前对代码的初步处理,对源代码进行了某些转换。另外,如果代码中有#define命令还会对相应的符号进行替换。

2.4 本章小结

本章介绍了编译器预处理的相关概念。预处理操作是在编译前对代码进行某些转换与处理,从而使编译得以成功进行。例如实现将定义的宏进行符号替换、引入头文件的内容、根据指令进行选择性编译等。hello.c经过预处理,头文件插入,宏定义展开变为hello.i后,长度极大地增加,为下一阶段的编译做准备。

第3章 编译

3.1 编译的概念与作用

1.概念:编译就是将源语言经过词法分析、语法分析、语义分析以及经过一系列优化后生成汇编代码的过程。这儿的编译是指从hello.i 到 hello.s 即预处理后的文件到生成汇编语言程序程序,

2.作用:编译程序把一个源程序翻译成目标程序的工作过程分为五个阶段:词法分析;语法分析;语义检查和中间代码生成;代码优化;目标代码生成。

1)词法分析的任务是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。

2)编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。编译程序的语法规则可用上下文无关文法来刻画。

3)中间代码是源程序的一种内部表示,或称中间语言。中间代码的作用是可使编译程序的结构在逻辑上更为简单明确,特别是可使目标代码的优化比较容易实现中间代码,即为中间语言程序,中间语言的复杂性介于源程序语言和机器语言之间。

4)代码优化是指对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。所谓等价,是指不改变程序的运行结果。所谓有效,主要指目标代码运行时间较短,以及占用的存储空间较小。这种变换称为优化。

5)目标代码生成是编译的最后一个阶段。目标代码生成器把语法分析后或优化后的中间代码变换成目标代码。   

       

3.2 在Ubuntu下编译的命令

命令:gcc hello.c -S -o hello.s

图3.1使用gcc命令生成hello.s文件

图3.2 hello.s文件

3.3 Hello的编译结果解析

3.3.1数据

图3.3伪指令

伪指令用于指导汇编器和链接器的工作。

.file      "hello.c"       // 申明源文件

.text                    // 指明代码段

.section .rodata   // 指示为rodata节

3.3.2rodata节数据

图3.4编译代码数据段

如图,在.rodata段,我们有两个数据:

1) 一个是LC0它存储的是我们第一条printf语句打印的字符串用法:“Hello 学号 姓名 秒数!\n”,均为编码表示。

2) 另一个是.LC1,由两个字符串,printf输出格式话化参数,打印学号和姓名。符。

3.3.3局部变量

图3.5main函数

1)两个参数 int argc 和 char *argv[] 存在栈中地址-20和-32处的寄存器%edi和%rsi。hello.c中main函数的局部变量是i 。在汇编中将变量i放在了栈上。

图3.6局部变量i

观察验证:-4(%rbp)被赋予了0的初值,然后与7进行比较,参与了循环,即可证明局部变量i存储在了-4(%rbp)的位置

图3.7循环中的局部变量i

3.3.4赋值操作

使用数据传送命令,可以进行赋值操作。最简单形式的数据传输类型是MOV类,MOV有movb, movw, movl, movq等操作。分别操作1、2、4、8字节的数据。mov操作的操作源可以是立即数、寄存器、内存。目的操作数可以是:寄存器、内存。即立即数只能作为mov的操作源。x86-64规定两个操作数不能都指向内存。

观察赋值语句 i = 0对应的汇编。

图3.8汇编代码

也就是说,通过movl把立即数0,放入i中,i是四字节int型。

3.3.5算数操作

x86-64中的运算指令如下图所示:

图3.9 x86-64中的运算指令

自加运算

3.3.6关系运算

CPU维护着一组单个位的条件码(condition code),它们描述了最近算数运算或逻辑运算的属性。常见的条件码有:

CF: 进位标志

ZF: 零标志

SF: 符号标志

OF: 溢出标志

算数运算会设置条件码。此外,CMP指令和TEST指令能够设置条件码并不改变其他寄存器。CMP指令与SUB指令行为一致;TEST指令与AND指令行为一致。

在我们的hello程序中,出现了i < 9 的关系运算,我们观察它的汇编代码实现:

图3.10比较关系

汇编代码利用cmpl $8, -4(%rbp) 和 jle小于等于跳转,实现 i<9,即利用i<=8实现i<8,程序正常会跳转至.L4。

3.3.7数组运算

对于数据类型T和整型常数N。T[N]若其位置为Xa,那么T[i]就被存放在Xa+sizeof(T)*i的地址。在hello.c中访问了argv[1]与argv[2]。

对应到汇编代码:

图3.11访问数组

argv[]首地址为-4(%rbp)。-4(%rbp)+16是argv[2]的地址,它指向的值被放入了%rdx;-4(%rbp)+8的地址是argv[1],将其指向的值放入%rsi。argv数组中存储的数据类型是char*,占据8个字节。而-4(%rbp)+16与-4(%rbp)+8恰好差8个字节。

3.3.8控制转移

条件分支的实现由两种主要的方式:

一、通过结合有条件跳转和无条件跳转,这是使用控制的条件跳转(if);

二、计算一个条件操作的两种结果,根据条件是否满足从中选取一个(switch)。

hello程序中有一个if(argc != 4)的条件判断,汇编如下

图3.12条件跳转

就是判断命令行输入符不符合要求

C语言有多种循环结构。

do-while循环

while [jump to the middle]:

while [guarded-do]:

对于for循环,gcc会产生while的一种。

上面的就是jump to the middle

首先在.L2中进行变量初始化,然后跳转到.L3中进行条件测试,满足条件则进入.L4中执行循环体。

3.3.9函数操作

在函数调用时,包括了许多机制:(P调用Q)

传递控制:程序计数器设置为Q的地址,返回时,计数器设置为Q之后指令的地址。

传递数据:P向Q提供了一个或多个参数,Q向P提供了一个返回值。

分配、释放内存。

图3.13printf函数汇编调用

将.LC0中的字符串作为参数放入了%rdi,通过call调用了函数puts。x86-64中,我们最多可以通过栈传递6个整形参数。参数有特定顺序:

图3.14参数传递顺序

也就是说,位于.LC0的字符串是puts函数的第一个参数。

3.4 本章小结

本章简述了编译的概念与作用,从伪指令、rodata节数据、局部变量、赋值操作、算数运算、关系运算、数组运算、控制转移、函数操作等方面对hello.s中的汇编代码进行了简单解析。

第4章 汇编

4.1 汇编的概念与作用

1.概念:汇编器(as)将hello.s文件翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件(这里是hello.o)中。

2.作用:实现将汇编代码转换为机器指令(二进制程序),使之在链接后能够被计算机直接执行。

4.2 在Ubuntu下汇编的命令

命令:

图4.1使用gcc命令生成hello.o文件

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

图4.2 hello.o的ELF格式

该可重定位目标文件,有13个节,查看头节表:

图4.3hello.o的头节表

节头表记录了.o文件中每个节的名称、类型、地址、偏移量、大小、总大小、读写权限(flags)、链接、信息以及对齐方式信息。另外, 由于可重定位目标文件还未链接,每个节未被分配地址故地址信息都为0。

查看符号表:

图4.4hello.o的符号表

每个节都从0开始,用于重定位,这段代码只读

符号表记录了程序中的符号以及他们类型,位置等信息

例如main为全局变量,类型是函数等等

查看重定位节:

图4.5hello.o的重定位节

rela.text节中存放的是.text节的所有重定向信息,程序中任何对外部函数或全局变量的引用在链接过程中都需要被重定向,而对于每一个符号的重定位,都需要到重定位节中找到相对应的重定位条目。每个重定位条目包含如下信息:符号名称、节内偏移、重定位类型和addend值。本程序中需要被重定位的是printf、puts、exit、sleepsecs、getchar、sleep等函数和.rodata中的两个字符串。

注意到:.rodate中两个隐性定义的全局字符串没在.rela.date节中,而被放在了.rela.text节中,是因为它们是只读数据。

4.4 Hello.o的结果解析

命令 objdump -d -r hello.o 

反汇编如下:

图4.6hello.o的反汇编

对照分析:

1)汇编语言中,代码的上下文定位是通过.LC0等标识符完成的,反汇编代码中,所有指令得到了具体的地址。

2)汇编代码中,分支跳转的目标是以.L2等标识符代表的代码段的开头,反汇编代码中,使用相对地址进行分支跳转。

3)汇编代码中,对外部函数的调用只使用了该函数的名称(符号),反汇编代码中,除了标识了call指令返回的下一条指令,还留下了重定位条目信息等待链接器补全被调用函数的地址。

4)汇编代码中使用.LC0(%rip)访问全局变量,反汇编代码中使用0x0(%rip),等待链接器通过重定位条目补全地址。

说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

4.5 本章小结

本章简述了汇编的概念与作用,对hello.s进行汇编生成了相应的可重定向目标文件hello.o 。使用readelf工具读取了elf文件的相关信息,观察了elf文件中的文件头、程序节、符号表等信息。另外,可以通过objdump反汇编目标文件,从中可以得到机器代码与汇编代码的对照,比较了hello.s与hello.o反汇编形成的汇编代码的差异。

5章 链接

5.1 链接的概念与作用

1.链接:链接是将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译时,也就是在源代码被翻译成机器代码时;也可以执行与加载时,也就是在程序被加载器加载到内存并执行时;甚至执行于运行时,也就是应用程序来执行

2.作用: 链接将各种代码和数据片段收集并合成为一个单一的可执行文件,因此大型项目可被分解为若干个小模块,可以独立地修改和编译这些模块,最后再统一链接,便于项目的管理和修改。

5.2 在Ubuntu下链接的命令

命令:

ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o

图5.1链接命令

5.3可执行目标文件hello的格式

图5.2可执行目标文件

该文件有27个节

图5.3节表头

5.4 hello的虚拟地址空间

命令行输入edb,然后打开文件hello,查看datadump一栏

 

图5.4edb查看虚拟地址空间

程序在0x0000560e7b56f000-0x0000560e7b570000这段虚拟地址

5.5 链接的重定位过程分析

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

首先,我们观察hello.o的反汇编代码。可以观察到,有许多地方并没有填入正确的地址,正等待进行链接。其中R_X86_64_32表示的类型是32位直接寻址,相应地方会填入.rodata的真实地址。而R_X86_64_PLT32表示puts函数和exit函数需要通过共享库进行动态链接。

图5.5hello.o的反汇编

在hello文件的反汇编代码中,我们发现之前的重定位地址已经被填入了正常的地址。

图5.6hello的反汇编

观察elf文件信息,401020恰好属于.plt节,402000恰好属于.rodata节

1)链接中新增加了函数如exit,printf,sleep,getchar等

2)hello中增加了.init和.plt节

3)hello中无hello.o中的重定位条目,并且跳转和函数调用的地址在hello中都变成了虚拟内存地址

5.6 hello的执行流程

此时使用edb执行hello程序,下面列出了从加载hello到_start,到call main,以及程序终止的所有过程,包括其调用与跳转的各个子程序名或程序地址。

<_init>:0x4e8

<.plt>:0x500

<puts@plt>:0x510

<__cxa_finalize@plt>:0x520

<_start>:0x 00530

<deregister_tm_clones>:0x560

<register_tm_clones>:0x5a0

<__do_global_dtors_aux>:0x5f0

<frame_dummy>:0x630

<main>:0x63a

<__libc_csu_init>:0x660

<__libc_csu_fini>:0x6d0

    <_fini>:0x6d4

5.7 Hello的动态链接分析

   对于动态共享链接库中PIC函数,编译器没有办法预测函数的运行时地址,所以需要添加重定位记录,等待动态链接器处理,为避免运行时修改调用模块的代码段,链接器采用延迟绑定的策略。动态链接器使用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。

图5.7.got.plt起始表的位置为0x404000

动态链接是把程序拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序。

图5.8 -dl_init前的.got.plt内容

 

图5.9 -dl_init后的.got.plt内容

从两个图中我们可以发现,-dl前后有两个字节的内容发生改变。PLT内存储的代码跳转到GOT表明的位置,然后再调用链接器。GOT中的PLT相应指令会被链接器修改,链接之后重新调用PLT时就能跳转到正确的地方。

5.8 本章小结

本章简述了链接的概念与作用,分析了我们经过链接生成的hello文件的结构(ELF格式)以及与之前经过链接的hello.o文件的异同,我们分析了hello文件的运行流程,使用edb探索了动态链接的过程,同时通过实例分析了重定位过程、加载以及运行时函数调用顺序以及动态链接过程,深入理解链接和重定位的过程

6章 hello进程管理

6.1 进程的概念与作用

1.概念:进程是执行中程序的一个实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的

2.作用:进程能够提供给应用程序一些关键抽象:

1) 一个独立的逻辑控制流。进程使得我们感觉好像在独占处理器。

2) 一个私有地址空间。进程使得我们感觉好像独占地使用内存系统。

6.2 简述壳Shell-bash的作用与处理流程

shell是命令行界面的解析器,能够为用户提供操作界面,提供内核服务。shell能执行一系列的读、求操作,然后终止。读操作读取来自用户的一个命令行。求值操作解析命令代表用户运行程序。

shell的处理流程为:

1) 读取用户输入

2) 解析用户输入

3) 若要执行内部命令,直接执行。

4) 若要执行非内部命令,shell会fork子进程,在子进程中execve执行相关命令。

5) 根据&的有无,确定程序的前后台运行。

6.3 Hello的fork进程创建过程

在命令行输入 ./hello 执行 hello 程序时,由于hello不是内部命令,所以shell会fork一个子进程并进行后续操作。

新建的子进程几乎和父进程相同。子进程拥有与父进程用户级虚拟地址空间相同且独立的一份副本,与父进程任何打开的文件描述符相同的副本。

使用fork()函数来创建一个子进程,fork函数的原型为:pid_t fork(void)

fork()函数有以下特点:

1)调用一次,返回两次。一次返回至父进程,返回的是子进程的pid;一次返回至子进程返回值为0。

2) 并发执行。父子进程是并发运行的独立进程。

3) 相同但独立的地址空间。子进程创建时,两个进程具有相同的用户栈、本地变量、堆、全局变量、代码。但是二者对这行的改变都是相互独立的。

4) 共享文件。

6.4 Hello的execve过程

使用fork创建进程后,子进程便会使用execve加载并运行hello程序,且带参数列表argv以及环境变量envp。execve调用一次,从不返回。

图6.1参数列表与环境变量列表

观察可知,argv指向一个指针数组,这个指针数组中的每一个指针指向一个参数字符串。其中argv[0]使我们所运行的程序的名字。envp指向一个指针数组,这个数组里面的每一个指针指向一个环境变量的字符串。环境变量字符串的格式为”name = value”。使用getenv函数获取环境变量,setenv、unsetenv来设置、删除环境变量。

execve会调用启动加载器。加载器会删除子进程现有的虚拟内存段,创建一组新的代码、数据、堆、栈。新的栈和堆被初始化为0。通过虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码数据初始化。最后,跳转到_start地址,最终调用main函数。

6.5 Hello的进程执行

系统中每个程序都运行在某个进程的上下文中。上下文是程序正确运行所需要的状态,由系统内核维持。

一个运行多个进程的系统,进程逻辑流的执行可能是交错的。每个进程执行它的流的一部分, 然后被抢占,轮到其他进程执行。一个逻辑流在时间上与另一个重叠,成为并发流。一个进程执行它的控制流的一部分时间叫做时间片。

控制寄存器利用模式位描述了当前进程享有的特权:当设置了模式位时,进程运行在内核模式中,可以执行任何命令,访问任何内存;当没有设置模式位时,进程为用户模式,不允许执行特权指令,不允许直接引用内核区的代码,数据。

在进程执行时,内核可以抢占当前进程,并重新开始一个先前被抢占了的进程。这种决策称为调度。当进程调度一个新的进程运行后,会使用上下文切换来将控制转移到新的进程。

上下文切换时:

1) 保存当前进程的上下文;

2) 恢复某个先前被抢占进程的被保存的上下文;

3) 将控制传递给新进程。系统调用、中断可能引起上下文切换。

图6.2进程的上下文切换

hello程序执行了sleep系统调用,应发了陷阱异常。此时会从用户模式进入内核模式,使程序休眠一段时间,将控制转给其他进程。当sleep结束后,发送信号给内核,进入内核状态异常处理,此时hello程序得以重新回到用户模式。当执行getchar函数时,会使用read系统调用,产生上下文切换。

6.6 hello的异常与信号处理

异常可分为四类:中断、陷阱、故障、终止。

图6.3异常

hello运行过程中,可能出现以上几种异常的原因:

图6.4正常运行

图6.5疯狂按回车

使用Ctrl + C发出SIGINT信号,终止程序

图6.6 Ctrl+C终止程序

使用Ctrl + Z发送SIGTSTP信号,程序停止直到下一个SIGCONT

图6.7 Ctrl+Z中断程序

使用ps查看进程信息

图6.8查看进程信息

使用jobs查看作业信息,可以发现hello程序处于停止状态

图6.9查看作业信息

使用pstree查看进程树

图6.10查看进程树

使用fg将后台程序置于前台

图6.11将后台进程置于前台运行

使用kill发送信号

图6.12kill发送信号

6.7本章小结

本章介绍了程序在shell执行及进程的相关概念。程序在shell中执行是通过fork函数及execve创建新的进程并执行程序。进程拥有着与父进程相同却又独立的环境,与其他系统进并发执行,拥有各自的时间片,在内核的调度下有条不紊的执行着各自的指令,分析了hello程序使用fork创建子进程的过程以及使用execve加载并运行用户程序的过程,运用上下文切换、用户模式、内核调度等知识,分析了hello进程的执行过程,最后分析了hello对于异常及信号的处理并进行了实际操作。

结论

Hello的程序先经历编译器系统得到可执行目标程序,接着通过在shell中的运行,在存储结构中的存储,以及输入输出,让我们看到了hello的人生经历。

hello.c(文本文件)经过预处理器(cpp)得到hello.i

hello.i(被修改了的源程序,文本文件)经过编译器(ccl)得到hello.s

hello.s(汇编程序,文本)经过汇编器(as)得到hello.o

hello.o(可重定位目标程序,二进制)与其他.o文件经过连接器(ld)得到hello,在链接过程中,hello.o的程序会与其他程序进行链接,或者与静态库,动态库进行链接。

hello为可执行目标程序(二进制文件)

hello进入到执行阶段,在内核系统中,shell为该程序调用fork函数为其分配进程,在子进程中使用execve函数执行对该程序的操作运行。此时hello程序往往会与其他进程并行执行在流水线中。

   在编译以及执行阶段中,hello程序会出现在不同地址中,这让我们初步了解了计算机的存储结构。

同时输出“hello world”到屏幕上与计算机系统的I/O设备相关。

当hello所属进程结束执行后,父进程会使用waitpid或者wait对该进程进行回收。到这里,内核会回收hello程序有关所有信息,我们可以认为hello程序的任务到此结束了。

计算机系统这门课程是计算机思维导论这门课程的进一步深层次的延申。由于大学之前从未了解过计算机的原理,甚至从未接触过代码,计算机系统这门课程对与了解计算机有很大的帮助。通过CSAPP(Computer System A Programmer’s Perpective)这本教材,对计算机系统有了比较深刻的了解与认识。同时对本课程所开设的实验印象尤为深刻。正如计算机一样,这门课程以及这门课程的实验对自己来说都是十分抽象的,老师只对大致方向进行模糊的讲解,很少设计具体操作,更不会有在实验中遇到问题的解析。因此在写实验的过程中,往往会遇到很多的问题,同时,在网上查找资料也往往没有进一步具体的操作,因此本课程学习起来十分吃力。但是学习这门课程,充分锻炼了自己的内心,从对一个什么都不会的实验的一种极度担心和恐惧的态度,转化为一种对于未知能学会大致进行解决的能力,虽然这个问题由于种种原因不能得到最完美的解决,但是至少提供了自己的一份方案或者学到了一份能够解决此问题的一个方案。

附件

hello.c: 源代码

hello.i: 预处理后的文本文件

hello.s: 编译之后的汇编文件

hello.o: 汇编之后的可重定位目标执行文件

hello: 链接之后的可执行文件

hello.o.elf: hello.o的ELF格式

hello.elf: hello的ELF格式

hello.o.txt: hello.o反汇编代码

hello.txt: hello的反汇编代码

参考文献

[1]  林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.

[2]  辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[3]  赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).

[4]  谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.

[5]  KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[6]  CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

[7]深入理解计算机系统(美)布赖恩特(Bryant,R.E.)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值