《深入计算机系统》(第六章)

来源:互联网 发布:python分析日志文件 编辑:程序博客网 时间:2024/05/17 23:20

                                               在系统上运行程序


第7章,链接:就是将不同部分的代码和数据收集和组合成一个单一文件的的过程,这个文件可被加载(或被拷贝)到存储器并执行。

链接可以执行在编译(源代码被转化为机器代码时)时,也可以执行于加载(程序被加载器加载到存储器并执行时)时,甚至执行与运行时。

链接是由链接器来安静地处理的,理解链接器的好处

1、帮助你构造大型程序2、避免一些危险的编程错误3、帮助你理解语言的作用域规则是如何实现的。4、理解其他的系统概念5、使你能够开发共享库。

大多数编译系统提供编译驱动程序,它为用户,根据需求调用语言预处理器、编译器、汇编器和链接器。

为了创建可执行文件,链接器必须完成两个主要任务1、符号解析 (将每个符号引用和一个符号定义联系起来)2、重定位。编译器和汇编器生成从地址零开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后对其修改所有对这些符号的引用,使他们指向这个这个存储器位置,从而重定位这些节。

目标文件:1、可重定位目标文件2、可执行目标文件3、共享目标文件

编译器和汇编器生成可重定位目标文件(包括共享目标文件)。链接器生成可执行目标文件。

一个目标模块就是一个字节序列,而一个目标文件就是一个存放在磁盘文件中的目标模块。

链接器解析符号引用的方法是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义联系起来。

在编译时,编译器输出每个全局符号给汇编器,或强或弱,而汇编器把这个信息隐含地编码在可重定位目标文件的符号表里。函数和已初始化的全局变量是强符号,未初始化的全局变量时弱符号。

Unix链接器处理多出定义的符号的规则:1、不允许有多个强符号2、如果有一个强符号和许多弱符号,那么选择强符号3、如果全部是若符号那么任意选择。

磁盘取数据要用一段相对较长的时间,所以内核执行从进程A到进程B的上下文切换,而不是在这个时间内等待什么都不做。

每个进程都有一个唯一的正数(非零)进程ID

对于程序员来讲,我们可以认为进程总是处于下面三种状态之一:1、运行  2、暂停   3、终止

新建的子进程几乎但不完全与父进程相同。父进程与新创建的子进程之间最大的区别在于它们有不同的PID.

fork函数是有趣的,因为 它只被调用一次,却会返回两次。一次在调用进程(父进程)中,一次是在新创建的子进程中。在父进程中,fork返回子进程的PID.在子进程中fork返回零,因为子进程的PID总是非零的,返回值就提供一个明确的方法来分辨程序是在父进程换是在子进程中执行的。fork函数在新的子进程中运行系相同的程序,新的子进程是父进程的一个复制品。

回收子进程:当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除,取而代之的是进程被保持在一种终止状态中,直到他被父进程收回(reaped),当父进程已经收回已终止的子进程时,内核将子进程的推出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了。

僵死进程:一个终止了但还未被回收的进程称。

如果父进程没有回收它的僵死子进程就终止了,那么内核就会安排init进程来回收他们。init进程的PID为1.并且是在系统初始化时由内核创建的。即使僵死子进程没有运行,它们仍然消耗系统的存储器资源。

等待集合的成员是由参数PID来确定的:

1、如果pid>0,那么等待集合就是一个单独的子进程,它的进程ID等于PID

2、如果PID=-1,那么等待集合就是由父进程的所有子进程组成的。

让进程休眠:sleep函数将一个进程挂起一段时间。

加载并运行程序:execve函数在当前进程的上下文中加载并运行一个新程序。

进程和程序的区别:

程序是代码和数据的集合;程序可以作为目标模块存在于磁盘上,或者作为段存在于地址空间中。而进程是执行程序的一个特殊实例,程序总是运行在某个程序的上下文中。

信号:研究一个更高层软件形式的异常,它允许进程中断其他进程。

一个信号就是一条消息,它通知进程一个某种类型的事件已经在系统中发生。

每种信号类型都对于某个类型的系统事件。

传送一个信号到目的进程是由两个不同步骤组成:1、发送信号2、接收信号。

一个只发出而没有被接受的信号叫做待处理信号。一个待处理信号最多被接收一次。

Unix系统提供了大量的机制,用来发送信号给进程,所有这些机制都是基于进程组。

进程组:每个进程都只属于一个进程组,进程组是由一个正整数进程组ID来标识的。

一个子进程和它的父进程同属于一个进程组。一个进程也可以通过使用setpgid函数来改变自己活着其他进程的进程组。

接收信号:当内核从一个异常处理程序返回,准备将控制传递给进程P时,它会检查未被阻塞的待处理信号的集合,如果这个集合为空(通常情况),那么内核传递控制给P的逻辑控制流中的下一条指令。

每一个信号类型都有一个预定义的默认行为:

1、进程终止。

2、进程终止并转储存储器。

3、进程暂停直到被SIGCONT信号重启。

4、进程忽略该信号。

信号处理问题:对于只捕捉一个信号并终止的程序来说信号处理是简单直接的,然而,当一个程序要捕捉多个信号时,一些细微的问题就产生了:

1、待处理信号被阻塞。

2、带处理信号不会排队等待。任意类型至多只有一个待处理信号。有第二个信号就会被简单地丢弃。

3、系统调用可以被中断

非本地跳转:C提供一种形式的用户异常控制流,称为非本地跳转,它将控制直接从一个函数转移到另一个当前正在执行的函数。,但不需要经过正常的调用-返回序列。

非本地跳转的一个重要应用就是循序从一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况而引起的。如果哦在一个深层嵌套的函数调用中发现一个错误情况,我们可以使用非本地跳转直接返回到一个普通的本地化的错误处理程序,而不是费力地解开调用栈。

非本地跳转的另一个重要应用是使一个信号处理程序分支到,一个特殊的代码位置,而不是返回到被信号到达中断了的指令的位置。

异常控制流发生在计算机系统的各个层次。

由四种不同类型的异常:中断、故障、终止和陷阱。