作者:李强,华清远见嵌入式学院讲师。

有这么几个问题,在上驱动课程的时候,我感觉一直困扰着同学们:

●    用户程序编译连接形成的地址空间在什么范围内?
        ●    内核编译后地址空间在什么范围内?
        ●    要对外设进行访问,I/O的地址空间又是什么样的?

于是就有了这篇文章,从大概上把内存相关知识点介绍一下,减少同学们在驱动课时对内存的困惑

先回答第一个问题。Linux最常见的可执行文件格式为elf(Executable and Linkable Format)。在elf格式的可执行代码中,ld总是从0x800 0000开始安排程序的“代码段”,对每个程序都是这样。至于程序执行时在物理内存中的实际地址,则由内核为其建立内存映射时临时分配,具体地址取决于当时所分配的物理内存页面。

我们可以用Linux的实用程序obj对你的程序进行反汇编,从而知晓其地址范围。

例如:假定我们有一个简单的C程序Hello.c

# include <stdio.h>
        greeting ( )
        {
                printf(“Hello,world!\n”);
        }
        main()
        {
                greeting();
        }

之所以把这样简单的程序写成两个函数,是为了说明指令的转移过程。我们用gcc和ld对其进行编译和连接,得到可执行代码hello。然后,用Linux的实用程序obj对其进行反汇编:
        $obj –d hello

得到的主要片段为:

08048568 <greeting>:
                8048568:        pushl     %ebp
                8048569:        movl     %esp, %ebp
                804856b:        pushl    $0x809404
                8048570:        call        8048474 <_init+0x84>
                8048575:        addl        $0x4, %esp
                8048578:        leave
                8048579:        ret
                804857a:        movl       %esi, %esi
                0804857c <main>:
                804857c:        pushl    %ebp
                804857d:        movl    %esp, %ebp
                804857f:         call        8048568 <greeting>
                8048584:        leave
                8048585:        ret
                8048586:        nop
                8048587:        nop

其中,像08048568这样的地址,就是我们常说的虚地址(这个地址实实在在的存在,只不过因为物理地址的存在,显得它是“虚”的罢了)。

虚拟内存、内核空间和用户空间(部分内容参考《ULK》V3中文版)

Linux虚拟内存的大小为2^32(在32位的x86机器上),内核将这4G字节的空间分为两部分。最高的1G字节(从虚地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。而较低的3G字节(从虚地址0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间”。因为每个进程可以通过系统调用进入内核,因此,Linux内核空间由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟地址空间(也叫虚拟内存)。

每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的。最高的1GB内核空间则为所有进程以及内核所共享。另外,进程的“用户空间”也叫“地址空间”,在后面的叙述中,我们对这两个术语不再区分。

用户空间不是进程共享的,而是进程隔离的。每个进程最大都可以有3GB的用户空间。一个进程对其中一个地址的访问,与其它进程对于同一地址的访问绝不冲突。比如,一个进程从其用户空间的地址0x1234ABCD处可以读出整数8,而另外一个进程从其用户空间的地址0x1234ABCD处可以读出整数20,这取决于进程自身的逻辑。

任意一个时刻,在一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个4GB的虚拟地址空间,这个虚拟地址空间是面向此进程的。当进程发生切换的时候,虚拟地址空间也随着切换。由此可以看出,每个进程都有自己的虚拟地址空间,只有此进程运行的时候,其虚拟地址空间才被运行它的CPU所知。在其它时刻,其虚拟地址空间对于CPU来说,是不可知的。所以尽管每个进程都可以有4 GB的虚拟地址空间,但在CPU眼中,只有一个虚拟地址空间存在。虚拟地址空间的变化,随着进程切换而变化。

从上面我们知道,一个程序编译连接后形成的地址空间是一个虚拟地址空间,但是程序最终还是要运行在物理内存中。因此,应用程序所给出的任何虚地址最终必须被转化为物理地址,所以,虚拟地址空间必须被映射到物理内存空间中,这个映射关系需要通过硬件体系结构所规定的数据结构来建立。这就是我们所说的段描述符表和页表,Linux主要通过页表来进行映射。

于是,我们得出一个结论,如果给出的页表不同,那么CPU将某一虚拟地址空间中的地址转化成的物理地址就会不同。所以我们为每一个进程都建立其页表,将每个进程的虚拟地址空间根据自己的需要映射到物理地址空间上。既然某一时刻在某一CPU上只能有一个进程在运行,那么当进程发生切换的时候,将页表也更换为相应进程的页表,这就可以实现每个进程都有自己的虚拟地址空间而互不影响。所以,在任意时刻,对于一个CPU来说,只需要有当前进程的页表,就可以实现其虚拟地址到物理地址的转化。

内核空间到物理内存的映射

在驱动中我们提的比较多的就是内核空间与硬件内存地址,那么我们下面来详细介绍下内核空间和实际的硬件物理地址。

内核空间对所有的进程都是共享的,其中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据,不管是内核程序还是用户程序,它们被编译和连接以后,所形成的指令和符号地址都是虚地址,而不是物理内存中的物理地址。

虽然内核空间占据了每个虚拟空间中的最高1GB字节,但映射到物理内存却总是从最低地址(0x00000000)开始的,之所以这么规定,是为了在内核空间与物理内存之间建立简单的线性映射关系。其中,3GB(0xC0000000)就是物理地址与虚拟地址之间的位移量,在Linux代码中就叫做PAGE_OFFSET。

我们来看一下在include/asm/i386/page.h头文件中对内核空间中地址映射的说明及定义:

#define __PAGE_OFFSET                (0xC0000000)
        ……
        #define PAGE_OFFSET                ((unsigned long)__PAGE_OFFSET)
        #define __pa(x)                ((unsigned long)(x)-PAGE_OFFSET)
        #define __va(x)                ((void *)((unsigned long)(x)+PAGE_OFFSET))

对于内核空间而言,给定一个虚地址x,其物理地址为“x- PAGE_OFFSET”,给定一个物理地址x,其虚地址为“x+ PAGE_OFFSET”。

这里再次说明,宏__pa()仅仅把一个内核空间的虚地址映射到物理地址,而决不适用于用户空间,用户空间的地址映射要复杂得多,它通过分页机制完成。

待续……

Linux驱动的地址空间和硬件地址空间说明——摘自华清远见嵌入式园地 .相关推荐

  1. linux内核培训广州,嵌入式Linux驱动开发高级培训班-华清远见嵌入式培训中心

    课程目标 本课程以案例教学为主,系统地介绍Linux下有关FrameBuffer.MMC卡.USB设备的驱动程序开发.参加本课程学习的学员,因为具备了Linux设备驱动开发基础,所以本课程针对性较强, ...

  2. 华清远见嵌入式Linux驱动开发培训班

    课程背景 开放的 Linux 受到广泛的欢迎,得到越来越多公司的支持,但是阻碍 Linux 在各个领域广泛应用的主要因素就是内核/驱动高端人才极度缺乏,Linux源代码中85%是设备驱动,嵌入式系统中 ...

  3. 华清远见嵌入式Linux就业培训班

    ● 课程目标 4 招生简章     通过本课程的系统学习,可以使学员由浅入深地对嵌入式Linux系统全面学习,能够独立胜任嵌入式Linux应用开发.系统开发.驱动开发等多方面工作.课程目标包括:    ...

  4. 基于V4L2的视频驱动开发(2) 华清远见 刘洪涛

    基于V4L2的视频驱动开发(2) 华清远见 刘洪涛 三.            V4L2 API及数据结构 V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范.包括一套数据结构和 ...

  5. linux内核驱动开发 培训,嵌入式Linux驱动开发培训 - 华清远见教育集团官网

    9.LINUX下USB驱动开发基础 9.1 USB规范介绍 9.2 USB主机控制器 9.3 USB HUB 9.4 USB设备状态 9.5 USB描述符 9.6 USB请求 9.7 USB通讯数据格 ...

  6. 嵌入式linux华清远见考试,嵌入式Linux小测及答案

    1.进程有三种状态:___. A 运行态.就绪态和等待态 B 精确态.模糊态和随机态 C 准备态.执行态和退出态 D 手工态.自动态和自由态 2.4个圆盘的Hanoi塔,总的移动次数为() A 7 B ...

  7. 嵌入式linux华清远见考试,华清远见系统移植考试复习题

    嵌入式Linux系统移植试题(时间:60分钟) 一.单项选择题(每题2分,共40分) 1. 嵌入式linux系统移植不包括(D ) [A] bootloader [B] linux内核[C]根文件系统 ...

  8. linux野指针追踪,【华清远见】野指针和空指针的两个小点

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 作者:吕老师,华清远见嵌入式学院讲师. 大家都知道指针的学习对于c语言学习来说可谓是至关重要的,下面我们来说一下在指针中两种比较特殊的关于指针的概念,野指 ...

  9. g华清远见基于linux和Qt,【华清远见】QT编程实例集

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 一.小例子 1.改变字体颜色(两种方法). a.此实现的是调色板的方式实现改变字体颜色. QPalette palette = ui->lineEd ...

  10. linux网络编程 华清,网络编程(华清远见内部培训资料).ppt

    网络编程(华清远见内部培训资料) 地址结构的一般用法 定义一个struct sockaddr_in类型的变量并清空 struct sockaddr_in myaddr; memset(&mya ...

最新文章

  1. 2022-2028年中国中空玻璃聚硫密封胶行业市场研究及前瞻分析报告
  2. 一种新的高级抖动分离解析方法
  3. thinkphp自定义标签库
  4. Python -bs4介绍
  5. PAT乙级 1003. 我要通过!
  6. linux的系统移植——【PC-开发板】的环境搭建
  7. 内蒙古农大孙志宏教授证实超深度混合宏基因组测序能够对人类肠道微生物组中的低丰度物种进行基因组和功能表征...
  8. 漫画:程序员和产品经理撕得真是太太太太厉害了
  9. 罗森伯格荣获2015年度中国数据中心优秀供应商与中国十大布线品牌两项大奖
  10. 拦截器(Interceptor)和过滤器(Filter)的区别
  11. Google Guice依赖注入框架使用
  12. 假如我来架构12306网站(二) - 浅谈系统需求调研
  13. 字节跳动测开发实习面试
  14. Mongodb分片学习
  15. 计算机网络协议简介及英文简写
  16. 服务器×××上的MSDTC不可用
  17. dz每个php模板页文件,DZ模板discuz3.2仿魔客吧模板源码-包含DIY文件-价值450元DZ模板discuz3.2仿魔客吧模板...
  18. Effective STL之算法
  19. USGS今天开始向全球提供Landsat9下载
  20. 格式化输出(占位符%)

热门文章

  1. IOS nonatomic 与 atomic 的区别
  2. 163邮箱苹果设置不成功_iphone手机,苹果手机如何登陆网易163邮箱
  3. Android 手写签名 (图片合成)
  4. 云计算时代的域名解析
  5. lol封号维护服务器,LOL手游:各大服务器突然断开,众主播账号被永久封停
  6. 时间管理——34枚金币
  7. IOS开发之相机、相册页面英文问题
  8. android Activity设置透明主题样式方法
  9. 物联网-移远m26使用MQTT协议,AT指令对接阿里云
  10. 微信小程序打开外部链接