程序的装入和链接

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

程序在运行前需要经过以下步骤:编译程序对源程序进行编译生成目标程序.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,一经查实,立即删除!

相关文章

静态库的制作和使用

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

虚拟地址空间

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

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

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

网络编程套接字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); 分析: 第一个参数可以是AF_INET或AF_INET6&am…

gdb调试器(三)

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

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

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

makefile的两个变量(自动变量和普通变量)

(1)普通变量 如: objmain.o add.o sub.o mul.o div.o //将后面的值赋值给obj,obj就是一个普通变量 targetzsx //将zsx赋值给target makefile中已经定义的一些普通变量(通常格式都是大写,类似环境变量,它们都是普通…

【C++ Priemr | 15】虚函数表剖析(二)

一、多重继承&#xff08;无虚函数覆盖&#xff09; 下面&#xff0c;再让我们来看看多重继承中的情况&#xff0c;假设有下面这样一个类的继承关系。注意&#xff1a;子类并没有覆盖父类的函数。 测试代码&#xff1a; class Base1 { public: virtual void f() { cout <…

makefile中的两个函数(wildcard和patsubst)

(1) wildcard函数 作用是查找指定目录下指定类型的文件&#xff0c;并最终返回一个环境变量&#xff0c;需要用$取值赋值给另一个环境变量&#xff01;该函数只有一个参数&#xff0c;如取出当前目录下的所有.c文件&#xff0c;并赋值给allc普通变量&#xff1a; allc$(wildc…

C库函数

Linux的系统I/O函数&#xff08;read、write、open、close和 lseek等&#xff09;与C语言的C库函数&#xff08;libc.so库文件中&#xff09;都是相对应的&#xff0c;它们都是动态库函数。如下图所示&#xff0c;C库函数有fopen、fclose、fwrite、fread和fseek等。这些C库函数…

C库函数与Linux系统函数之间的关系

由上小节知道&#xff0c;C库函数是借助FILE类型的结构体来对文件进行操作的&#xff0c;其本身只是在用户空间&#xff08;I/O缓冲区&#xff09;进行读写操作&#xff0c;而数据在内核与用户空间之间的传递、以及将内核与I/O设备之间的数据传递都是该C库函数进行一系列的系统…

open函数和errno全局变量

&#xff08;1&#xff09;open函数 man man 查看man文档的首页 其中DESCRIPTION部分描述了man文档的每一章的章节内容 第2章System calls为系统调用&#xff0c;即Liunx系统函数。 man 2 open 查看第二章的open函数的详细帮助文件。 open函数用于打开一个已经的文件或者创…

open函数和close函数的使用

学习几个常用的Linux系统I/O函数&#xff1a;open、close、write、read和lseek。注意&#xff0c;系统调用函数必须都考虑返回值。 &#xff08;1&#xff09;open函数的使用 首先&#xff0c;需要包含三个头文件&#xff1a;<sys/types.h> <sys/stat.h> <…

1091. Acute Stroke (30)

One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the results of image analysis in which the core regions are identified in each MRI slice, your job is to calculate the volume of the stroke core. Input Speci…

stat函数(stat、fstat、lstat)

#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> //需包含头文件 有如下三个函数的函数原型&#xff1a; int stat(const char *path, struct stat *buf); 第一个形参&#xff1a;指出文件&#xff08;文件路径&#xff09;&…

【Leetcode | 235】 235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己…

CPU和MMU(内存管理单元)

CPU的架构&#xff1a;要求能够理解从源程序到微指令的整个经历过程&#xff1a;存储器的层次结构&#xff08;网络资源下载到硬盘、磁盘缓存、内存、Cache、寄存器&#xff09;&#xff1b;CPU的四大部分&#xff1a;ALU、CU、中断系统和寄存器&#xff1b;程序执行的整个过程…

【C++ Primer | 09】容器适配器

一、stack s.push(): 向栈内压入一个成员&#xff1b; s.pop(): 从栈顶弹出一个成员&#xff1b; s.empty(): 如果栈为空返回true&#xff0c;否则返回false&#xff1b; s.top(): 返回栈顶&#xff0c;但不删除成员&#xff1b; s.size(): 返回栈内元素…

进程控制块PCB(进程描述符)

&#xff08;1&#xff09;PCB 每个进程在内核中都有一个进程控制块&#xff08;PCB&#xff09;来维护进程相关的信息&#xff0c;Linux内核的进程控制块是task_struct结构体。grep -r “task_struct” / 可以查找根目录下&#xff0c;包含task_struct的文件文件。或者 find…

【C++ Primer | 19】控制内存分配

1. 测试代码&#xff1a; #include <iostream> #include <new> #include <cstring> #include <cstdlib> using namespace std;void* operator new(size_t size) {cout << "global Override operator new" << endl;if (void* p…