程序的装入和链接

注:这是本人学习汤小丹等编写的计算机操作系统(西安电子科技大学出版社)的学习笔记,因此许多引用来源于此书,在正文中就不注明了!

程序在运行前需要经过以下步骤:编译程序对源程序进行编译生成目标程序.obj;链接程序将目标程序和需要的库文件链接在一起形成可执行程序.exe,即一个完整的装入模块;装入程序将装入模块装入内存,然后运行。依次称为程序的编译、链接、装入。

程序的装入方式有三种:绝对装入方式、可重定位装入方式和动态运行时的装入方式。

绝对装入方式。适用系统小且仅运行单道程序的情况,此时很容易知道程序装入后将留在内存的什么位置,因此程序经编译后将产生含有绝对地址(物理地址或内存地址)的目标代码,装入程序将按照该地址装入内存,因此装入内存后程序的实际地址(内存地址)与程序的逻辑地址完全相同,不需要进行地址变换。程序中所使用的绝对地址既可以在编译或汇编时指出(将程序中的符号地址转变为绝对地址),也可以由程序员直接赋予(用绝对地址编程),但要求程序员熟悉内存的使用情况。

可重定位装入方式。用户程序编译链接后形成的可执行程序的起始地址一般都是从0开始的,其它地址都是相对于起始地址计算的(逻辑地址),因此此时装入后的实际地址与程序中的逻辑地址不同,需要进行地址变换。对于多道程序采用基址寻址,有效地址EA=A+(BR),A为指令的地址码字段,BR为基址寄存器,其内容由OS和系统管理程序决定,值不变,系统根据BR的值为程序分配储存空间,因此装入后需要根据该值进行地址变换,即程序中的逻辑地址(或虚拟地址)加上基址即可,对于程序中的数据地址(语句中引用到的逻辑地址)和外部调用符号都要进行相应的修改,即需要对程序地址(指令地址)、数据地址和外部调用符号进行修改。把在装入时对目标程序中指令和数据地址的修改过程称为重定位,对于可重定位装入方式,该地址变换是一次完成的,以后不可以再修改,因此称为静态重定位。变址寻址,EA=A+(IX),IX为变址寄存器,此时IX的值可变,由用户设定,A的值不变,通常为数组的起始地址。变址寻址用于处理数组问题,编制循环程序;相对寻址,EA=(PC)+A,常用于转移类指令,无论该程序在主存的哪一个区域都可以正确运行,对于编写浮动程序非常有利。

动态运行时的装入方式。如果程序在内存中会发生移动,此时物理位置会发生变化,因此可重定位装入方式不再适用,移动后需要再次对地址进行修改,如在具有对换功能的系统中,一个进程可能会被多次换入和换出,其位置总是在发生变化,此时可采用动态运行时的装入方式。该方式,将装入模块装入内存后,并不立即把装入模块中的逻辑地址(虚拟地址)变换为物理地址,而是把这种地址变换过程推迟到真正执行时进行,即执行时通过:EA=A+(BR)来完成地址变换,因此装入模块的地址实际上一直都是逻辑地址(虚拟地址),程序执行时自动根据该地址进行变换,此时在CPU中需要设置一个基址寄存器BR,对于每一个进程,OS都会为其分配一个基址。

链接程序的功能是将一组目标程序和需要的库文件链接成一个完成的装入模块,根据进行链接的时间不同分为三种链接方式:静态链接方式、装入时动态链接、运行时动态链接。

静态链接方式。在装入之前就将所有的目标模块和需要的库文件链接成为一个完整的装入模块(可执行文件.exe),以后不再拆开。每个目标模块都采用的是逻辑地址,即起始地址均为0,因此链接过程中需要做以下修改:对相对地址(程序或指令地址、数据地址)进行修改;对外部调用符号进行修改,如CALLB,表示调用模块B(目标文件B),改为JSR“L”,L为B的首地址。如下:

装入时动态链接。对于各个目标模块和库文件采用边装入边链接的方式,即在装入一个目标模块时,若发生一个外部调用事件,将引起装入程序去找出相应的外部调用模块,并将其装入内存,然后进行链接,并按照上图所示的方式修改地址。该方式具有以下优点:1.便于对目标模块修改和更新。对于经过静态链接装配在一起的装入模块,如果要修改或更新某个目标模块,需要将其拆开,低效且麻烦,而且有时不可能。对于动态链接方式(装入时动态链接和运行时动态链接),模块是分开存放的,对于修改和更新很容易实现(注意即使装入了的模块,原模块依然存在,相当于拷贝一份装入)。2.便于实现目标模块的共享。对于静态链接方式,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享。而动态链接方式却可以容易将一个目标模块链接到几个应用模块上。

运行时动态链接。其是对装入时动态链接方式的一种改进。这种链接方式是,将对某些模块的链接推迟到程序执行时才进行。亦即,在执行过程中,当发现一个被调用模块尚未装入内存时,立即由OS去找到该模块,并将之装入内存,将其链接到调用者模块上。凡在执行过程中未被用到的目标模块(如处理错误用的模块,如果运行过程中不出现错误,则不会用到),都不会被调入内存和被链接到装入模块上,这样不仅能加快程序的装入过程,而且可节省大量的内存空间

在运行时进行链接,通常被链接的共享代码称为动态链接库(DLL, Dynamic Link Library)或共享库(shared library

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内存对齐

1. 对齐原则: 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。结构(或…

1006. 换个格式输出整数 (15)

让我们用字母B来表示“百”、字母S表示“十”&#xff0c;用“12...n”来表示个位数字n&#xff08;<10&#xff09;&#xff0c;换个格式来输出任一个不超过3位的正整数。例如234应该被输出为BBSSS1234&#xff0c;因为它有2个“百”、3个“十”、以及个位的4。 输入格式&a…

静态库的制作和使用

Linux下的静态库为lib*.a格式的二进制文件&#xff08;目标文件&#xff09;&#xff0c;对应于Windows下的.lib格式的文件。 &#xff08;1&#xff09;命名规则 lib库名字 .a libMytest.a &#xff0c;则库名字为mytest。下面以具体的代码为例介绍如何制作静态库。 //mai…

IO多路复用之select

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); 分析&#xff1a; nfds: 监控的文件描述符集里最大文件描述符加1&#xff0c;因为此参数会告诉内核检测前多少个文件描述符的状态 readfds&#xff1a; …

1031. 查验身份证(15)

一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下&#xff1a; 首先对前17位数字加权求和&#xff0c;权重分配为&#xff1a;{7&#xff0c;9&#xff0c;10&#xff0c;5&#xff0c;8&#xff0c;4&#xff0c;2&#xff0c;1&…

虚拟地址空间

对于每一个进程都会对应一个虚拟地址空间&#xff0c;对于32位的操作系统&#xff08;其指令的位数最大为32位&#xff0c;因此地址码最多32位&#xff09;&#xff0c;虚拟地址空间的大小为B即0~4GB的虚拟地址空间&#xff0c;其中内核空间为1GB&#xff0c;如下所示&#xff…

Leecode 69. x 的平方根

实现 int sqrt(int x) 函数。 计算并返回 x 的平方根&#xff0c;其中 x 是非负整数。 由于返回类型是整数&#xff0c;结果只保留整数的部分&#xff0c;小数部分将被舍去。 示例 1: 输入: 4 输出: 2 示例 2: 输入: 8 输出: 2 说明: 8 的平方根是 2.82842..., 由于返回类…

1002. 写出这个数 (20)

读入一个自然数n&#xff0c;计算其各位数字之和&#xff0c;用汉语拼音写出和的每一位数字。 输入格式&#xff1a;每个测试输入包含1个测试用例&#xff0c;即给出自然数n的值。这里保证n小于10100。 输出格式&#xff1a;在一行内输出n的各位数字之和的每一位&#xff0c;拼…

C/C++中NULL指针

先谈一下C/C的强制类型转换Type cast。与强制类型转换相对应的是自动类型转换。或者强制类型转换叫显示类型转换&#xff0c;自动类型转换叫隐式类型转换。自动类型转换会在赋值运算、混合运算、参数传递、返回函数返回值、格式化输出时且当类型出现不一致时发生&#xff0c;转…

1009. 说反话 (20)

给定一句英语&#xff0c;要求你编写程序&#xff0c;将句中所有单词的顺序颠倒输出。 输入格式&#xff1a;测试输入包含一个测试用例&#xff0c;在一行内给出总长度不超过80的字符串。字符串由若干单词和若干空格组成&#xff0c;其中单词是由英文字母&#xff08;大小写有区…

动态库(共享库)的制作和使用

Linux下的动态库为lib*.so格式的二进制文件&#xff08;目标文件&#xff09;&#xff0c;对应于Windows下的.dll格式的文件。 &#xff08;1&#xff09;命名规则 lib库名.so &#xff08;2&#xff09;动态库的制作 1&#xff09;生成与位置无关的代码&#xff08;.o&…

孤儿进程、僵尸进进程

一、儿进程与僵尸进程 1、基本概念 我们知道在unix/linux中&#xff0c;正常情况下&#xff0c;子进程是通过父进程创建的&#xff0c;子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工…

管道 -pipe

gcc编译器将源代码编译成可执行程序的过程中&#xff0c;需要经过许多中间步骤&#xff08;预处理、编译、汇编、链接&#xff09;&#xff0c;这些过程实际上是由不同的程序来负责完成的&#xff08;/usr/bin/gcc、cpp、ccl、as和ld等&#xff09;。在这个过程的每一个阶段中&…

gdb调试器(一)

在默认情况下&#xff0c;gcc在编译时不会把调试符号插入到最终生成的二进制代码&#xff08;机器代码&#xff09;中&#xff0c;因为这样会急剧增加可执行程序的大小。如果需要在编译时生成调试符号的信息&#xff0c;则可以采用-g或-ggdb参数。 gcc在产生调试信息时&#x…

1048. 数字加密(20)

本题要求实现一种数字加密方法。首先固定一个加密用正整数A&#xff0c;对任一正整数B&#xff0c;将其每1位数字与A的对应位置上的数字进行以下运算&#xff1a;对奇数位&#xff0c;对应位的数字相加后对13取余——这里用J代表10、Q代表11、K代表12&#xff1b;对偶数位&…

网络编程套接字API

uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);int inet_pton(int family, const char *strptr, void *addrptr); 分析&#xff1a; 第一个参数可以是AF_INET或AF_INET6&am…

gdb调试器(二)

Linux下的gdb&#xff08;GNU Debugger&#xff09;是一个用来调试C、C程序的调试器&#xff08;命令行方式的调试器&#xff09;&#xff0c;能够在程序运行期间观察程序的内部结构和内存的使用情况。程序员也可以使用gdb来跟踪程序中的错误&#xff0c;从而减少程序员的工作量…

gdb调试器(三)

File/file 装入想要调试的可执行文件 run(r) 执行当前被调试的程序 kill(k) 终止正在调试的程序 quit(q) 退出gdb shell 使用户不离开gdb就可以执行Linux的shell命令 backtrace(bt) 回溯跟踪&#xff08;当对代码进行调试时&#xff0c;run后…

IO多路复用之poll

1. poll函数原型&#xff1a; int poll(struct pollfd *fds, nfds_t nfds, int timeout);参数&#xff1a; fds&#xff1a;指向一个结构体数组的第0个元素的指针&#xff0c;每个数组元素都是一个struct pollfd结构&#xff0c;用于指定测试某个给定的fd的条件 nfds&#x…

makefile文件的书写规则(make和makefile)

对于makefile&#xff0c;掌握一个规则&#xff0c;两个变量和三个函数。下面介绍一个规则。 makefile的作用&#xff1a;一个项目代码的管理工具。当一个项目的代码文件数&#xff08;如.c文件&#xff09;太多&#xff0c;用gcc编译会太麻烦&#xff0c;如果全部文件一次性编…