使用LiME收集主机物理内存的内容时发生宕机

本文涉及的产品
文档翻译,文档翻译 1千页
文本翻译,文本翻译 100万字符
图片翻译,图片翻译 100张
简介: 使用LiME收集主机物理内存的内容时发生宕机

作者

pengdonglin137@163.com

现象

在一台ARM64的Centos7虚拟机里加载 https://github.com/504ensicsLabs/LiME 编译出的内核模块时发生宕机:

insmod lime.ko path=/root/allmem.dump format=raw

上面的目的是把机器物理内存的内容全部dump到文件中,大致的实现过程是,遍历系统中所有的"System RAM",然后处理每一个物理页:根据物理页帧获取对应的page,然后调用kmap_atomic得到虚拟地址,最后将这个虚拟页的数据读取出来存放到文件中。

分析

宕机的调用栈如下:

如果对ARM64的页表属性很熟悉的话,应该可以看出PTE的bit0是0,说明这是一个无效的PTE,虽然其他的bit看上去很正常。

如果对页表熟悉不熟的话,当然也可以分析,就是麻烦一些,下面按不熟的方法来。

在源码中加调试语句,把每次访问的物理也的信息打印出来:

反复几次,发现每次都是这个地址出错0xffff80009fe80000,对应的物理地址是0xdfe80000。

为了测试这个问题,我单独写了一个demo模块,单独去访问这个地址,发现确实会宕机。

查看代码,发现驱动中使用kmap_atomic获取page对应的虚拟地址:

看上去直接返回的是这个page对应的64KB物理内存在直接映射区的虚拟地址,而且是在开机时就映射好的,没有道理不能访问呀:

ESR的内容记录了发生异常的原因,在读的时候发生了DARA ABORT异常。

查看这段物理地址空间在crash kernel的范围内:(/proc/iomem)

难道跟crash kernel有关?暂时放下这个。

那是不是可以把之前可以访问的物理页的映射信息也打出来比较一下呢?紧接着需要思考如何将某个虚拟地址的页表映射信息输出呢?

内核提供了show_pte这个函数:arch/arm64/mm/fault.c

void show_pte(unsigned long addr)
{
  struct mm_struct *mm;
  pgd_t *pgdp;
  pgd_t pgd;
  if (is_ttbr0_addr(addr)) {
    /* TTBR0 */
    mm = current->active_mm;
    if (mm == &init_mm) {
      pr_alert("[%016lx] user address but active_mm is swapper\n",
         addr);
      return;
    }
  } else if (is_ttbr1_addr(addr)) {
    /* TTBR1 */
    mm = &init_mm;
  } else {
    pr_alert("[%016lx] address between user and kernel address ranges\n",
       addr);
    return;
  }
  pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n",
     mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K,
     vabits_actual, (unsigned long)virt_to_phys(mm->pgd));
  pgdp = pgd_offset(mm, addr);
  pgd = READ_ONCE(*pgdp);
  pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd));
  do {
    pud_t *pudp, pud;
    pmd_t *pmdp, pmd;
    pte_t *ptep, pte;
    if (pgd_none(pgd) || pgd_bad(pgd))
      break;
    pudp = pud_offset(pgdp, addr);
    pud = READ_ONCE(*pudp);
    pr_cont(", pud=%016llx", pud_val(pud));
    if (pud_none(pud) || pud_bad(pud))
      break;
    pmdp = pmd_offset(pudp, addr);
    pmd = READ_ONCE(*pmdp);
    pr_cont(", pmd=%016llx", pmd_val(pmd));
    if (pmd_none(pmd) || pmd_bad(pmd))
      break;
    ptep = pte_offset_map(pmdp, addr);
    pte = READ_ONCE(*ptep);
    pr_cont(", pte=%016llx", pte_val(pte));
    pte_unmap(ptep);
  } while(0);
  pr_cont("\n");
}

但是函数并没有调用EXPORT_SYMBOL_GPL导出给模块用,怎么办呢?

可以使用内核提供的kallsyms_lookup_name来获取这个函数的地址:

void (*func)(unsigned long addr);
func = kallsyms_lookup_name("show_pte");
func(addr);

如果内核连kallsyms_lookup_name都没有导出怎么办?

可以使用kprobe。在调用register_kprobe注册kprobe的时候,会根据设置的函数名称得到函数地址,然后存放到kprobe->addr中,那么我们可以先只设置kprobe->symbol_name,当注册成功可以访问kprobe->addr得到函数的地址。目前在最新的6.5版本的内核里,register_kprobe也是导出的。

有了show_pte,那么可以输出之前几个地址的PTE的内容:

对比发现PTE的值排除物理地址占用的bit外,属性部分只有bit0的内容不同。

既然kmap_atomic直接返回了物理页的线性地址,那么可不可以通过ioremap把这个有问题的物理地址重新映射一下呢? 我测试了一下,不行,在ioremap时会检查要映射的物理地址是否是合法的系统物理内存地址,更明确地说是DDR内存,这里要跟设备内存地址区别开来。如果是系统物理内存,那么直接返回0. 这么处理也好理解,既然是ioremap,当然应该针对的是io memory,如寄存器地址。下面是ARM64上ioreamp的定义:

#define ioremap(addr, size)   __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define ioremap_wc(addr, size)    __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
#define ioremap_wt(addr, size)    __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)
void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot)
{
  return __ioremap_caller(phys_addr, size, prot,
        __builtin_return_address(0));
}
static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,
              pgprot_t prot, void *caller)
{
  unsigned long last_addr;
  unsigned long offset = phys_addr & ~PAGE_MASK;
  int err;
  unsigned long addr;
  struct vm_struct *area;
  /*
   * Page align the mapping address and size, taking account of any
   * offset.
   */
  phys_addr &= PAGE_MASK;
  size = PAGE_ALIGN(size + offset);
  /*
   * Don't allow wraparound, zero size or outside PHYS_MASK.
   */
  last_addr = phys_addr + size - 1;
  if (!size || last_addr < phys_addr || (last_addr & ~PHYS_MASK))
    return NULL;
  /*
   * Don't allow RAM to be mapped.
   */
  if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))))
    return NULL;
  area = get_vm_area_caller(size, VM_IOREMAP, caller);
  if (!area)
    return NULL;
  addr = (unsigned long)area->addr;
  area->phys_addr = phys_addr;
  err = ioremap_page_range(addr, addr + size, phys_addr, prot);
  if (err) {
    vunmap((void *)addr);
    return NULL;
  }
  return (void __iomem *)(offset + addr);
}

可以看到,上面的内存属性都是DEVICE MEMORY,其中pfn_valid(__phys_to_pfn(phys_addr))就是用来判断是否是系统物理内存的,如果是的话,返回true,那么ioremap就会直接返回0.

下面分析PTE是怎么构造的呢?以缺页异常中中构造PTE的部分为例:

handle_pte_fault
  |- do_anonymous_page
    |- entry = mk_pte(page, vma->vm_page_prot);

这里vm_page_prot存放的就是PTE中属性部分,这些属性是通过vm_get_page_prot根据vm_flags转换而来:

/* description of effects of mapping type and prot in current implementation.
 * this is due to the limited x86 page protection hardware.  The expected
 * behavior is in parens:
 *
 * map_type prot
 *    PROT_NONE PROT_READ PROT_WRITE  PROT_EXEC
 * MAP_SHARED r: (no) no  r: (yes) yes  r: (no) yes r: (no) yes
 *    w: (no) no  w: (no) no  w: (yes) yes  w: (no) no
 *    x: (no) no  x: (no) yes x: (no) yes x: (yes) yes
 *
 * MAP_PRIVATE  r: (no) no  r: (yes) yes  r: (no) yes r: (no) yes
 *    w: (no) no  w: (no) no  w: (copy) copy  w: (no) no
 *    x: (no) no  x: (no) yes x: (no) yes x: (yes) yes
 */
pgprot_t protection_map[16] __ro_after_init = {
  __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
  __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
};
pgprot_t vm_get_page_prot(unsigned long vm_flags)
{
  pgprot_t ret = __pgprot(pgprot_val(protection_map[vm_flags &
        (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]) |
      pgprot_val(arch_vm_get_page_prot(vm_flags)));
  return arch_filter_pgprot(ret);
}
EXPORT_SYMBOL(vm_get_page_prot);

上面这些宏定义在arch/arm64/include/asm/pgtable-prot.h中,

#define PAGE_NONE   __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
#define PAGE_SHARED   __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
#define PAGE_SHARED_EXEC  __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE)
#define PAGE_READONLY   __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
#define PAGE_READONLY_EXEC  __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
#define __P000  PAGE_NONE
#define __P001  PAGE_READONLY
#define __P010  PAGE_READONLY
#define __P011  PAGE_READONLY
#define __P100  PAGE_READONLY_EXEC
#define __P101  PAGE_READONLY_EXEC
#define __P110  PAGE_READONLY_EXEC
#define __P111  PAGE_READONLY_EXEC
#define __S000  PAGE_NONE
#define __S001  PAGE_READONLY
#define __S010  PAGE_SHARED
#define __S011  PAGE_SHARED
#define __S100  PAGE_READONLY_EXEC
#define __S101  PAGE_READONLY_EXEC
#define __S110  PAGE_SHARED_EXEC
#define __S111  PAGE_SHARED_EXEC

其中BIT0对应的是宏是PTE_VALID,有问题的PTE的BIT0确实是0.

然后搜索一下这个宏在内核中的用法,发现使用这个宏的函数还不少:

int set_memory_valid(unsigned long addr, int numpages, int enable)
{
  if (enable)
    return __change_memory_common(addr, PAGE_SIZE * numpages,
          __pgprot(PTE_VALID),
          __pgprot(0));
  else
    return __change_memory_common(addr, PAGE_SIZE * numpages,
          __pgprot(0),
          __pgprot(PTE_VALID));
}
/*
 * This function is used to determine if a linear map page has been marked as
 * not-valid. Walk the page table and check the PTE_VALID bit. This is based
 * on kern_addr_valid(), which almost does what we need.
 *
 * Because this is only called on the kernel linear map,  p?d_sect() implies
 * p?d_present(). When debug_pagealloc is enabled, sections mappings are
 * disabled.
 */
bool kernel_page_present(struct page *page);
static inline pte_t pte_mkpresent(pte_t pte)
{
  return set_pte_bit(pte, __pgprot(PTE_VALID));
}
static inline int pte_protnone(pte_t pte)
{
  return (pte_val(pte) & (PTE_VALID | PTE_PROT_NONE)) == PTE_PROT_NONE;
}

接着看到arch_kexec_protect_crashkres调用了set_memory_valid,这个函数是给crash_kernel所在的内存设置属性的,将那段内存映射的属性设置为无效,防止被破坏。

void arch_kexec_protect_crashkres(void)
{
  int i;
  kexec_segment_flush(kexec_crash_image);
  for (i = 0; i < kexec_crash_image->nr_segments; i++)
    set_memory_valid(
      __phys_to_virt(kexec_crash_image->segment[i].mem),
      kexec_crash_image->segment[i].memsz >> PAGE_SHIFT, 0);
}

结合之前看到的iomem的内容,基本可以确认就是这导致的。

验证

下面验证了一下,将/etc/default/grub中配置的crashkernel=auto删除,然后重新生成grub.cfg,重启后再次加载lime模块就可以正常运行了。

补充

最后补充一点ARM64的页表属性和ESR的知识,参考ARMv8手册。

TABLE和BLOCK级的页表项的格式(Table和Block描述符)

可以看到,BIT0如果是0,那么就是无效的。Block描述符的bit1是0,Table描述符的bit1是1.

PTE级的页表项格式(Page描述符)

对于Page描述符,bit0和bit1都必须是1才有效,否则的话访问会异常。

64KB页情况下的页表遍历

内存属性

在页表中除了包含物理内存地址外,还包含用于描述这段内存地址属性的字段,在ARMv8中页表中分为Table描述符、Block描述符和Page描述符,在虚拟化场景,还分为Stage1和Stage2两个阶段,每个阶段的地址翻译都有前面3中类型,根据PAGE_SIZE的不同,每种类型都有各自的格式。

其中说,Stage-2的Table描述符中没有内存属性字段,其他的描述符都有内存属性字段,但是Stage1和Stage2的有所区别。

Stage1的Table描述符中属性字段

可以看到,这里只有Upper Attributes。

Bit Name Meaning
63 NSTable 只对Secure状态下的内存访问有效,在Non-secure状态下忽略
62:61 APTable Access permissions limit for subsequent levels of lookup
60 UXNTable or XNTable XN limit for subsequent levels of lookup. This bit is called UXNTable in the EL1&0 translation regime, where it only determines whether execution at EL0 of instructions fetched from the region identified at a lower level of lookup permitted. In the other translation regimes the bit is called XNTable.
59 PXNTable PXN limit for subsequent levels of lookup

Stage1的Block和Page描述符中的属性字段

上面Lower attributes是从bit2开始的。

Bit Name Meaning
54 UXN or XN The Execute-never bit. Determines whether the region is executable. This bit is called UXN in the EL1&0 translation regime, where it only determines whether execution at EL0 of instructions fetched from the region is permitted. In the other translation regimes the bit is called XN
53 PXN The Privileged execute-never bit. Determines whether the region is executable at EL1
52 Contiguous A hint bit indicating that the translation table entry is one of a contiguous set or entries, that might be cached in a single TLB entry
11 nG The not global bit. Determines whether the TLB entry applies to all ASID values, or only to the current ASID value. Valid only to the EL1&0 translation regime.
10 AF The Access flag
9:8 SH Shareability field
7:6 AP[2:1] Data Access Permissions bits. The ARMv8 translation table descriptor format defines AP[2:1] as the Access Permissions bits, and does not define an AP[0] bit.
5 NS Non-secure bit. For memory accesses from Secure state, specifies whether the output address is in the Secure or Non-secure address map
4:2 AttrIndx[2:0] Stage 1 memory attributes index field, for the MAIR_ELx

Stage2的Block和Page描述符的属性字段

上面Lower attributes是从bit2开始的。

Bit Name Meaning
54 XN The Execute-never bit. Determines whether the region is executable
52 Contiguous A hint bit indicating that the translation table entry is one of a contiguous set or entries, that might be cached in a single TLB entry
10 AF The Access flag
9:8 SH Shareability field
7:6 S2AP Stage 2 data Access Permissions bits
5:2 MemAttr Stage 2 memory attributes

主要的几个bit的含义

The Access flag

下面是Linux内核中的用法:

static inline pte_t pte_mkold(pte_t pte)
{
  return clear_pte_bit(pte, __pgprot(PTE_AF));
}

函数ptep_test_and_clear_young会调用上面的函数,主要用于检测page的活跃程度。

Data Access Permissions bits

  • 在Stage1的Block和Page描述符中的属性字段中的AP[2:1]

  • 在Stage2的Block和Page描述符的属性字段中的S2AP:

从这里看到,AP[1]用于控制用户是否可以访问,AP[2]用于控制是否只读,内核里是这么用的:

#define PTE_USER    (_AT(pteval_t, 1) << 6)   /* AP[1] */
#define PTE_RDONLY    (_AT(pteval_t, 1) << 7)   /* AP[2] */
  • PTE_USER

可以通过下面的宏判断是否是用户映射:

#define pte_user(pte)   (!!(pte_val(pte) & PTE_USER))
static inline bool pte_user_accessible_page(pte_t pte)
{
  return pte_present(pte) && (pte_user(pte) || pte_user_exec(pte));
}
  • PTE_RDONLY

用于控制是否只读,内核是这么用的:

// 当page是clean时,为了检测写访问时变脏,会设置为只读
static inline pte_t pte_mkclean(pte_t pte)
{
  pte = clear_pte_bit(pte, __pgprot(PTE_DIRTY));
  pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
  return pte;
}
// 写保护
static inline pte_t pte_wrprotect(pte_t pte)
{
  /*
   * If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY
   * clear), set the PTE_DIRTY bit.
   */
  if (pte_hw_dirty(pte))
    pte = pte_mkdirty(pte);
  pte = clear_pte_bit(pte, __pgprot(PTE_WRITE));
  pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
  return pte;
}

Access permissions limit for subsequent levels of lookup

在Stage1的Table描述符中属性字段中的APTable:

Shareability field

在"Stage1的Block和Page描述符中的属性字段"和"Stage2的Block和Page描述符的属性字段"中的SH:

需要注意的是:

The shareability field is only relevant if the memory is a Normal Cacheable memory type. All Device and Normal Non-cacheable memory regions are always treated as Outer Shareable, regardless of the translation table shareability attributes.

Linux内核中提供了下面的宏:

#define PTE_SHARED    (_AT(pteval_t, 3) << 8)   /* SH[1:0], inner shareable */

即默认Linux的内存映射用的是InnerShareable。

MAIR_EL1

截取部分手册中对Attr的约定:

  • Device Memory

  • Normal Memory

在内核启动时会在arch/arm64/mm/proc.S中设置MAIR_EL1寄存器,预制内核可能会用到的各种内存属性

其中相关宏的定义是:

#define MAIR(attr, mt)  ((attr) << ((mt) * 8))

可以看到上面对Inner和Outer配置的Cache策略是相同的。

在映射的时候,会用到上面MT_DEVICE*/MT_NORMAL*,这些宏表示属性在MAIR_EL1寄存器中的索引。

以下面的函数为例:

void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size)
{
  /* For normal memory we already have a cacheable mapping. */
  if (pfn_valid(__phys_to_pfn(phys_addr)))
    return (void __iomem *)__phys_to_virt(phys_addr);
  return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL),
        __builtin_return_address(0));
}

表示将指定的物理地址映射为PORT_NORMAL属性的,这种属性的内存是带cache的。这个宏定义在arch/arm64/include/asm/pgtable-prot.h中:

#define PROT_DEVICE_nGnRnE  (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_NORMAL_NC    (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
#define PROT_NORMAL_WT    (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
#define PROT_NORMAL   (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))

后面用的是PTE_ATTRINDX(MT_NORMAL),属性索引是MT_NORMAL,在MAIR_EL1中的属性值是0xFF

ESR_EL1

  • 当发生异常时,ESR会记录发生异常的原因,对于陷入到EL1的情况,参考ESR_EL1

以上面报错为例,ESR_EL1的值为0x96000007:

  • EC的值是:0b100101

表示Data Abort,具体细节还需要查看ISS。

  • IL的值:1

表示出异常的指令是一条32bit的指令。

  • ISS的值是:0b111

下面挑几个必要重要的bit位说明一下:

Bit Name Val Meaning
15 FnP, FAR not Precise. 0 The FAR holds the faulting virtual address that generated the Data Abort
7 S1PTW, For a stage 2 fault, indicates whether the fault was a stage 2 fault on an access made for a stage 1 translation table walk 0 Fault not on a stage 2 translation for a stage 1 translation table walk
6 WnR, Write not Read. Indicates whether a synchronous abort was caused by an instruction writing to a memory location, or by an instruction reading from a memory location 0 Abort caused by an instruction reading from a memory location
5:0 DFSC, Data Fault Status Code 0b000111 Translation fault, level 3
相关文章
|
4月前
|
设计模式 uml
在电脑主机(MainFrame)中只需要按下主机的开机按钮(on()),即可调用其它硬件设备和软件的启动方法,如内存(Memory)的自检(check())、CPU的运行(run())、硬盘(Hard
该博客文章通过一个电脑主机启动的示例代码,展示了外观模式(Facade Pattern)的设计模式,其中主机(MainFrame)类通过调用内部硬件组件(如内存、CPU、硬盘)和操作系统的启动方法来实现开机流程,同时讨论了外观模式的优缺点。
|
3月前
crash —— 获取物理内存布局信息
crash —— 获取物理内存布局信息
|
5月前
|
开发者 Java
JVM内存问题之top命令的物理内存信息中,'used'和'free','avail Mem'分别表示什么
JVM内存问题之top命令的物理内存信息中,'used'和'free','avail Mem'分别表示什么
|
5月前
|
监控
主机状态监控,通过top命令查看CPU、内存使用情况,ctrl + c退出,输入top整个页面就变成一个任务管理器的形式了,Ctrl + C直接退出,Q也可以退掉了
主机状态监控,通过top命令查看CPU、内存使用情况,ctrl + c退出,输入top整个页面就变成一个任务管理器的形式了,Ctrl + C直接退出,Q也可以退掉了
|
7月前
|
算法 内存技术
深入理解操作系统内存管理:从虚拟内存到物理内存的旅程
【5月更文挑战第24天】 在现代计算机系统中,操作系统的内存管理是确保系统高效稳定运行的关键组成部分。本文将探讨操作系统是如何通过虚拟内存到物理内存的映射机制,实现对内存资源的高效管理和保护。我们将剖析分页和分段两种主要的内存管理技术,并讨论它们如何协同工作以提供内存抽象、重定位、共享和保护。文章还将涉及虚拟内存的技术细节,包括页面置换算法和内存分配策略,以及它们对系统性能的影响。
|
7月前
|
存储 缓存 算法
深入理解操作系统内存管理:从虚拟内存到物理内存
【5月更文挑战第30天】操作系统的心脏——内存管理,在系统性能和稳定性中扮演着关键角色。本文将深入探讨操作系统中的内存管理机制,特别是虚拟内存与物理内存之间的映射关系、分页机制以及内存分配策略。通过分析现代操作系统如何处理内存资源,我们可以更好地理解计算机系统的内部工作原理,并掌握提升系统性能的关键因素。
|
7月前
|
存储 算法 内存技术
深入理解操作系统内存管理:从虚拟内存到物理内存的映射
【4月更文挑战第30天】 在现代操作系统中,内存管理是一个复杂而关键的功能。它不仅确保了系统资源的有效利用,还为每个运行的程序提供了独立的地址空间,保障了程序之间的隔离性和安全性。本文将探讨操作系统如何通过分页机制和虚拟内存技术实现内存的抽象化,以及这些技术是如何影响应用程序性能的。我们将详细解析虚拟地址到物理地址的转换过程,并讨论操作系统在此过程中扮演的角色。文章的目的是为读者提供一个清晰的框架,以便更好地理解内存管理的工作原理及其对系统稳定性和效率的影响。
|
7月前
|
人工智能 缓存 算法
深入理解操作系统内存管理:从虚拟内存到物理内存的映射
【4月更文挑战第8天】 在现代操作系统中,内存管理是核心功能之一,它负责协调和管理计算机的内存资源,确保系统稳定高效地运行。本文深入探讨了操作系统内存管理的关键概念——虚拟内存和物理内存的映射机制。通过剖析分页系统、分段机制和虚拟内存地址转换过程,文章旨在为读者提供一个清晰的理解框架,同时讨论了内存管理的优化技术及其对系统性能的影响。此外,还简要介绍了内存碎片问题以及垃圾回收机制的重要性,并展望了未来内存管理技术的发展趋势。
|
7月前
|
存储 安全 Linux
机器物理内存用光后,到底会怎么样?
以下一切在Linux OS(3.10.0)的运行环境下讨论: 我们都知道,进程内存资源不足后,会OOM crash,进程退出。 然而,如果机器的物理内存资源被占用光后,在不考虑cgroup kill的情况下,会怎么样呢?其上的进程又会表现如何? 有人说,机器会宕机。 有人说,进程申请内存失败会在进程...
135 0
机器物理内存用光后,到底会怎么样?
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
394 0