进程—内存段机制

进程—内存段机制

开始阅读之前可以先看看The Curse of Segments

1.x86的硬件段机制

wiki.osdev-Segmentation

Modes of Memory Addressing on x86

1.1 段机制的引入

? 就在8086CPU出现之前,地址总线已经是16位(64KB)的了,在刚开始,段的引入是为了解决“地址总线的宽度大于寄存器的宽度”这个问题。例如8086的寄存器只有16位,但是地址总线却有20位(1MB),为了使程序能利用到1MB的物理内存空间却又不对寄存器长度作出改变(内存容量扩大,成本基本保存不变),Intel在8086中引入了段机制。

? 8086提供了四个段寄存器 CS, DS, SS, 和ES ,从缩写的含义来看,这四个寄存器分别表示当前运行进程的code segment (CS), data segment (DS),stack segment (SS), 和一个用户自定义的段 (ES),操作系统可以同时跟踪一个进程的四个段。

1.2 段机制汇编

? 从纯硬件的角度来讨论段机制对汇编程序员的影响:

x86寻址模式之register indirect addressing modes:

The 80x86 CPUs let you access memory indirectly through a register using the register indirect addressing modes. There are four forms of this addressing mode on the 8086, best demonstrated by the following instructions:

mov     al, [bx]  //将DS:bx地址中的字节拷贝到al中
mov     al, [bp]  //将SS:bp地址中的字节拷贝到al中
mov     al, [si]  //将DS:si地址中的字节拷贝到al中
mov     al, [di]  //将DS:di地址中的字节拷贝到al中

As with the x86 [bx] addressing mode, these four addressing modes reference the byte at the offset found in the bx, bp, si, or di register, respectively. The [bx], [si], and [di] modes use the ds segment by default. The [bp] addressing mode uses the stack segment (ss) by default.

You can use the segment override prefix symbols if you wish to access data in different segments. The following instructions demonstrate the use of these overrides:

mov     al, cs:[bx]
mov     al, ds:[bp]
mov     al, ss:[si]
mov     al, es:[di]

Intel refers to [bx] and [bp] as base addressing modes and bx and bp as base registers (in fact, bp stands for base pointer). Intel refers to the [si] and [di] addressing modes as indexed addressing modes (si stands for source index, di stands for destination index). However, these addressing modes are functionally equivalent. This text will call these forms register indirect modes to be consistent.

? 更多寻址模式请参考Art of Assembly: Chapter Four- The 80x86 Addressing Modes

? 很显然,没有操作系统对内存进行管理,硬件段机制主要是用来扩展内存,每一条访存指令执行之前不会检查内存访问是否合理,也就不存在保护机制,段的共享也无法实现。

1.3 实模式下硬件怎么计算地址的呢?

1.3.1 汇编代码

1.在编写汇编代码时,将程序组织成不同的段。每一个段内的数据的地址都是相对于段的可重定位的相对偏移地址。从而一个程序的地址空间是一个二维的(x, y),一个维度表示段(0~x),另一个维度表示段内偏移(0~y)。

? 在x86硬件段机制下,使用段寄存器进行的地址引用(eg. mov al, [bx])可以用< segment, offset >的二元对来表示,其中segment表示段,offset表示段内偏移。在实模式时(real mode),segment就是段寄存器的值,所以< segment:offset >就是物理地址。

? segment为16bit,offset是16bit,通过地址运算segment:offset得到一个20位的地址< segment:offset >,这个地址运算的计算按下图方式进行:

? 可以看到,段基址是16(4位)的倍数,一共可以取216个不同的值,段内偏移可以取216个不同的值。很明显,段之间有重叠。

? 因为存在重叠,所以对于同一个物理内存地址值,可由多个不同的< segment, offset >二元对得到。故< segment, offset >到物理内存地址(address)的映射是一个多对一的映射。

1.3.2 A C/C++ compiler for DOS in those days

1.进程

感谢操作系统的出现,尤其感谢进程这一非常重要的概念以及相关的一些技术的出现,程序员可以不用再基于RAM编程了,不用去绞尽脑汁考虑怎么把程序布局到物理内存才能使之正常运行。

进程的重要性再怎么强调也不为过,如果说操作系统是应用软件与计算机硬件的接口,那么进程就是操作系统提供给编程人员的物理硬件的虚拟视图。编程人员不再过分考虑硬件,转而考虑进程视图。每一个进程都给编程人员提供了一个单用户单任务的虚拟计算机视图,在进程视图里永远只有一个程序在跑着。对基于进程编程的用户而言,他们只需认为自己的程序是唯一一个运行着的任务,不用考虑自己的任务是否会被其他任务覆盖之类的问题,从而只需重点专注于如何编写一个单用户单任务的程序。进程的内存视图:

2.DOS and Turboc compiler

1.DOS是一个基于x86计算机的单用户单进程个人PC操作系统,运行在实模式下,没有进入保护模式。

基于DOS操作系统的C编译器:

3.far pointer in C:扩大程序可访问地址范围

1.far类型的指针在早期DOS和OS/2个人操作系统流行的时候被用来访问扩展区(或者其他段空间)的内存地址。

2.In a segmented architecture computer, a far pointer is a pointer which includes a segment selector, making it possible to point to addresses outside of the default segment. — Far pointer

3.We programmers’ view of a c program address space(which is arranged by a C compiler) like this:

Understanding C by learning assembly

4.On C compilers targeting the 8086 processor family, far pointers were declared using a non-standard far qualifier.

Size of far pointer is 4 byte.

First 16 bit stores: Segment selector

Next 16 bit stores: Offset address( or effective address)

? With the far pointer’s help, we can tell the C compiler to use two registers to calculate the address, one for segment selector(16 bit) and one for offset address(16 bit) to this segment.

For more information about far pointer see Load Far Pointer and the references below. There I find a forum which has the topic near, far and huge pointers.

Example of far pointer(targeting the 8086 processor):

//What is segment number and offset address?

#include<stdio.h>

int main(){

int x=100;
int far *ptr;

ptr=&x;
printf("%Fp",ptr);

return 0;
}

Output: 8FD8:FFF4 (Assume)

Here 8FD8 is segment address and FFF4 is offset address in hexadecimal number format.

Note: %Fp is used for print offset and segment address of pointer in printf function in hexadecimal number format.

In the header file dos.h there are three macro functions to get the offset address and segment address from far pointer and vice versa.

  1. FP_OFF(): To get offset address from far address.
  2. FP_SEG(): To get segment address from far address.
  3. MK_FP(): To make far address from segment and offset address.
//What will be output of following c program?

#include <dos.h>

#include<stdio.h>

int main(){

int i=25;
int far*ptr=&i;
unsigned int s,o;

s=FP_SEG(ptr);
o=FP_OFF(ptr);
printf("%Fp",MK_FP(s,o));

return 0;
}

Output: 8FD9:FFF4 (Assume)

Note: We cannot guess what will be offset address, segment address and far address of any far pointer. These address are decided by the compiler. also note that the output(address of i) of printf function is an address from the process’s view, it’s not the actual physic memory of the RAM(because we are programming in a view provided by the process(not RAM)).The actual address of the contents in a process is arranged by the memory management system of OS(different OS may have different memory management system that use different mapping strategy).

far-pointer-in-c-programming

1.4 保护模式下的段机制

? x86后续系列的cpu引入了保护模式,为操作系统实现分段和分页的虚拟内存管理机制提供了硬件支持。Protected mode

? 高级语言如C、C++等编写的程序在生成的过程中会编译成汇编语言,为了更好的理解系统,我们不妨假设自己就是一个汇编程序员,然后接着往下思考操作系统的段机制内存管理策略

1.4.1 x86系列cpu中无法避开的硬件段机制

? 参考x86的寻址模式,你会发现所有寻址模式都使用了段寄存器,要么寻址指令显式地指定一个段寄存器,要么硬件根据寻址指令的形式使用默认的段寄存器,总之,x86的硬件段机制渗入到了它的指令集中,无法避开。

When operating in protected mode, some form of segmentation must be used. There is no mode bit to disable segmentation. The use of paging, however, is optional. — 64-IA-32-architectures-software-developer-system-programming-manual-chapter-3

1.4.2 从逻辑上取消段机制

在(基于80x86的)Window和Linux平台上编过程序的人知道,我们写的程序的虚拟地址空间(或者说进程地址空间)完全没有用到分段,也可以看作所有的内容(数据、栈、代码…)都在一个段中。把程序虚拟内存的这种布局模式叫做平坦内存模式,要绕过x86的硬件段机制,只需将相关的段选择器指向的段的基地址设为0,段的长度设为4G。

Memory layout of a process in linux:

参考Anatomy of a Program in Memory

1.4.2 使用段机制管理进程内存

虽然主流的PC操作系统如Linux,Window等都没有用到段机制,但是,x86系列CPU为实现分段内存管理的操作系统提供了硬件支持,不过要求该类型操作运行在保护模式下。

Wiki-x86 memory segmentation

2. 操作系统分段机制

multics-vm-slides

multics-vm

—multics 操作系统的内存管理使用了分段分页机制。

OS/2 1.x Programming

—OS/2 was the only operating system which made full use of segmentation features

Segmentation and the Design of Multiprogrammed Computer Systems

2.1 分段机制

分段,就是将一个程序的进程视图按逻辑方式组织成不同的段(segment),每一个段是一个独立的对象,也可以看作独立的视图,所以,利用分段机制进行编程的程序员可以将编程任务划分为不同的段,然后分给不同的人去编写(或许在编译器等软件工具的帮助下)不同的段,也可以在程序运行时创建新的段来扩展内存,例如用C在早期的OS/2系统下编程时可以:

SEL selArray ;
PCH pchArray ;
USHORT i ;
DosAllocSeg (512 , &selArray , 0) ;//创建一个512字节的段,并将段选择子赋值给selArray。
pchArray = MAKEP (selArray , 0) ; //返回一个far指针,指向selArray段的段首。
for (i = 0 ; i < 512 ; i + + )
pchArray[i] = 0 ;//通过far指针跨段访问新建段的内存空间。

虽然实现分段内存管理是操作系统(在硬件的支持下)的事,但是要想利用分段内存管理提高编程的工作效率以及分段内存管理所具有的其他优点(段共享,扩展内存…)还得程序员对程序编程任务进行合理地分段。

? 如果不考虑具体的实现,从原理上说,进程的逻辑地址是一个< segment_number, offset >的二元组,其中,segment_number给出了段号,用来选择进程的某个段进而得到与这个段相关的全部信息,offset给出了段内偏移量,逻辑地址会被转换成线性地址,若是没有开启分页机制,这个线性地址就作为物理地址,被送上地址总线。

`

划分段之后,程序员可以在自己负责的段的段视图下进行编程,不用过分考虑进程视图,更不用考虑RAM物理内存视图。

问题来了:

既然进程的逻辑地址是一个< segment_number, offset>的二元组,那么在进程执行过程中进行访存时,怎么确定内存的物理地址呢?

站在汇编程序员的角度来看这个问题,在执行一条寻址的汇编指令时,操作系统必须能利用硬件的特性将这条寻址指令转化为确切的线性地址,进而输出到地址总线上。下面就这个问题给出一种解决方法。

2.2 虚拟地址分段

将整个进程地址空间看作连续的一个地址空间(虚拟地址空间),然后利用虚拟地址的前几位将虚拟地址空间划分为不同的段,这种实现方式将虚拟地址分为两部分,一部分是段号位,一部分是段内偏移位。

将一个程序的进程视图划分为不同段之后,负责一个段的编程人员得到自己负责的段的段号,在对段内进程编程之前先设置段号,之后的编程就可以不用考虑段号了。例如先用指令将相关的段选择器设置为自己对应的段号,后面的所有访存指令都会在硬件的支持下自动相对于这个段选择器选择的段进行偏移。或者把段号、共享段等设置交给编译工具去完成,程序员只需在自己的段视图下进行编程。至于操作系统会将段选择器标定的段映射到物理内存的什么位置则完全是操作系统的事,与段编程人员无关。

下面用一个例子说明用直接方式实现的段机制的内存转换过程:

这是一个程序的逻辑地址空间:

这个进程的大小为16KB,有3个段,分别是代码段、堆段、栈段。

这是进程在物理内存中的布局:

在段机制下,操作系统以对用户透明的方式,将上图的进程加载到了物理内存中。

这是操作系统维持的段描述表

一个进程有一个本地的段表,在进程被创建的时候创建,由操作系统维护,表项是一个段描述符,记录了与进程的某个段相关的全部信息。操作系统在将上面的程序映射到主存时为执行该程序的进程维护着它的本地描述符表,这里给出了表中每个表项的4个主要的信息,它们分别是段基址、段大小、段偏移方向和保护信息。可以用一个寄存器指向当前进程的本地段表,发生进程切换的时候,更正这个寄存器的值。

下面就基于上面的图示进行实例说明:

  1. 假设一个指令引用了进程的逻辑地址100,当这个引用指令被执行时,硬件就会自动解析逻辑地址值100(00 0000 0110 0100),得到segment_number = 00 = 0,offset = 000001100100 = 100,首先,硬件拿offset与Size比较,检查offset是否越界,100<2K,没有越界。所得得到最终的物理地址不是100,而是Base of setment0 + offset = 32K + 100 = 32868。
  2. 假设一个指令引用了进程的逻辑地址4200,当这个引用指令被执行时,硬件就会自动解析逻辑地址值4200(01 0000 0110 1000),得到segment_number = 01 = 1,offset = 000001101000 = 104,首先,硬件拿offset与Size比较,检查offset是否越界,104<2K,没有越界。所得得到最终的物理地址不是4200,而是Base of setment1 + offset = 34K + 104 = 34920。
  3. 假设一个指令引用了进程的逻辑地址7KB(01 1100 0000 0000)时,得到segment_number = 01 = 1,offset = 110000000000 = 3KB,首先,硬件拿offset与Size比较,检查offset是否越界,3KB > 2KB,硬件检测到对堆段的访问越界。
  4. 假设一个指令引用了进程的逻辑地址15KB,当这个引用指令被执行时,硬件就会自动解析逻辑地址值15KB(11 1100 0000 0000),得到segment_number = 11 = 3,发现Grows Positive字段为0,表示这个段由高地址向低地址方向作偏移,所以offset = 1100 0000 0000 - 4KB = - 1KB,首先,硬件拿offset与Size比较,检查offset是否越界,1K<2K,没有越界。所得得到最终的物理地址不是15KB,而是Base of setment2 + offset = 28K + -1K = 27K。

这样看来,段机制就和页机制十分相似,它们之间最大的区别就是:

  1. 一个段的空间和一个页的空间在物理内存中的映射都是连续分配的,它们分别是两种机制下,进程在物理内存中进行连续内存分配的单位,不同的是,段的大小通常要比页的大小大很多,而且段的大小不固定,可以小到1B也可以大到offset所能表示的最大值
  2. 页的大小由系统决定,是固定的,段的大小由用户决定,不固定
  3. 段是逻辑上相关的数据的集合。例如代码段、数据段、堆栈段。页则只是进程虚拟内存空间中位于同一个虚拟页内的数据的简单集合,所以对段的保护比对页的保护更具有意义
  4. 段可以更好的实现共享

分段机制主要功能:

  • 运行一个程序(可能比主存大)而不用考虑主存的实际大小,但必须保证主存能够容纳下这个进程最大的一个段。
  • 权限控制。将每个段设置权限位,对程序的访问进行控制。
  • 共享段。
  • 通过段换入换出的方式,程序不必全部放入内存。


references

Wiki-Memory segmentation

Segmented Memory management (operating systems)

morgan_david/cs40/segmentation

The Curse of Segments

Segmentation

How are the segment registers (fs, gs, cs, ss, ds, es) used in Linux?

Art of Assembly: Chapter Four- The 80x86 Addressing Modes

Virtual memory organization

Memory layout and access

Removing the Mystery from SEGMENT : OFFSET Addressing

What is a far pointer

far pointer in c with examples

Load Far Pointer

Load Effective Address

SSCE-Intel-Memory

COMPILER, ASSEMBLER, LINKER AND LOADER:A BRIEF STORY

时间: 2024-10-29 19:08:07

进程—内存段机制的相关文章

Linux内存管理机制

一.首先大概了解一下计算机CPU.Cache.内存.硬盘之间的关系及区别. 1.  CPU也称为中央处理器(CPU,Central Processing Unit)是一块超大规模的集成电路, 是一台计算机的运算核心(Core)和控制核心( Control Unit).它的功能主要是解释计算机指令以及处理计算机软件中的数据.中央处理器主要由三核心部件组成,运算器.控制器和总线(BUS),运算器又主要由算术逻辑单元(ALU)和寄存器(RS)组成. 2.Cache即高速缓冲存储器,是位于CPU与主内存

Linux的内存管理机制

内存管理的一些基本概念: 地址 1)逻辑地址:指由程序产生的与段相关的偏移地址部分.在C语言指针中,读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址. 2)线性地址:段中的偏移地址(逻辑地址),加上相应段的基地址就生成了一个线性地址. 3)物理地址:放在寻址总线上的地址. 4)虚拟地址:保护模式下段和段内偏移量组成的地址,而逻辑地址就是代码段内偏移量,或称进程的逻辑地址. 内存管理主要解决以下问题: 进程的地址空间隔离: 提高内存的使用效率: 程序运行时重

浅谈Linux内存管理机制

经常遇到一些刚接触Linux的新手会问内存占用怎么那么多? 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这方面,区别于Windows的内存管理.主要特点是,无论物理内存有多大,Linux 都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能.而Windows是只在需要内存时,才为应用程序分配内存,并不能充分利用大容量的内存空间.换句话说,每增加一

Java虚拟机内存管理机制

自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动和结束而建立和销毁.根据<Java虚拟机规范 第2版>规定,运行时数据区包括: 1.程序计数器 一块较小的内存空间,不在Ram上,而是直接划分在CPU上的,程序员无法直接操作它.当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令.每条

Android内存管理机制及优化

1.基于Linux内存管理 Android系统是基于Linux 2.6内核开发的开源操作系统,而linux系统的内存管理有其独特的动态存储管理机制.不过Android系统对Linux的内存管理机制进行了优化,Linux系统会在进程活动停止后就结束该进程,而Android把这些进程都保留在内存中,直到系统需要更多内存为止.这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度. 2.Android内存分配机制 与java的垃圾回收机制类似,系

浅谈Linux的内存管理机制

转至:http://ixdba.blog.51cto.com/2895551/541355 一 物理内存和虚拟内存          我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念.物理内存就是系统硬件提供的内存大小,是真正的内存,相对于物理内存,在linux下还有一个虚拟内存的概念,虚拟内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的

windows内存详解(一) 全面介绍Windows内存管理机制及C++内存分配实例

十分感谢MS社区的帖子,讲得很好~ http://social.technet.microsoft.com/Forums/zh-CN/2219/thread/afc1269f-fe08-4dc7-bb94-c395d607e536 (一):进程空间 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本文目的: 对Windows内存管理机制了解清楚,有效的利用C+

转:浅谈Linux的内存管理机制

一 物理内存和虚拟内存          我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念. 物理内存就是系统硬件提供的内存大小,是真正的内存,相对于物理内存,在linux下还有一个虚拟内存的概念,虚拟内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间(Swap Space).          作为物理内存的扩展,l

Linux基础篇之内存管理机制

转载这篇文章之前,先区分下缓存和缓冲的区别, 缓冲和缓存都是RAM中的数据.简单来说,buffer是即将要写入磁盘的数据,而cache是从磁盘中读取数据的.buffer是由各种进程分配的,被用在如输入队列等方面,一个简单的例子如某个进程要求有多个字段读入,在所有字段 被读入完整之前,进程把先前读入的字段放在buffer中保存.cache经常被用在磁盘的I/O请求上,如果有多个进程都要访问某个文件,于是该文件便被做成cache以方便下次被访问,这样可以提高系统性能. 1 Linux内存管理的主要特