当前位置: 首页 > 科技观察

Linux系统内存知识总结

时间:2023-03-16 01:19:55 科技观察

一、走进linux内存1、什么是内存?1)内存,又称主存,是CPU可以直接寻址的存储空间,由半导体器件制成。2)内存的特点是存取速度快。2、内存的作用1)暂存CPU的计算数据2)硬盘等外存交换的数据3)保证cpu计算的稳定性和高性能2、Linux内存地址空间1、Linux内存地址spaceLinux内存管理概述2.内存地址-用户态&内核态用户态:Ring3运行在用户核心态的代码受制于处理器的许多内核态:Ring0在处理器的存储保护中,有从核心态用户态切换到内核态的三种方式:系统调用、异常、外设中断区别:每个进程都有完全属于自己的、独立的、不间断的内存空间;用户态程序不能随意操作内核地址空间,具有一定的安全保护;内核态线程共享内核地址空间;3.内存地址——MMU地址转换MMU是一个硬件电路,由两部分组成,一个是分段部分,一个是分页部分。分段机制将逻辑地址转换为线性地址。分页机制将线性地址转换为物理地址。4.内存地址——段机制1)段选择器为了方便和快速的检索段选择器,处理器提供了6个段寄存器来缓存段选择器,分别是:cs,ss,ds,es,fs,gs的基地址segments(BaseAddress):段在线性地址空间中的起始地址segment的limit(Limit):在虚拟地址空间中,段中可以使用的最大偏移量段描述符,进而得到段基址address和segmentlimit从段描述符,然后加上逻辑地址的偏移量得到线性地址5.内存地址-分页机制(32位)分页机制是在段机制之后,进一步将线性地址转化为物理地址10位页目录、10位页表项和12位页偏移地址。单页大小为4KB6。用户态地址空间TEXT:代码段可执行代码,字符串字面值,只读变量DATA:数据段,映射程序中已经初始化的全局变量BSS段BSS段:存放程序中未初始化的全局变量HEAP:runtimeheap,程序运行时malloc申请的内存区域MMAP:共享库和匿名文件映射区STACK:用户进程栈7.内核态地址空间直接映射区:线性空间中从3G开始最大896M区间,为直接内存映射区动态内存映射区:本区由内核函数vmalloc控制分配永久内存映射区:该区可以访问高端内存固定映射区:该区与4G的顶部只有一个4k的隔离区,每个地址项都有特定的用途,如如:ACPI_BASE等8.进程内存空间用户进程通常只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址。内核空间由内核映射,不会随进程改变;内核空间地址有自己对应的页表,用户进程有不同的Front页表3.Linux内存分配算法内存管理算法——上帝给那些讨厌自己管理内存的人的礼物生命周期更长,内存碎片会重复应用后发生。优点:提高分配速度,方便内存管理,防止内存泄漏。缺点:大量的内存碎片会拖慢系统,内存使用率低,浪费大2)如何避免内存碎片少使用动态内存分配函数(尽量使用栈空间)分配内存和释放内存试试在同一个函数中一次性申请更大的内存,而不是重复申请小内存尽可能申请大块2Exponentialpower-sized内存空间外部碎片避免-伙伴系统算法内部碎片避免-slab算法执行内存自己管理工作,设计内存池2、搭档系统算法——组织结构1)概念为内核提供了一种通过分配一组连续页建立的高效分配策略,有效解决了外部碎片问题。分配的内存区域是基于页框的。2)外部碎片外部碎片是指还没有分配(不属于任何进程),但是因为太小,无法分配到申请内存空间的新进程的内存空闲区3)组织结构将所有空闲页分组为11个块列表,每个块列表包含1、2、4、8、16、32、64、128、256、512和1024个连续页框的页块最多可申请1024个连续页,对应4MB连续内存3.好友系统算法-申请与回收1)申请算法申请2^i页块存储空间,如果2^对应的区块链表i有空闲页块,如果没有空闲页块,则分配给应用程序,然后检查2^(i1)对应的块列表是否有空闲页块,如果有,则分配2^i个块列表节点给应用程序,将其他2^i个块列表节点插入到2^i对应的块列表中,如果2^(i1)块列表中没有空闲页块,则重复步骤2,直到一个有空闲页的块列表blocksisfound如果仍然没有blocklist,返回内存分配失败2)回收算法释放2^i个pageblocks的存储空间,查找2^i个pageblocks对应的blocklist,检查是否有page物理地址连续的块。如果不是,则无需合并它们。i1)页块,以此类推,继续寻找下一级块链接,直到无法合并3)两个块大小相同且物理地址连续的页块大小相同的条件4.4M以上内存如何分配?1)为什么要限制大块内存的分配分配的内存越大,失败的可能性越大,使用大块内存的场景就越少2)内核中获取4M以上大内存的方法是修改MAX_ORDER,重新编译内核,启动时选择传递“mem=”参数,如“mem=80M,预留部分内存;然后通过request_mem_region和ioremap_nocache将预留内存映射到模块中。需要修改内核启动参数无需重新编译内核。但是这种方法不支持x86架构,只支持ARM、PowerPC等非x86架构。调用alloc_boot_mem函数在start_kernel中的mem_init函数之前预分配一大块内存。内核vmalloc函数需要重新编译,内核代码使用它来分配连续的虚拟内存,但不一定是连续的物理内存内存5.Buddysystem—anti-fragmentationmechanism1)不可移动的页面这些页面在内存中有一个固定的位置,不能被移动或回收。来自内核kmalloc()的内核代码段、数据段和内存被内核线程占用。2)可回收页面这些页面不能移动,但可以删除。当回收的页面占用过多内存或内存不足时,内核会回收页面。3)可移动页面这些页面可以任意移动,用户空间应用程序使用的页面属于此类。它们是通过页表映射的,当它们移动到一个新的位置时,页表条目将相应地更新一种系统首次引入的算法。它的基本思想是将内核中经常使用的对象放入缓存中,让它们保持在系统初始可用状态。比如进程描述符,内核会频繁的申请和释放这个数据2)内部碎片分配的内存空间大于请求需要的内存空间3)基本目标是减少伙伴花费的时间分配连续内存小块时的算法生成的内部碎片缓存频繁使用的对象,减少分配、初始化和释放对象的时间开销通过着色技术调整对象,更好地使用硬件缓存7.slab分配器的结构由于对象是从slab分配并释放,因此单个slab可以在slab列表之间移动slabs_empty列表中的slab是获取slab的主要候选者slab还支持通用对象初始化,这避免了为相同目的复制对象初始化8.Slab缓存1)普通缓存slab分配器提供的小块连续内存的分配实现了th粗糙的一般缓存。通用缓存提供的对象几何分布大小在32到131072字节之间。内核提供了kmalloc()和kfree()两个接口,分别申请和释放内存。2)专用缓存内核为专用缓存的申请和释放提供了一套完整的接口。根据传入的参数,Specificobjectallocationslabcachekmem_cache_create()用于为指定对象创建缓存。它从cache_cache公共缓存中为新的私有缓存分配一个缓存描述符,并将这个描述符插入到缓存描述符组成的cache_chain链表中。kmem_cache_alloc()在其参数slab指定的缓存中分配一个缓存描述符。相反,kmem_cache_free()在其参数指定的cache中释放一个slab9和内核态内存池1)基本原理首先申请分配一定数量(一般情况下)大小相等的内存块作为备份,当有是新的需要内存的时候,从内存池中分配一部分内存块。如果内存块不够用,继续申请新的内存。这样做的一个显着好处是尽可能避免了内存碎片,提高了内存分配效率。2)KernelAPImempool_create创建内存池对象mempool_alloc分配函数获取对象mempool_free释放对象mempool_destroy销毁内存池10、用户态内存池1)C++实例11、DMA内存1)什么是DMA直接内存访问是硬件允许外围设备和主存储器之间直接传输它们的I/O数据而无需系统处理器参与的机制2)DMA控制器的功能可以向CPU发送系统保持(HOLD)信号,并提出总线接管要求。当CPU发出权限接管信号,负责总线的控制,进入DMA模式,可以寻址内存并修改地址指针,实现对内存的读写操作,确定传输的字节数此时通过DMA,判断DMA传输是否结束,并发出DMA结束信号,使CPU恢复正常工作状态。2)DMA信号DREQ:DMA请求信号。是外设对DMA控制器的请求信号,DMA操作的申请信号DACK:DMA响应信号。它是DMA控制器向发出DMA请求的外围设备指示它已收到请求并正在处理它的信号。HRQ:DMA控制器发给CPU的信号,请求接管总线的请求信号。HLDA:CPU发送给DMA控制器的信号,让响应信号接管总线:4.内存使用场景outofmemory的时代过去了吗?不行,内存再多也不能乱用。1.内存使用场景pagemanagementslab(kmalloc,memorypool)用户态内存使用(malloc,rellocfilemapping,sharedmemory)程序内存映射(stack,heap,code,data)内核和用户态数据传输(copy_from_user,copy_to_user)内存映射(硬件寄存器,保留内存)DMA内存2、用户态内存分配函数alloca从栈中申请内存,所以不需要释放malloc分配的内存空间。使用malloc()函数的程序启动(内存空间还没有被重新分配)可以正常运行,但是一段时间后(内存空间已经被重新分配)可能会出问题calloc会将分配的内存空间中的每一位都初始化为零reallocextensionExistingmemoryspacesizea)如果当前连续内存块足够realloc,就扩展p指向的空间,返回p的指针地址。此时q和p指向的地址是一样的。b)如果当前连续内存块不够长,则找一个足够长的地方分配新的内存块q,将p指向的内容复制到q,并返回q。并删除p指向的内存空间3.内核态内存分配函数函数分配原理最大内存Others_get_free_pages直接对页框进行操作4MB适用于分配大量连续的物理内存kmem_cache_alloc是基于slab机制实现的128KB适合频繁应用当释放相同大小的内存块时,使用kmalloc基于kmem_cache_alloc实现最常见的128KB分配方式。当您需要小于页框大小时,可以使用vmalloc创建从非连续物理内存到虚拟地址的映射。物理不连续适用于大内存,但对于不需要地址连续性的情况,dma_alloc_coherent在_alloc_pages的基础上实现了4MB,适用于DMA操作。ioremap实现了已知物理地址到虚拟地址的映射。适用于物理地址已知的场合。例如,设备驱动程序alloc_bootmem在启动内核时预留了一段内存,内核看不到它小于物理内存大小,内存管理要求高。4、malloc申请内存。一个freeheapspacebufferlist如果spacebufferlist没有找到对应的节点,则需要通过系统调用sys_brk扩展进程的栈空间5.对于pagefault异常,通过get_free_pages申请一个或多个物理页在进程pdg映射中转换addr所在的pte地址,将addr对应的pte设置为物理页系统调用的首地址:brk—申请内存小于等于128kb,do_map—申请内存大于大于128kb6、用户进程访问内存分析用户态进程独享虚拟地址空间,两个进程的虚拟地址可以相同。访问用户态虚拟地址空间时,如果没有物理地址映射,则通过系统调用抛出缺页异常,缺页异常落入内核,分配物理地址空间,建立映射与用户模式的虚拟地址。7.共享内存1)原理它允许多个不相关的进程访问逻辑内存的同一部分来在两个运行的进程之间传递数据,共享内存将是一个非常高效的解决方案两个运行的进程共享数据,是一个进程间的一种高效方法-通信,可有效减少数据副本数2)shm接口shmget创建共享内存shmat开始访问共享内存,并将共享内存连接到当前进程的地址空间shmdt将共享内存与当前进程分离五、内存使用陷阱1、类构造函数和C内存泄漏new和delete函数在析构函数中不匹配。嵌套对象指针未正确清除。基类的析构函数没有定义为虚函数。当基类的指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数不会被调用,子类的资源也不会被正确释放,所以内存泄漏将是由于缺少拷贝构造函数造成的。按值传递会调用(复制)构造函数,而按引用传递不会调用对象指针数组不等同于对象数组。该数组存储指向对象的指针。不仅要释放每个对象的空间,还要释放每个指针的空间。缺少重载的赋值运算符也是一个成员一个成员的复制。如果这个类的大小是可变的,结果就是内存泄漏。2.C野指针变量没有初始化。指针释放或删除后,它不会设置为NULL。指针操作超出了变量的范围。比如返回栈内存的指针是野指针访问空指针(需要做空判断)sizeof无法获取数组大小尝试修改常量,如:charp="1234";p='1';3、C资源访问冲突较多线程共享变量没有用valotile修饰。多线程访问全局变量不加锁。全局变量只对单个进程有效。多个进程在不同步的情况下写入共享内存数据。Mmap内存映射对于多进程来说是不安全的。4.STL迭代器失效和删除Iteratorinvalidation添加元素(insert/push_back等),删除元素导致顺序容器迭代器失效错误示例:删除当前迭代器,迭代器将失效正确示例:当迭代器被擦除时,下一个迭代器需要保存5、C++11用unique_ptr替换智能指针auto_ptr使用make_shared初始化一个shared_ptrweak_ptr智能指针助手(1)原理分析:(2)数据结构:(3)用法:a.lock()获取被管理对象的强引用指针b.expired()测试管理C。get()访问智能指针对象6、C++11更小、更快、更安全std::atomic原子数据类型多线程安全std::array定长数组开销比array和std::vector的区别在于长度数组的大小是固定的,不能动态扩展。std::vector向量瘦身shrink_to_fit():将容量缩减到和size()一样大小td::forward_listforward_list是单链表(std::list是双链表),当只需要顺序遍历时,forward_list可以节省更多的内存,插入和删除的性能高于liststd::unordered_map和std::unordered_set。hash实现的无序容器,插入、删除和查找的时间复杂度都是O(1)。当不关心容器中元素的顺序时,使用无序容器可以获得更高的性能。6.如何查看内存系统中的内存使用情况:/proc/meminfo进程内存使用情况:/proc/28040/status查询总内存使用情况:free查询进程cpu和内存使用率:top虚拟内存统计:vmstat进程内存使用情况比例和排序:psaux–sort-rss释放系统内存缓存:/proc/sys/vm/drop_caches