下一页 上一页 目录

3. 基础

3.1 什么是内核?

内核是任何计算机系统的“核心”:它是允许用户共享计算机资源的“软件”。

内核可以被认为是操作系统 (OS) 的主要软件,它也可能包括图形管理。

例如,在 Linux (像其他类 Unix 操作系统一样) 下,XWindow 环境不属于 Linux 内核,因为它只管理图形操作 (它使用用户模式 I/O 来访问显卡设备)。

相比之下,Windows 环境 (Win9x, WinME, WinNT, Win2K, WinXP 等等) 是图形环境和内核的混合体。

3.2 用户模式和内核模式之间有什么区别?

概述

许多年前,当计算机像房间一样大时,用户运行他们的应用程序非常困难,有时,他们的应用程序会使计算机崩溃。

操作模式

为了避免应用程序不断崩溃,较新的操作系统被设计为具有 2 种不同的操作模式

  1. 内核模式:机器在关键数据结构、直接硬件 (IN/OUT 或内存映射)、直接内存、IRQ、DMA 等下运行。
  2. 用户模式:用户可以运行应用程序。

                      
               |          Applications           /|\
               |         ______________           |
               |         | User Mode  |           |  
               |         ______________           | 
               |               |                  |  
Implementation |        _______ _______           |   Abstraction
    Detail     |        | Kernel Mode |           |
               |        _______________           |
               |               |                  |
               |               |                  | 
               |               |                  |
              \|/          Hardware               |

内核模式“阻止”用户模式应用程序损坏系统或其功能。

现代微处理器在硬件中至少实现了 2 种不同的状态。例如在 Intel 下,4 种状态决定了 PL (特权级别)。可以使用 0,1,2,3 状态,其中 0 用于内核模式。

Unix 操作系统只需要 2 个特权级别,我们将使用这种范例作为参考点。

3.3 从用户模式切换到内核模式

我们什么时候切换?

一旦我们理解了存在 2 种不同的模式,我们就必须知道我们什么时候从一种模式切换到另一种模式。

通常,有 2 个切换点

  1. 当调用系统调用时:在调用系统调用后,任务会主动调用驻留在内核模式中的代码片段
  2. 当 IRQ (或异常) 发生时:在 IRQ 之后,IRQ 处理程序 (或异常处理程序) 被调用,然后控制权返回到被中断的任务,就像什么都没发生一样。

系统调用

系统调用就像管理驻留在内核模式中的操作系统例程的特殊函数。

当我们

                                 |                |
                         ------->| System Call i  | (Accessing Devices)
|                |       |       |  [sys_read()]  |
| ...            |       |       |                |
| system_call(i) |--------       |                |
|   [read()]     |               |                |
| ...            |               |                |
| system_call(j) |--------       |                |  
|   [get_pid()]  |       |       |                |
| ...            |       ------->| System Call j  | (Accessing kernel data structures)
|                |               |  [sys_getpid()]|
                                 |                | 
 
    USER MODE                        KERNEL MODE
 
  
                        Unix System Calls Working 

系统调用几乎是用户模式与底层资源 (硬件) 通信的唯一接口。此语句的唯一例外是当进程使用 ''ioperm'' 系统调用时。在这种情况下,用户模式进程可以直接访问设备 (不能使用 IRQ)。

注意:并非每个 ''C'' 函数都是系统调用,只有一部分是。

以下是 Linux Kernel 2.4.17 下的系统调用列表,来自 [ arch/i386/kernel/entry.S ]

        .long SYMBOL_NAME(sys_ni_syscall)       /* 0  -  old "setup()" system call*/
        .long SYMBOL_NAME(sys_exit)
        .long SYMBOL_NAME(sys_fork)
        .long SYMBOL_NAME(sys_read)
        .long SYMBOL_NAME(sys_write)
        .long SYMBOL_NAME(sys_open)             /* 5 */
        .long SYMBOL_NAME(sys_close)
        .long SYMBOL_NAME(sys_waitpid)
        .long SYMBOL_NAME(sys_creat)
        .long SYMBOL_NAME(sys_link)
        .long SYMBOL_NAME(sys_unlink)           /* 10 */
        .long SYMBOL_NAME(sys_execve)
        .long SYMBOL_NAME(sys_chdir)
        .long SYMBOL_NAME(sys_time)
        .long SYMBOL_NAME(sys_mknod)
        .long SYMBOL_NAME(sys_chmod)            /* 15 */
        .long SYMBOL_NAME(sys_lchown16)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old break syscall holder */
        .long SYMBOL_NAME(sys_stat)
        .long SYMBOL_NAME(sys_lseek)
        .long SYMBOL_NAME(sys_getpid)           /* 20 */
        .long SYMBOL_NAME(sys_mount)
        .long SYMBOL_NAME(sys_oldumount)
        .long SYMBOL_NAME(sys_setuid16)
        .long SYMBOL_NAME(sys_getuid16)
        .long SYMBOL_NAME(sys_stime)            /* 25 */
        .long SYMBOL_NAME(sys_ptrace)
        .long SYMBOL_NAME(sys_alarm)
        .long SYMBOL_NAME(sys_fstat)
        .long SYMBOL_NAME(sys_pause)
        .long SYMBOL_NAME(sys_utime)            /* 30 */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old stty syscall holder */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old gtty syscall holder */
        .long SYMBOL_NAME(sys_access)
        .long SYMBOL_NAME(sys_nice)
        .long SYMBOL_NAME(sys_ni_syscall)       /* 35 */                /* old ftime syscall holder */
        .long SYMBOL_NAME(sys_sync)
        .long SYMBOL_NAME(sys_kill)
        .long SYMBOL_NAME(sys_rename)
        .long SYMBOL_NAME(sys_mkdir)
        .long SYMBOL_NAME(sys_rmdir)            /* 40 */
        .long SYMBOL_NAME(sys_dup)
        .long SYMBOL_NAME(sys_pipe)
        .long SYMBOL_NAME(sys_times)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old prof syscall holder */
        .long SYMBOL_NAME(sys_brk)              /* 45 */
        .long SYMBOL_NAME(sys_setgid16)
        .long SYMBOL_NAME(sys_getgid16)
        .long SYMBOL_NAME(sys_signal)
        .long SYMBOL_NAME(sys_geteuid16)
        .long SYMBOL_NAME(sys_getegid16)        /* 50 */
        .long SYMBOL_NAME(sys_acct)
        .long SYMBOL_NAME(sys_umount)                                   /* recycled never used phys() */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old lock syscall holder */
        .long SYMBOL_NAME(sys_ioctl)
        .long SYMBOL_NAME(sys_fcntl)            /* 55 */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old mpx syscall holder */
        .long SYMBOL_NAME(sys_setpgid)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old ulimit syscall holder */
        .long SYMBOL_NAME(sys_olduname)
        .long SYMBOL_NAME(sys_umask)            /* 60 */
        .long SYMBOL_NAME(sys_chroot)
        .long SYMBOL_NAME(sys_ustat)
        .long SYMBOL_NAME(sys_dup2)
        .long SYMBOL_NAME(sys_getppid)
        .long SYMBOL_NAME(sys_getpgrp)          /* 65 */
        .long SYMBOL_NAME(sys_setsid)
        .long SYMBOL_NAME(sys_sigaction)
        .long SYMBOL_NAME(sys_sgetmask)
        .long SYMBOL_NAME(sys_ssetmask)
        .long SYMBOL_NAME(sys_setreuid16)       /* 70 */
        .long SYMBOL_NAME(sys_setregid16)
        .long SYMBOL_NAME(sys_sigsuspend)
        .long SYMBOL_NAME(sys_sigpending)
        .long SYMBOL_NAME(sys_sethostname)
        .long SYMBOL_NAME(sys_setrlimit)        /* 75 */
        .long SYMBOL_NAME(sys_old_getrlimit)
        .long SYMBOL_NAME(sys_getrusage)
        .long SYMBOL_NAME(sys_gettimeofday)
        .long SYMBOL_NAME(sys_settimeofday)
        .long SYMBOL_NAME(sys_getgroups16)      /* 80 */
        .long SYMBOL_NAME(sys_setgroups16)
        .long SYMBOL_NAME(old_select)
        .long SYMBOL_NAME(sys_symlink)
        .long SYMBOL_NAME(sys_lstat)
        .long SYMBOL_NAME(sys_readlink)         /* 85 */
        .long SYMBOL_NAME(sys_uselib)
        .long SYMBOL_NAME(sys_swapon)
        .long SYMBOL_NAME(sys_reboot)
        .long SYMBOL_NAME(old_readdir)
        .long SYMBOL_NAME(old_mmap)             /* 90 */
        .long SYMBOL_NAME(sys_munmap)
        .long SYMBOL_NAME(sys_truncate)
        .long SYMBOL_NAME(sys_ftruncate)
        .long SYMBOL_NAME(sys_fchmod)
        .long SYMBOL_NAME(sys_fchown16)         /* 95 */
        .long SYMBOL_NAME(sys_getpriority)
        .long SYMBOL_NAME(sys_setpriority)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old profil syscall holder */
        .long SYMBOL_NAME(sys_statfs)
        .long SYMBOL_NAME(sys_fstatfs)          /* 100 */
        .long SYMBOL_NAME(sys_ioperm)
        .long SYMBOL_NAME(sys_socketcall)
        .long SYMBOL_NAME(sys_syslog)
        .long SYMBOL_NAME(sys_setitimer)
        .long SYMBOL_NAME(sys_getitimer)        /* 105 */
        .long SYMBOL_NAME(sys_newstat)
        .long SYMBOL_NAME(sys_newlstat)
        .long SYMBOL_NAME(sys_newfstat)
        .long SYMBOL_NAME(sys_uname)
        .long SYMBOL_NAME(sys_iopl)             /* 110 */
        .long SYMBOL_NAME(sys_vhangup)
        .long SYMBOL_NAME(sys_ni_syscall)       /* old "idle" system call */
        .long SYMBOL_NAME(sys_vm86old)
        .long SYMBOL_NAME(sys_wait4)
        .long SYMBOL_NAME(sys_swapoff)          /* 115 */
        .long SYMBOL_NAME(sys_sysinfo)
        .long SYMBOL_NAME(sys_ipc)
        .long SYMBOL_NAME(sys_fsync)
        .long SYMBOL_NAME(sys_sigreturn)
        .long SYMBOL_NAME(sys_clone)            /* 120 */
        .long SYMBOL_NAME(sys_setdomainname)
        .long SYMBOL_NAME(sys_newuname)
        .long SYMBOL_NAME(sys_modify_ldt)
        .long SYMBOL_NAME(sys_adjtimex)
        .long SYMBOL_NAME(sys_mprotect)         /* 125 */
        .long SYMBOL_NAME(sys_sigprocmask)
        .long SYMBOL_NAME(sys_create_module)
        .long SYMBOL_NAME(sys_init_module)
        .long SYMBOL_NAME(sys_delete_module)
        .long SYMBOL_NAME(sys_get_kernel_syms)  /* 130 */
        .long SYMBOL_NAME(sys_quotactl)
        .long SYMBOL_NAME(sys_getpgid)
        .long SYMBOL_NAME(sys_fchdir)
        .long SYMBOL_NAME(sys_bdflush)
        .long SYMBOL_NAME(sys_sysfs)            /* 135 */
        .long SYMBOL_NAME(sys_personality)
        .long SYMBOL_NAME(sys_ni_syscall)       /* for afs_syscall */
        .long SYMBOL_NAME(sys_setfsuid16)
        .long SYMBOL_NAME(sys_setfsgid16)
        .long SYMBOL_NAME(sys_llseek)           /* 140 */
        .long SYMBOL_NAME(sys_getdents)
        .long SYMBOL_NAME(sys_select)
        .long SYMBOL_NAME(sys_flock)
        .long SYMBOL_NAME(sys_msync)
        .long SYMBOL_NAME(sys_readv)            /* 145 */
        .long SYMBOL_NAME(sys_writev)
        .long SYMBOL_NAME(sys_getsid)
        .long SYMBOL_NAME(sys_fdatasync)
        .long SYMBOL_NAME(sys_sysctl)
        .long SYMBOL_NAME(sys_mlock)            /* 150 */
        .long SYMBOL_NAME(sys_munlock)
        .long SYMBOL_NAME(sys_mlockall)
        .long SYMBOL_NAME(sys_munlockall)
        .long SYMBOL_NAME(sys_sched_setparam)
        .long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
        .long SYMBOL_NAME(sys_sched_setscheduler)
        .long SYMBOL_NAME(sys_sched_getscheduler)
        .long SYMBOL_NAME(sys_sched_yield)
        .long SYMBOL_NAME(sys_sched_get_priority_max)
        .long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
        .long SYMBOL_NAME(sys_sched_rr_get_interval)
        .long SYMBOL_NAME(sys_nanosleep)
        .long SYMBOL_NAME(sys_mremap)
        .long SYMBOL_NAME(sys_setresuid16)
        .long SYMBOL_NAME(sys_getresuid16)      /* 165 */
        .long SYMBOL_NAME(sys_vm86)
        .long SYMBOL_NAME(sys_query_module)
        .long SYMBOL_NAME(sys_poll)
        .long SYMBOL_NAME(sys_nfsservctl)
        .long SYMBOL_NAME(sys_setresgid16)      /* 170 */
        .long SYMBOL_NAME(sys_getresgid16)
        .long SYMBOL_NAME(sys_prctl)
        .long SYMBOL_NAME(sys_rt_sigreturn)
        .long SYMBOL_NAME(sys_rt_sigaction)
        .long SYMBOL_NAME(sys_rt_sigprocmask)   /* 175 */
        .long SYMBOL_NAME(sys_rt_sigpending)
        .long SYMBOL_NAME(sys_rt_sigtimedwait)
        .long SYMBOL_NAME(sys_rt_sigqueueinfo)
        .long SYMBOL_NAME(sys_rt_sigsuspend)
        .long SYMBOL_NAME(sys_pread)            /* 180 */
        .long SYMBOL_NAME(sys_pwrite)
        .long SYMBOL_NAME(sys_chown16)
        .long SYMBOL_NAME(sys_getcwd)
        .long SYMBOL_NAME(sys_capget)
        .long SYMBOL_NAME(sys_capset)           /* 185 */
        .long SYMBOL_NAME(sys_sigaltstack)
        .long SYMBOL_NAME(sys_sendfile)
        .long SYMBOL_NAME(sys_ni_syscall)               /* streams1 */
        .long SYMBOL_NAME(sys_ni_syscall)               /* streams2 */
        .long SYMBOL_NAME(sys_vfork)            /* 190 */
        .long SYMBOL_NAME(sys_getrlimit)
        .long SYMBOL_NAME(sys_mmap2)
        .long SYMBOL_NAME(sys_truncate64)
        .long SYMBOL_NAME(sys_ftruncate64)
        .long SYMBOL_NAME(sys_stat64)           /* 195 */
        .long SYMBOL_NAME(sys_lstat64)
        .long SYMBOL_NAME(sys_fstat64)
        .long SYMBOL_NAME(sys_lchown)
        .long SYMBOL_NAME(sys_getuid)
        .long SYMBOL_NAME(sys_getgid)           /* 200 */
        .long SYMBOL_NAME(sys_geteuid)
        .long SYMBOL_NAME(sys_getegid)
        .long SYMBOL_NAME(sys_setreuid)
        .long SYMBOL_NAME(sys_setregid)
        .long SYMBOL_NAME(sys_getgroups)        /* 205 */
        .long SYMBOL_NAME(sys_setgroups)
        .long SYMBOL_NAME(sys_fchown)
        .long SYMBOL_NAME(sys_setresuid)
        .long SYMBOL_NAME(sys_getresuid)
        .long SYMBOL_NAME(sys_setresgid)        /* 210 */
        .long SYMBOL_NAME(sys_getresgid)
        .long SYMBOL_NAME(sys_chown)
        .long SYMBOL_NAME(sys_setuid)
        .long SYMBOL_NAME(sys_setgid)
        .long SYMBOL_NAME(sys_setfsuid)         /* 215 */
        .long SYMBOL_NAME(sys_setfsgid)
        .long SYMBOL_NAME(sys_pivot_root)
        .long SYMBOL_NAME(sys_mincore)
        .long SYMBOL_NAME(sys_madvise)
        .long SYMBOL_NAME(sys_getdents64)       /* 220 */
        .long SYMBOL_NAME(sys_fcntl64)
        .long SYMBOL_NAME(sys_ni_syscall)       /* reserved for TUX */
        .long SYMBOL_NAME(sys_ni_syscall)       /* Reserved for Security */
        .long SYMBOL_NAME(sys_gettid)
        .long SYMBOL_NAME(sys_readahead)        /* 225 */


IRQ 事件

当 IRQ 发生时,正在运行的任务会被中断,以便服务 IRQ 处理程序。

在 IRQ 被处理后,控制权会精确地返回到中断点,就像什么都没发生一样。

           
              Running Task 
             |-----------|          (3)
NORMAL       |   |       | [break execution] IRQ Handler
EXECUTION (1)|   |       |     ------------->|---------| 
             |  \|/      |     |             |  does   |         
 IRQ (2)---->| ..        |----->             |  some   |      
             |   |       |<-----             |  work   |       
BACK TO      |   |       |     |             |  ..(4). |
NORMAL    (6)|  \|/      |     <-------------|_________|
EXECUTION    |___________|  [return to code]
                                    (5)
               USER MODE                     KERNEL MODE

         User->Kernel Mode Transition caused by IRQ event
     

以下编号的步骤指的是上面图表中的事件序列

  1. 进程正在执行
  2. 当任务正在运行时,IRQ 发生。
  3. 任务被中断以调用“中断处理程序”。
  4. “中断处理程序”代码被执行。
  5. 控制权返回到任务用户模式 (就像什么都没发生一样)
  6. 进程返回到正常执行

定时器 IRQ 非常重要,它每 TIMER 毫秒发生一次,用于管理

  1. 警报
  2. 系统和任务计数器 (由调度程序使用,以决定何时停止进程或用于记账)
  3. 基于 TIMESLICE 时间后唤醒机制的多任务处理。

3.4 多任务处理

机制

现代操作系统的关键点是“任务”。任务是在内存中运行的应用程序,与其他任务共享所有资源 (包括 CPU 和内存)。

这种“资源共享”由“多任务处理机制”管理。多任务处理机制在“时间片”时间后从一个任务切换到另一个任务。用户有一种“错觉”,认为他们拥有所有资源。我们也可以想象一个单用户场景,用户可以“错觉”同时运行多个任务。

为了实现这种多任务处理,任务使用“状态”变量,它可以是

  1. READY,准备执行
  2. BLOCKED,等待资源

任务状态由其在相关列表中的存在来管理:READY 列表和 BLOCKED 列表。

任务切换

从一个任务到另一个任务的移动称为“任务切换”。许多计算机都有硬件指令自动执行此操作。任务切换发生在以下情况下

  1. 在时间片结束之后:我们需要调度一个“准备执行”的任务并赋予它访问权限。
  2. 当任务必须等待设备时:我们需要调度一个新任务并切换到它 *

* 我们调度另一个任务以防止“忙等待”,这种情况发生在当我们等待设备而不是执行其他工作时。

任务切换由“调度”实体管理。

 
Timer    |           |
 IRQ     |           |                            Schedule
  |      |           |                     ________________________
  |----->|   Task 1  |<------------------>|(1)Chooses a Ready Task |
  |      |           |                    |(2)Task Switching       |
  |      |___________|                    |________________________|   
  |      |           |                               /|\
  |      |           |                                | 
  |      |           |                                |
  |      |           |                                |
  |      |           |                                |      
  |----->|   Task 2  |<-------------------------------|
  |      |           |                                |
  |      |___________|                                |
  .      .     .     .                                .
  .      .     .     .                                .
  .      .     .     .                                .
  |      |           |                                |
  |      |           |                                |
  ------>|   Task N  |<--------------------------------
         |           |
         |___________| 
    
            Task Switching based on TimeSlice
 

Linux 的典型时间片约为 10 毫秒。

 

 |           |            
 |           | Resource    _____________________________
 |   Task 1  |----------->|(1) Enqueue Resource request |
 |           |  Access    |(2)  Mark Task as blocked    |
 |           |            |(3)  Choose a Ready Task     |
 |___________|            |(4)    Task Switching        |
                          |_____________________________|
                                       |
                                       |
 |           |                         |
 |           |                         |
 |   Task 2  |<-------------------------
 |           |  
 |           |
 |___________|
 
     Task Switching based on Waiting for a Resource
 

3.5 微内核与单内核操作系统

概述

到目前为止,我们已经了解了所谓的单内核操作系统,但还有另一种操作系统:''微内核''。

微内核操作系统使用任务,不仅用于用户模式进程,还用作真正的内核管理器,例如 Floppy-Task、HDD-Task、Net-Task 等等。一些例子是 Amoeba 和 Mach。

微内核操作系统的优点和缺点

优点

缺点

我个人的观点是,微内核是一个很好的教学示例 (如 Minix),但它们不是“最优的”,因此不太适用。Linux 使用一些称为“内核线程”的任务来实现一个小的微内核结构 (例如 kswapd,它用于从大容量存储中检索内存页)。在这种情况下,性能没有问题,因为交换是一个非常缓慢的工作。

3.6 网络

ISO OSI 层级

标准 ISO-OSI 描述了一个具有以下层级的网络架构

  1. 物理层 (示例:PPP 和 Ethernet)
  2. 数据链路层 (示例:PPP 和 Ethernet)
  3. 网络层 (示例:IP 和 X.25)
  4. 传输层 (示例:TCP, UDP)
  5. 会话层 (SSL)
  6. 表示层 (FTP 二进制-ASCII 编码)
  7. 应用层 (应用程序,如 Netscape)

上面列出的前 2 层通常在硬件中实现。接下来的层在软件中 (或路由器的固件中)。

操作系统使用许多协议:其中之一是 TCP/IP (最重要的位于 3-4 层)。

内核做什么?

内核对 ISO-OSI 的前 2 层一无所知 (只知道地址)。

在 RX 中

  1. 管理与底层设备 (如以太网卡或调制解调器) 的握手,从它们接收“帧”。
  2. 从“帧” (如以太网或 PPP 帧) 构建 TCP/IP“数据包”,
  3. 将“数据包”转换为“套接字”,并将它们传递给正确的应用程序 (使用端口号) 或
  4. 将数据包转发到正确的队列

frames         packets              sockets
NIC ---------> Kernel ----------> Application
                  |    packets
                  --------------> Forward
                        - RX - 

在 TX 阶段

  1. 转换套接字或
  2. 将数据排队到 TCP/IP“数据包”中
  3. 将“数据包”拆分为“帧” (如以太网或 PPP 帧)
  4. 使用硬件驱动程序发送“帧”

sockets       packets                     frames
Application ---------> Kernel ----------> NIC
              packets     /|\    
Forward  -------------------
                        - TX -  


3.7 虚拟内存

分段

分段是解决内存分配问题的第一个方法:它允许您编译源代码,而无需关心应用程序将放置在内存中的哪个位置。事实上,此功能帮助应用程序开发人员以独立于操作系统和硬件的方式进行开发。

     
            |       Stack        |
            |          |         |
            |         \|/        |
            |        Free        | 
            |         /|\        |     Segment <---> Process    
            |          |         |
            |        Heap        |
            | Data uninitialized |
            |  Data initialized  |
            |       Code         |
            |____________________|  
 
                   Segment  

我们可以说,段是应用程序的逻辑实体,或者应用程序在内存中的映像。

在编程时,我们不关心我们的数据放在内存中的哪个位置,我们只关心段 (我们的应用程序) 内部的偏移量。

我们过去常常为每个进程分配一个段,反之亦然。但在 Linux 中,情况并非如此。Linux 仅为内核和所有进程使用 4 个段。

分段的问题

 
                                 ____________________
                          ----->|                    |----->
                          | IN  |     Segment A      | OUT
 ____________________     |     |____________________|   
|                    |____|     |                    |   
|     Segment B      |          |     Segment B      |
|                    |____      |                    |   
|____________________|    |     |____________________|   
                          |     |     Segment C      |   
                          |     |____________________|
                          ----->|     Segment D      |-----> 
                            IN  |____________________| OUT 
 
                     Segmentation problem


在上面的图中,我们想要退出进程 A 和 D 并进入进程 B。正如我们所见,B 有足够的空间,但我们不能将其分成 2 块,因此我们无法加载它 (内存不足)。

出现此问题的原因是纯段是连续区域 (因为它们是逻辑区域),并且不能被拆分。

分页

 
             ____________________
            |     Page 1         |
            |____________________|
            |     Page 2         |
            |____________________| 
            |      ..            |     Segment <---> Process    
            |____________________|
            |     Page n         |
            |____________________|
            |                    |
            |____________________|
            |                    |
            |____________________|  
 
                   Segment  
 

分页将内存分成“n”块,每块都有固定的长度。

一个进程可以加载到一个或多个页中。当内存被释放时,所有页都被释放 (参见之前的分段问题)。

分页还用于另一个重要目的,“交换”。如果页不存在于物理内存中,则会生成一个异常,这将使内核在存储内存中搜索新页。这种机制允许操作系统加载比仅物理内存允许的更多的应用程序。

分页问题

             ____________________
   Page   X |     Process Y      |
            |____________________|
            |                    |
            |       WASTE        |
            |       SPACE        |
            |____________________|  
   
              Pagination Problem
 

在上面的图中,我们可以看到分页策略的错误之处:当进程 Y 加载到页 X 中时,页的所有内存空间都被分配,因此页末尾的剩余空间被浪费了。

分段和分页

我们如何解决分段和分页问题?使用这两种策略。

 
                                  |      ..            |
                                  |____________________|
                            ----->|      Page 1        |
                            |     |____________________|
                            |     |      ..            |
 ____________________       |     |____________________|
|                    |      |---->|      Page 2        |
|      Segment X     |  ----|     |____________________|
|                    |      |     |       ..           |
|____________________|      |     |____________________|
                            |     |       ..           |
                            |     |____________________|
                            |---->|      Page 3        |
                                  |____________________|
                                  |       ..           |
 

进程 X,由段 X 标识,被分成 3 块,并且每一块都加载到一个页中。

我们没有

  1. 分段问题:我们按页分配,因此我们也释放页,并且我们以优化的方式管理可用空间。
  2. 分页问题:只有最后一页浪费空间,但我们可以决定使用非常小的页,例如 4096 字节长度 (最多损失 4096*N_Tasks 字节),并管理分层分页 (使用 2 或 3 级分页)

 
 

                          |         |           |         |
                          |         |   Offset2 |  Value  |
                          |         |        /|\|         |
                  Offset1 |         |-----    | |         |
                      /|\ |         |    |    | |         |
                       |  |         |    |   \|/|         | 
                       |  |         |    ------>|         |
                      \|/ |         |           |         |
 Base Paging Address ---->|         |           |         |
                          | ....... |           | ....... |
                          |         |           |         |    
 
                     Hierarchical Paging

下一页 上一页 目录