下面这条指令访问代码段中的数据,cs存放的是代码段的段选择子,如果ebx的值超出了此代码段描述符中指定的段界限,则这条指令执行时会引发异常
mov ax,cs:[ebx]
下面这条指令向代码段写入数据,cs存放的是代码段的段选择子,段描述符有字段指示是否可以读、写、执行操作,代码段通常是不可写的,所以这条指令执行时会引发异常
mov word ptr cs:[ebx],idata
操作系统中,用户空间和内核空间通常各自有各自的代码段、数据段、栈段。用户空间可通过执行特别的指令从用户空间转到内核空间,转换后,所有的段选择器中的值会指向内核空间相应的段描述符。
用户程序在编写时,使用的逻辑地址是从0开始增加的,用户程序被操作系统加载到线性地址空间的过程是一个地址重定位的过程。
在x86实模式下,加载器会将用户程序所用的段的段地址重定位后回填到用户程序预先安排的段地址存储空间中,这个过程具体为:用户程序被加载到物理地址空间后,加载器将用户空间的逻辑段基址与用户程序被加载到物理地址空间的基地址相加,形成新的段基址,将此段基址转换为段地址后,覆写到用户程序预先安排的段地址存储空间中,之后用户程序在访问自己的段时,首先从段地址存储空间中取得段地址,然后与段内偏移组合构成物理地址。在保护模式下,用户程序不必预留用于存储段地址的空间,操作系统在加载用户程序前会为用户程序创建各种段描述符,并将相应的段选择子设置到各段寄存器中,用户程序只需要告诉加载器用户程序的执行入口地址。
比如保护模式下一用户可执行程序文件结构部分如下:
dd start;编译后start变为字符串"Hello World"的长度,即11
;这个32位数字通常是由链接器加到可执行文件中的,加载器首选读取可执行文件开始的4字节,得到用
;户程序的执行入口地址11,于是设置eip=11,应用程序不需要考虑段基址,段基址由加载器(属操作
;系统)预先设置到段描述符中
;以下是用户源程序文件结构
label db "Hello World"
;应用程序入口
start:
mov ah,cs:[label];编译后label变为0
Linux更喜欢用分页,用户空间和内核空间各自有各自的代码段、数据段、栈段,各个用户进程共用段选择子(段描述符也就一样了),这样不同用户进程的代码段、数据段、栈段具有相同的线性基地址,当不同进程的偏移地址相同时它们的线性地址就发生了重合,Linux为每个用户进程创建了页表,通过页表将不同进程间相同的线性地址映射到不同的物理内存,从而实现了进程间隔离,分页还可以解决物理内存不足的问题,实现虚拟内存。