C++面试题汇总 操作系统

C++面试题汇总 操作系统

进程与线程的概念,以及为什么要有进程线程,其中有什么区别,他们各自又是怎么同步的

  • 基本概念:

    • 进程
      • 是对运行时程序的封装,
      • 是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;
    • 线程
      • 是进程的子任务,是CPU调度分派的基本单位,用于保证程序的实时性,实现进程内部的并发;
      • 线程是操作系统可识别的最小执行和调度单位
      • 每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态(栈也是独立的)。
      • 每个线程完成不同的任务,但是共享
        • 同一地址空间(也就是同样的动态内存(堆区),映射文件(映射区),目标代码等等)
        • 打开的文件队列和其他内核资源。(例如 Socket, 文件句柄什么的)
  • 区别:

    1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
    2. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
      • 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
      • 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。
      • 但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
    3. 进程资源分配的最小单位,线程CPU调度的最小单位;
    4. 系统开销:进程切换的开销也远大于线程切换的开销。
      • 由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/O设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。
      • 在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。
      • 线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。
    5. 通信:
      • 同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
      • 进程间通信IPC主要包括: 管道, 系统IPC(包括消息队列、信号量、信号、共享内存等) 以及套接字socket
    • 进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。
    • 进程间不会相互影响 ;线程一个线程挂掉将导致整个进程挂掉
    • 进程适应于多核、多机分布;线程适用于多核

进程间通信的方式:

  • 进程间通信主要包括管道、系统IPC(包括消息队列信号量信号共享内存等)、以及套接字socket

    • 管道:管道主要包括无名管道命名管道, 管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信
      • 普通管道PIPE
        1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端
        2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)
        3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的readwrite等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
      • 命名管道FIFO
        1. FIFO可以在无关的进程之间交换数据
        2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
    • 系统IPC
      • 消息队列
        • 消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。 (消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点)具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;
      • 特点:
        1. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
        2. 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
        3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
    • 信号量semaphore
      • 信号量(semaphore)与已经介绍过的IPC结构不同,它是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
      • 特点:
        1. 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
        2. 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
        3. 每次对信号量的 PV 操作不仅限于对信号量值加 1减 1,而且可以加减任意正整数。
        4. 支持信号量组。
    • 信号signal: 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
    • 共享内存(Shared Memory):它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等
      • 特点:
        1. 共享内存是最快的一种IPC,因为进程是直接对内存进行存取
        2. 因为多个进程可以同时操作,所以需要进行同步
        3. 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问
    • 套接字SOCKET
      • socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。
  • 线程间通信的方式:

    • 临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;
    • 互斥量Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
    • 信号量Semphare:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
    • 事件(信号) Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作

Linux虚拟地址空间

  • 虚拟内存技术
    • 目的是为了解决进程地址空间隔离的问题, 采用了虚拟内存。
    • 虚拟内存技术为每个进程提供一个大的, 一致的和私有的地址空间, 就好像独占整个内存空间一样.
    • 所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。
    • 事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。
    • 还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。
    • 请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。
    • 扩大地址空间; (最简单的例子不就是64位的系统么, 逻辑地址空间大的逆天 !!)

  • 虚拟内存的好处:
    • 扩大地址空间; (最简单的例子不就是64位的系统么, 逻辑地址空间大的逆天, 物理地址远小于逻辑地址 !!)
    • 扩大了可寻址空间 (例如32位逻辑地址寻址36位的物理地址)
    • 内存保护:每个进程运行在各自的虚拟内存地址空间,互相不能干扰对方。虚存还对特定的内存地址提供写保护,可以防止代码或数据被恶意篡改。
    • 公平内存分配。采用了虚存之后,每个进程都相当于有同样大小的虚存空间。
    • (内存共享)当进程通信时,可采用虚存共享的方式实现。
      • 当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存
    • (虚拟储存技术)虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。在内存中可以保留多个进程,系统并发度提高
      • 在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用内存碎片


  • 虚拟内存的代价:
    • 虚存的管理需要建立很多数据结构,这些数据结构要占用额外的内存
    • 虚拟地址到物理地址的转换,增加了指令的执行时间。
    • 页面的换入换出需要磁盘I/O,这是很耗时的
    • 如果一页中只有一部分数据,会浪费内存。

操作系统中的程序的内存结构

操作系统中的程序的内存结构

  • 任何程序程序本质上都是由BSS段data段text段三个组成的。

    • BSS段(未初始化数据区):通常用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态分配,程序结束后静态变量资源由系统自动释放。
    • 数据(data)段:存放程序中已初始化的全局变量, 静态变量以及常量数据的一块内存区域。数据段也属于静态内存分配。
    • 代码(text)段:存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量(例如字节型字面值常量)
  • 可执行程序在存储(没有调入内存)时分为代码段数据区未初始化数据区三部分。

    • text段data段在编译时已经分配了空间,而BSS段并不占用可执行文件的大小(因为没有初值),它是由链接器来获取内存的。bss段(未进行初始化的数据)的内容并不存放在磁盘上的程序文件中。其原因是内核在程序开始运行前将它们设置为0。需要存放在程序文件中的只有正文段和初始化数据段。
    • data段(已经初始化的数据)则为数据分配空间,数据保存到目标文件中。数据段包含经过初始化的全局变量以及它们的值。
    • BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段的后面。当这个内存进入程序的地址空间后全部清零。
    • 包含数据段BSS段的整个区段此时通常称为数据区(静态区)
  • 可执行程序在运行时又多出两个区域:栈区堆区

    • 栈区:
      • 由编译器自动释放,存放函数的参数值、局部变量等。
      • 每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。
      • 然后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。
      • 每调用一个函数一个新的栈就会被使用。
      • 栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量(1M)是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。
    • 堆区:
      • 用于动态分配内存,位于BSS中间的地址区域。由程序员申请分配和释放。
      • 堆是从低地址位向高地址位增长,采用链式存储结构。
      • 频繁的malloc/free造成内存空间的不连续,产生碎片。
      • 申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。

操作系统中的缺页中断

  • malloc()mmap()等内存分配函数,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存.
  • 当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常。
  • 缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存是,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。
    • 缺页本身是一种中断,与一般的中断一样,需要经过4个处理步骤:
    1. 保护 CPU 现场
    2. 分析中断原因
    3. 转入缺页中断处理程序进行处理
    4. 恢复 CPU 现场,继续执行

    • 中断的处理右两个处理步骤: 中断响应和中断处理
    • 中断响应由硬件实现主要高阔: 识别中断源, 保护现场, 引入中断处理函数入口地址(检查中断向量表)
    • 中断处理就是执行中断处理函数

    • 但是缺页中断是由于所要访问的页面不存在于内存时,由硬件所产生的一种特殊的中断,因此,与一般的中断存在区别:
    1. 在指令执行期间产生和处理缺页中断信号
    2. 一条指令在执行期间,可能产生多次缺页中断
    3. 缺页中断返回是,执行产生中断的一条指令,而一般的中断返回是,执行下一条指令。

forkvfork的区别

  • fork:创建一个和当前进程映像一样的进程可以通过fork( )系统调用:
    #include <sys/types.h>
    #include <unistd.h>
    pid_t fork(void);
    
    • 成功调用fork( )会创建一个新的进程,它几乎与调用fork( )的进程一模一样,这两个进程都会继续运行。
      • 在子进程中,成功的fork( )调用会返回0
      • 在父进程中fork( )返回子进程的pid
      • 如果出现错误,fork( )返回一个负值。
    • 最常见的fork( )用法是创建一个新的进程,然后使用exec( )载入二进制映像,替换当前进程的映像。
      • 这种情况下,fork派生了新的进程,而这个子进程会执行一个新的二进制可执行文件的映像。这种“派生加执行”的方式是很常见的。

  • 早期的Unix系统中,创建进程比较原始。当调用fork时,内核会把所有的内部数据结构复制一份,复制进程的页表项,然后把父进程的地址空间中的内容逐页的复制到子进程的地址空间中。但从内核角度来说,逐页的复制方式是十分耗时的。
  • 现代的Unix系统采取了更多的优化,例如Linux,采用了写时复制的方法,而不是对父进程空间进程整体复制。

  • vfork:在实现写时复制之前,Unix的设计者们就一直很关注在fork后立刻执行exec所造成的地址空间的浪费。BSD的开发者们在3.0BSD系统中引入了vfork( )系统调用。
    #include <sys/types.h>
    #include <unistd.h>
    pid_t vfork(void);
    
    • 除了子进程必须要立刻执行一次对exec的系统调用,或者调用_exit( )退出,对vfork( )的成功调用所产生的结果和fork( )是一样的。
    • vfork( )挂起父进程直到子进程终止或者运行了一个新的可执行文件的映像。
      • 通过这样的方式,vfork( )避免了地址空间的按页复制。
      • 在这个过程中,父进程和子进程共享相同的地址空间和页表项。
      • 实际上vfork( )只完成了一件事:复制内部的内核数据结构。因此,子进程也就不能修改地址空间中的任何内存。
    • vfork( )是一个历史遗留产物,Linux本不应该实现它。需要注意的是,即使增加了写时复制,vfork( )也要比fork( )快,因为它没有进行页表项的复制。然而,写时复制的出现减少了对于替换fork( )争论。
    • 实际上,直到2.2.0内核,vfork( )只是一个封装过的fork( )。因为对vfork( )的需求要小于fork( ),所以vfork( )的这种实现方式是可行的。

  • 补充知识点:写时复制
    • Linux采用了写时复制的方法,以减少fork时对父进程空间进程整体复制带来的开销。
    • 写时复制是一种采取了惰性优化方法来避免复制时的系统开销。
    • 它的前提很简单:
      • 如果有多个进程要读取它们自己的那部门资源的副本,那么复制是不必要的。
      • 每个进程只要保存一个指向这个资源的指针就可以了。
      • 只要没有进程要去修改自己的“副本”,就存在着这样的幻觉:每个进程好像独占那个资源。
      • 从而就避免了复制带来的负担。如果一个进程要修改自己的那份资源“副本”,那么就会复制那份资源,并把复制的那份提供给进程。
      • 不过其中的复制对进程来说是透明的。这个进程就可以修改复制后的资源了,同时其他的进程仍然共享那份没有修改过的资源。
      • 所以这就是名称的由来:在写入时进行复制。
    • 写时复制的主要好处在于:如果进程从来就不需要修改资源,则不需要进行复制。惰性算法的好处就在于它们尽量推迟代价高昂的操作,直到必要的时刻才会去执行。
    • 在使用虚拟内存的情况下,写时复制(Copy-On-Write)是以页为基础进行的。所以,只要进程不修改它全部的地址空间,那么就不必复制整个地址空间。在fork( )调用结束后,父进程和子进程都相信它们有一个自己的地址空间,但实际上它们共享父进程的原始页,接下来这些页又可以被其他的父进程或子进程共享。
    • 写时复制在内核中的实现非常简单。与内核页相关的数据结构可以被标记为只读和写时复制。如果有进程试图修改一个页,就会产生一个缺页中断。内核处理缺页中断的方式就是对该页进行一次透明复制。这时会清除页面的COW属性,表示着它不再被共享。
    • 现代的计算机系统结构中都在内存管理单元(MMU)提供了硬件级别的写时复制支持,所以实现是很容易的。
    • 在调用fork( )时,写时复制是有很大优势的。因为大量的fork之后都会跟着执行exec,那么复制整个父进程地址空间中的内容到子进程的地址空间完全是在浪费时间:如果子进程立刻执行一个新的二进制可执行文件的映像,它先前的地址空间就会被交换出去。写时复制可以对这种情况进行优化。

  • forkvfork的区别:
    1. fork( )的子进程拷贝父进程的数据段和代码段;vfork( )的子进程与父进程共享数据段
    2. fork( )的父子进程的执行次序不确定;vfork( )保证子进程先运行,在调用execexit之前与父进程数据是共享的,在它调用execexit之后父进程才可能被调度运行。
    3. vfork( )保证子进程先运行,在它调用execexit之后父进程才可能被调度运行 。 如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会 导致死锁
    4. 当需要改变共享数据段中变量的值,则拷贝父进程。

如何修改文件最大句柄数?

  • linux默认最大文件句柄数是1024个,在linux服务器文件并发量比较大的情况下,系统会报"too many open files"的错误。故在linux服务器高并发调优时,往往需要预先调优Linux参数,修改Linux最大文件句柄数。

  • 有两种方法:

    • ulimit -n <可以同时打开的文件数>,将当前进程的最大句柄数修改为指定的参数
      • 注:该方法只针对当前进程有效,重新打开一个shell或者重新开启一个进程,参数还是之前的值
        首先用 ulimit -a 查询`Linux`相关的参数,如下所示:
        core file size          (blocks, -c) 0
        data seg size           (kbytes, -d) unlimited
        scheduling priority             (-e) 0
        file size               (blocks, -f) unlimited
        pending signals                 (-i) 94739
        max locked memory       (kbytes, -l) 64
        max memory size         (kbytes, -m) unlimited
        open files                      (-n) 1024
        pipe size            (512 bytes, -p) 8
        POSIX message queues     (bytes, -q) 819200
        real-time priority              (-r) 0
        stack size              (kbytes, -s) 8192
        cpu time               (seconds, -t) unlimited
        max user processes              (-u) 94739
        virtual memory          (kbytes, -v) unlimited
        file locks                      (-x) unlimited
        其中,open files就是最大文件句柄数,默认是1024个。
        修改Linux最大文件句柄数:  ulimit -n 2048, 将最大句柄数修改为 2048个。
        
    • 对所有进程都有效的方法,修改Linux系统参数
      • vi /etc/security/limits.conf 添加
        *  soft  nofile  65536
        *  hard  nofile  65536
        // 将最大句柄数改为 65536
        
      • 修改以后保存,注销当前用户,重新登录,修改后的参数就生效了

并发(concurrency)和并行(parallelism)

  • 并发(concurrency):指宏观上看起来两个程序在同时运行,比如说在单核cpu上的多任务。但是从微观上看两个程序的指令是交织着运行的,你的指令之间穿插着我的指令,我的指令之间穿插着你的,在单个周期内只运行了一个指令。这种并发并不能提高计算机的性能,只能提高效率。
  • 并行(parallelism):指严格物理意义上的同时运行,比如多核cpu,两个程序分别运行在两个核上,两者之间互不影响,单个周期内每个程序都运行了自己的指令,也就是运行了两条指令。这样说来并行的确提高了计算机的效率。所以现在的cpu都是往多核方面发展。

MySQL的端口号是多少,如何修改这个端口号

  • 查看端口号:
    • 使用命令show global variables like 'port';查看端口号
    • mysql的默认端口是3306
    • 补充:
      • sqlserver默认端口号为:1433
      • oracle默认端口号为:1521
      • DB2默认端口号为:5000
      • PostgreSQL默认端口号为:5432
  • 修改端口号:
    • 修改端口号:编辑/etc/my.cnf文件,早期版本有可能是my.conf文件名,增加端口参数,并且设定端口,注意该端口未被使用,保存退出。

操作系统中的页表寻址

  • 页式内存管理,内存分成固定长度的一个个页片。

    • 操作系统为每一个进程维护了一个从虚拟地址到物理地址的映射关系的数据结构,叫页表,页表的内容就是该进程的虚拟地址到物理地址的一个映射。页表中的每一项都记录了这个页的基地址。
    • 通过页表,由逻辑地址的高位部分先找到逻辑地址对应的页基地址,再由页基地址偏移一定长度就得到最后的物理地址,偏移的长度由逻辑地址的低位部分决定。一般情况下,这个过程都可以由硬件完成,所以效率还是比较高的。
    • 页式内存管理的优点就是比较灵活,内存管理以较小的页为单位(内存碎片的最小单位为页),方便内存换入换出和扩充地址空间。
    • 优点:
      • 页面都用固定的长度, 页表简单,调如方便
    • 缺点:
      • 程序不可能正好是页面的整数倍, 所以最后一页的零头将会浪费
      • 页不是逻辑上的独立实体,所以处理, 保护和共享都不级段式虚拟存储
  • Linux最初的两级页表机制:

    • 两级分页机制将32位的虚拟空间分成三段,分别表示页目录表项, 页表项, 内页偏移, 虚拟地址高10位表示页目录表偏移, 中间10位表示页表偏移, 低12位表示页内偏移
    • 低十二位表示页内偏移,高20分成两段分别表示两级页表的偏移。
      • PGD(Page Global Directory): 最高10位,全局页目录表索引
      • PTE(Page Table Entry):中间10位,页表入口索引
      • 其中的计算关系为:
        • 一页的大小为4K, 页表项索引的大小为4bites, 所以一页中可以存放1024(2 10 ^{10} 10)个页表项
          • 对于页目录表 就是一页存放了1024个页表项索引
          • 对于页表就是 一页可以存放 1024页索引
    • 当在进行地址转换时,
      • 首先CR3寄存器中存放的全局页目录表(page directory, PGD)的这一页的物理地址
      • 再加上从虚拟地址中抽出高10位叫做页目录表项(内核也称这为pgd)的部分作为偏移, 即定位到可以描述该地址的pgd;从该pgd中可以获取可以描述该地址的页表的物理地址,
      • 再加上从虚拟地址中抽取中间10位作为偏移, 即定位到可以描述该地址的pte
      • 在这个pte中即可获取该地址对应的页的物理地址, 加上从虚拟地址中抽取的最后12位,即形成该页的页内偏移, 即可最终完成从虚拟地址到物理地址的转换。
      • 从上述过程中,可以看出,对虚拟地址的分级解析过程,实际上就是不断深入页表层次,逐渐定位到最终地址的过程,所以这一过程被叫做page talbe walk
  • Linux的三级页表机制:

    • X86引入物理地址扩展(Pisycal Addrress Extension, PAE)后,可以支持大于4G的物理内存(36位),但虚拟地址依然是32位,原先的页表项不适用,它实际多4 bytes被扩充到8 bytes,这意味着,每一页现在能存放的pte数目从1024变成512了(4k/8)。相应地,页表层级发生了变化,Linux新增加了一个层级,叫做页中间目录(page middle directory, PMD), 变成:
    描述 位数
    r3 指向一个PDPT crs寄存器存储
    GD 指向PDPT中4个项中的一个 位31~30
    MD 指向页目录中512项中的一个 位29~21
    TE 指向页表中512项中的一个 位20~12
    age offset 4KB页中的偏移 位11~0
    • 现在就同时存在2级页表和3级页表,在代码管理上肯定不方便。巧妙的是,Linux采取了一种抽象方法:所有架构全部使用3级页表: 即PGD -> PMD -> PTE
    • 那只使用2级页表(如非PAE的X86)怎么办?
      • 办法是针对使用2级页表的架构,把PMD抽象掉,即虚设一个PMD表项。这样在page table walk过程中,PGD本直接指向PTE的,现在不了,指向一个虚拟的PMD,然后再由PMD指向PTE。这种抽象保持了代码结构的统一。
  • Linux的四级页表机制:

    • 硬件在发展,3级页表很快又捉襟见肘了,原因是64位CPU出现了, 比如X86_64, 它的硬件是实实在在支持4级页表的。它支持48位的虚拟地址空间。如下:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值