- 浏览: 266237 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
AndMacLinuXp:
试了下,不错!
printk内核调试 -
klose:
我引用你的文章,并做了简单的分析:这里贴出url:http:/ ...
linux系统调用fork, vfork, clone -
klose:
你上面提到的问题:free的问题。首先你可能疏忽了,stack ...
linux系统调用fork, vfork, clone -
qwe_rt:
HI ,非常nice的文章,在阅读过程中,我发现我的ubunt ...
linux手动添加开机启动的服务 -
suifeng:
谢谢分享, 受用中.
shell编程分支,循环
linux内存寻址详细过程:
下面我们看这条语句call 0x0804837c
整个内存寻址分为2个阶段,段式和页式。处理器是intel i386系列.操作系统会先将虚地址经过段式映射后变成线性地址,再通过页式变成物理地址。从而获取内存上的数据或者是代码.
这里执行的是call语句,转移指令。首先会找到cs寄存器,intel i386是32位处理器,CS仍然是16位。高13位存贮的是段描述位置(注意不是地址,读到后面就知道了)。低3位中,倒数第3位是指使用GDT寄存器还是LDT(寄存器)。后2位表示权限。(在linux中只用了2种权限,最高级别的系统权限00和用户权限最低11)。该寄存器内容是在建立进程的时候就被设置好的,其代码可以在include/asm-i386/processor.h中找到:
define start_thread(regs, new_eip, new_esp) do { \
__asm__("movl %0,%%fs ; movl %0,%%gs": :"r" (0)); \
set_fs(USER_DS); \
regs->xds = __USER_DS; \
regs->xes = __USER_DS; \
regs->xss = __USER_DS; \
regs->xcs = __USER_CS; \
regs->eip = new_eip; \
regs->esp = new_esp; \
} while (0)
这里我们可以看到除了CS寄存器,其他的寄存器值都被设置成了__USER_DS,这与intel当初设计有出入。linux并没有买intel的帐。当然我们想知道具体的值,至于__USER_DS和__USER_CS的值我们可以在include/asm-i386/segment.h中找到:
#define __KERNEL_CS 0X10 //相应的2进制表示 0000 0000 0001 0000
#define __KERNEL_DS 0X18 //相应的2进制表示 0000 0000 0001 1000
#define __USER_CS 0X23 //相应的2进制表示 0000 0000 0010 0011
#define __USER_DS 0X2B //相应的2进制表示 0000 0000 0010 1011
注意不同内核版本的值可能不一样,但这并不影响。看到后面你们会发现这些值是有自己的规则的。对应着上面说到的CS,DS等等寄存器的意义(DS和CS16位值意义是一样的),我们回忆可得知,最高13位表示段描述结构的位置,倒数第3位表示用GDT还是LDT,最后表示权限,我们很容易得到下面的值:
段描述结构的位置 是GDT还是LDT(0表示用GDT,1表示LDT) 权限
__KERNEL_CS 2 0用GDT 00最高系统权限
__KERNEL_DS 3 0用GDT 00最高系统权限
__USER_CS 4 0用GDT 11用户权限
__USER_DS 5 0用GDT 11用户权限
这里我们看到linux只用了GDT寄存器,这违背了intel初衷(GDT表示全局段描述表,LDT是局部段描述表)。名字也反映了GDT和LDT的作用,他们是用来存贮段描述结构的地址起点的。看到这里可能有些人不明白,内存寻址不是段地址加上偏移吗,直接把CS的值加上程序中给出的偏移就可以找到了物理地址了吗?那是intel 8086和8088的虚地址模式,在intel 80286确切的说是80386更多的采用的是保护模式寻址,这种寻址方式改变了寄存器内部值的意义,至于什么是保护模式,大家GOOGLE下就知道了。
既然用到了GDT寄存器,那么在装入线程的时候肯定是要给这个寄存器所指向的表赋值了,这段代码是在arch/i386/kernel/head.S中:
ENTRY(cpu_gdt_table) //定义了GDT表值
.quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */
我只贴出了关键的代码,其实在赋值时候,GDT指向的表的第一项没有被使用,原因是防止加电后段寄存器未经初始化就进入保护模式并使用GDT,第2项也没有被使用,那么我们找到我们程序要用的第4项,既是.quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */,这是个8字节64位的数。我们转换成相应的2进制就可以看到它的值是:0000 0000 1100 1111 1111 1010 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111,我们看下它的意义。从低位向高位,从第16位(起始第0位),到第31位是0,从第32位到第39位是0,最高的8位还是0,这个就是我们以前所说的段址,也就是段的基地址(有人会问为什么地址搞这么复杂32位基地址连在一起不好吗,干嘛分成3段。16位16位分想信大家可以理解,至于后面又把16位分成了2个8位,是因为intel设计初衷是用于24位处理器,但很快意识到应该用于32位,于是修修补补就变成了现在这个样子了),在linux中他被设置成了0,也许有人会说这不会出问题吗,后面还有页式内存管理,这里不是最终的物理地址。接下来每个描述段都有自己的意义了,其实在linux中对这些2进制单元的内存设置是为了应付intelCPU基于段式内存管理的保护模式,最低16位加上,倒数第12位到15位都是1,他们合起来16进制值便是0xffffff表示段的上限,一旦用户越过了这个上限就会报非法越界了。我们看到倒数第8位是1表示4KB,0表示段长度单位是1B,1表示段长度单位为4KB,倒数第9为1表示当前指令为32,否则为16位。倒数第16位为1表示在内存,否则则不在内存。后面紧接着为11表示要求的权限,这里为11和段寄存器中相符,表示可以使用,没有越权。剩下的几个数位就不一一介绍了,读者可以自己去查相关的资料。到这里读者可能会想到,我们可以通过设置CS或者DS寄存器从而可以达到非法访问的目的,但linux内核会在页式映射里面继续执行一次检查,之所以在这里做一次是因为例行i386的公事,不得已而为之。大家也会看到由于段寄存器被指定了值,任何一个地址访问到的最后的段描述结构的值都将会是代码中列出的值。linux这样做只是由于i386的设计,必需先完成段式印射。
这样我们得到了段的基地址0,加上偏移就是线性地址了,很明显在linux中线性地址数值是等于虚地址的,因为加的是0.接下来要做的是把线性地址转换成物理地址。这里介绍下i386是如何把线性地址转换成物理地址的。线性地址是32位,I386将其看成10位,10位,12位。在i386中有个寄存器叫CR3。它指向一个地址,内存管理单元(mmu)找到这里,根据最高10位,得到下标,从而获取页面表位置,找到对应的页面表,在从第2个10位作为下标找到页面位置,加上最后12为偏移就是我们要找的物理地址了。CR3的设置是在include/asm-i386/mmu_context.h里面,代码为:
static inline void switch_mm(struct mm_struct *prev,
struct mm_struct *next,
struct task_struct *tsk){
...
asm voliatile("movl %0, %%cr3"::"r" (__pa(next->pgd));
...
}
大家也许没有看到有什么权限判断的地方,权限判断是在找页面表的时候完成的,页面表的大小占一个页面,在32位的i386中,一个页面为4K,那么页面的起始位置一定是4K的倍数,存贮页面表其实地址的表单里面,每个值(表示页面表的起始位置)最后的12必然是0,因此,抛弃这12个零,找到值后直接在后面加上12个0就是页面表的地址,同理,在页面表中低12位也是0,因此可以将这些必定是0的数位用到其它方面,比如权限判断。因此从线性地址到物理地址的过程就是到CR3指向地址,以最高10位为下标找到页面表地址,再在页面表中以第2个10位为下标找到页面位置,加上最后12位的位移便是物理地址了。
也许大家会觉得为了一个内存寻址,会访问内存到3次,效率是不是太低了。但是由于告诉缓存的作用,加上大部分操作是由硬件完成的,所以速度很快。
发表评论
-
linux下挂在windows共享文件夹
2012-05-06 12:53 840smbmount //nas/xxxx /mnt/nas -o ... -
进程组 对话期
2010-04-16 16:12 1343为了便于进程控制,于是就有了进程组,对话期的概念。 进 ... -
感谢你教会我如何提问-《提问的智慧》
2009-01-12 11:25 1410转自:http://linux.chinaunix ... -
linux手动添加开机启动的服务
2009-01-02 14:48 7151如果你只是想知道如何 ... -
让信号量来实现临界区控制
2008-12-09 16:14 1555前面我们谈到过管道,消息队列。我们可以使用他们可以解决了2个进 ... -
进程通行之报文消息队列
2008-12-04 17:39 1365报文和消息队列又是进 ... -
信号和信号处理
2008-11-26 16:09 3305信号同样是用于进程通 ... -
命名管道实现样例
2008-11-18 17:41 1240[root@liumengli name_pipe]# mkn ... -
linux中的管道实现和管道
2008-11-14 17:52 12937inux的shell中有个很常用的东西管道,很多人认为他是sh ... -
文件访问权限和安全
2008-10-28 18:02 1168打开文件,读取内容或 ... -
printk内核调试
2008-10-15 17:07 9375首先阐明一点,我调试的目的是为了学习,看看内核代码是如何运行, ... -
linux内核编译
2008-10-07 17:36 1919这是调试linux内核的必要步骤,虽然很麻烦,而且容易出错(所 ... -
linux文件系统
2008-10-06 11:08 960文件系统是一个比较模糊的名词,文件也是一个比较模糊的名词。狭义 ... -
linux内核书籍下载
2008-09-25 11:47 2116学习内核代码之前,需要有以下一些基础: 1.linux系统管理 ... -
nanosleep函数
2008-09-24 17:36 3329int nanosleep(const struct ti ... -
linux进程调度政策
2008-09-23 11:45 1543进程调度政策就是调度系统种哪一个进程来CPU运行。这种调度分2 ... -
linux进程调度发生的时机
2008-09-19 18:03 4085所谓调度时机就是:什么时候会发生进程调度。在linux中,进程 ... -
SSH远程启动服务的问题
2008-09-18 12:32 4682SSH是我们最长用的远程连接linux服务器的工具了,安全 ... -
时钟中断
2008-09-17 16:57 3339时钟中断:是指在计算 ... -
wait4的实现
2008-09-17 15:38 2937不贴实现的代码了,很 ...
相关推荐
。。。。。。。MySQL-5.6.25-1.linux_glibc2.5.i386.rpm-bundle.tar
Kali Linux VMware(kali-linux-2022.2-vmware-i386.7z)文件分割成 3个 压缩包,必须集齐3个 文件后才能一起解压一起使用: Kali Linux VMware(kali-linux-2022.2-vmware-i386.part1.rar) ... ...
Kali Linux VirtualBox(kali-linux-2022.2-virtualbox-i386.ova)文件分割成 4个 压缩包,必须集齐4个 文件后才能一起解压一起使用: kali-linux-2022.2-virtualbox-i386.part1 ... kali-linux-2022.2-virtualbox-i386...
MySQL5.6.25Linux安装包,服务器版,MySQL-server-5.6.25-1.linux_glibc2.5.i386.rpm,安装教程请参照https://blog.csdn.net/cadn_jueying/article/details/80467966
MySQL-shared-compat-5.6.22-1.linux_glibc2.5.i386.rpm安装包
Linux 2.4.18i386启动过程.pdf
VS Code从1.36版开始不再支持32位系统。 很难从官网上找到32位版本的下载地址。 这个共享一个1.35版本的32位deb安装包,本人亲测可用。 方便给有需要的人们。 关键词: VS Code Ubuntu i386
Kali Linux VirtualBox(kali-linux-2022.2-virtualbox-i386.ova)文件分割成 4个 压缩包,必须集齐4个 文件后才能一起解压一起使用: kali-linux-2022.2-virtualbox-i386.part1 ... kali-linux-2022.2-virtualbox-i386...
Kali Linux VirtualBox(kali-linux-2022.2-virtualbox-i386.ova)文件分割成 4个 压缩包,必须集齐4个 文件后才能一起解压一起使用: kali-linux-2022.2-virtualbox-i386.part1 ... kali-linux-2022.2-virtualbox-i386...
Kali Linux VMware(kali-linux-2022.2-vmware-i386.7z)文件分割成 3个 压缩包,必须集齐3个 文件后才能一起解压一起使用: Kali Linux VMware(kali-linux-2022.2-vmware-i386.part1.rar) ... ...
linuxqq_v1.0.2_i386.tar.gz
i386-linux-gnu
Linux内核源代码启动代码i386文件(部分自己已注释)
MySQL-server-5.5.32-2.linux2.6.i386.rpm 此资源为linux 下mysql 服务器端的安装程序。 客户端的安装程序和安装说明,在下一个上传文件。
MySQL-server-5.6.24-1.linux_glibc2.5.i386.part2.rar
intel-linux-graphics-installer_1.1.0-0intel1_i386.deb
Kali Linux VirtualBox(kali-linux-2022.2-virtualbox-i386.ova)文件分割成 4个 压缩包,必须集齐4个 文件后才能一起解压一起使用: kali-linux-2022.2-virtualbox-i386.part1 ... kali-linux-2022.2-virtualbox-i386...
Linux 2.2.x(i386体系结构)进程管理分析及最大进程数限制的突破.pdf
linux i386 bootsetup process
压缩包里面为:MySQL-client-5.5.48-1.linux2.6.i386.rpm和MySQL-server-5.5.48-1.linux2.6.i386.rpm