什么时候为进程分配内存 什么是内存

如何检查一个进程因缺页而中断的次数?
使用ps -o majflt,minflt -C程序命令查看 。
Majflt代表重大失误,中文名是大失误,minflt代表小失误,中文名是小失误 。
这两个值表示自进程启动以来发生的缺页中断的数量 。
页面中断后执行了哪些操作?
当一个进程因缺页而中断时,该进程将进入内核状态,并执行以下操作:1 .检查要访问的虚拟地址是否合法;2.查找/分配物理页面;3.填写物理页面的内容(读取磁盘,或者直接设置为0,或者什么都不做);4.建立映射关系(虚拟地址到物理地址),重新执行缺页中断的指令 。如果在步骤3中需要读取磁盘,这一次它丢失了 。
内存分配原则
从操作系统的角度来看,进程分配内存有两种方式,由两个系统调用完成:brk和mmap(不考虑共享内存) 。
1.brk是推数据段的最高地址指针_edata( 。数据)到更高的地址;
2.mmap就是在进程的虚拟地址空中找一块空空闲的虚拟内存(堆和栈的中间,称为文件映射区) 。
在这两种方式中,都分配了虚拟内存,但没有分配物理内存 。第一次访问分配的虚拟地址空时,发生页面错误,操作系统负责分配物理内存,然后建立虚拟内存和物理内存的映射关系 。在标准C库中,提供了malloc/free函数来分配和释放内存 。这两个函数的底层是通过brk、mmap、munmap等系统调用来实现的 。
这里有一个例子来说明内存分配的原理:
1.对于malloc小于128k的内存,使用brk分配内存,push _edata到高位地址(只分配虚拟空,不对应物理内存(所以不初始化) 。第一次读写数据时,内核会因为缺页而中断,之后内核会分配相应的物理内存,然后建立虚拟地址空的映射关系 。
1.当进程开始时,其(虚拟)内存空的初始布局如图1所示 。
其中mmap内存映射文件在堆和栈中间(如libc-2.2.93.so,其他数据文件等 。),为了简单起见,省略了内存映射文件 。
_edata指针(在glibc中定义)指向数据段的最高地址 。2.在进程调用A=malloc(30K)之后,memory 空 room如图2所示:
malloc函数会调用brk系统调用,将_edata指针推至高地址30K,完成虚拟内存分配 。
你可能会问:只放_edata 30K就完成内存分配了?
事实是,_edata 30K只完成了虚拟地址的分配,仍然没有这个内存A对应的物理页,当进程第一次读写这个内存A时,发生了缺页中断 。此时内核分配的是这个内存A对应的物理页,也就是说,如果内容A是用malloc分配的,然后一直不访问,那么A对应的物理页就不会被分配 。3.在进程调用B=malloc(40K)之后,memory 空 room如图3所示 。
C/C Linux服务器架构师需要学习后台私有信息“信息”(信息包括C/C、Linux、golang技术、Nginx、ZeroMQ、MySQL、Redis、fastdfs、MongoDB、ZK、流媒体、CDN、P2P、K8S、Docker、TCP/IP、协成、DPDK、ffmpeg等).
二 。对于malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空的空闲内存来分配(对应独立内存,初始化为0),如下图:
4.在进程调用C=malloc(200K)之后,memory 空 room如图4所示:
默认情况下,malloc函数分配内存 。如果请求的内存大于128K(可通过M_MMAP_THRESHOLD选项进行调整),它不会推送_edata指针,而是使用MMAP系统调用从堆和堆栈中间分配一个虚拟内存 。
这主要是因为:
brk分配的内存需要等到高地址内存释放后才能释放(比如B释放前,A释放不了,这就是内存碎片的原因 。见下图何时收紧),而mmap分配的内存可以单独释放 。
当然也有其他的优缺点 。更具体地说,感兴趣的同学可以阅读glibc中的malloc代码 。5.进程调用D=malloc(100K)后,内存空空间如图5所示;6.进程调用free(C)后,C对应的虚拟内存和物理内存一起被释放 。
7.该过程调用free(B)后,如图7所示:
B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针 。如果往后推,D的内存怎么办?
当然,内存B可以重复使用 。如果此时又来了一个40K的请求,malloc很可能会返回内存B. 8 。该过程调用free(D)后,如图8所示:
而b和d连接起来形成一个140K 空的空闲内存 。
9.默认情况下:
当最高地址空之间的空空闲内存超过128K(可通过M_TRIM_THRESHOLD选项调整)时,执行内存紧缩操作(TRIM) 。在free的最后一步,发现最高地址空的空闲内存超过了128K,于是内存变紧,如图9所示 。了解了内存分配的原理之后,再来看一个现象:现象 。
1 .在压力试验过程中,发现被试对象的性能不够理想,具体如下:
进程的系统CPU消耗是20,用户CPU消耗是10,系统空闲大约是70
2 ps -o majflt,minflt -C程序命令显示majflt每秒增量为0,而minflt每秒增量大于10000 。
初步分析
Majflt代表major fault,中文叫大错误,minflt代表minor fault,中文名字叫小错误 。
这两个值表示自进程启动以来发生的缺页中断的数量 。
当进程被页面错误中断时,该进程将进入内核状态并执行以下操作:
检查要访问的虚拟地址是否合法 。
查找/分配物理页面
填充物理页面内容(读取磁盘,或者直接设置为0,或者什么都不做)
建立映射关系(虚拟地址到物理地址)
使用页面错误中断重新执行指令 。
第三步,如果需要读磁盘,那么这个缺页中断就是majflt,否则就是minflt 。
这个进程的minflt这么高,一秒一万多次,我们不得不怀疑和进程的内核cpu消耗有很大关系 。
分析代码
看代码,发现是这样写的:一个请求来了,用malloc分配2M内存,请求结束后释放这个内存 。查看日志,发现分配内存的语句需要10us,一个请求的平均处理时间是1000us 。原因已经找到了!
尽管内存分配语句在处理请求时花费的时间很少,但它会严重影响性能 。要解释清楚原因,首先需要知道内存分配的原理 。
这个案子已经完全澄清了 。
在谈到内存分配的原理之后,被测试模块在内核状态下cpu消耗高的原因就很清楚了:malloc在每次请求到来时都有一个2M内存 。默认情况下,malloc调用mmap来分配内存,当请求完成时,它调用munmap来释放内存 。假设每个请求需要6个物理页面,每个请求将产生6个缺页中断 。在2000的压力下,每秒有超过10000个缺页中断 。这些缺页中断不需要通过读盘来解决,所以称为minflt页面中断是在内核态执行的,所以进程的内核态cpu消耗很大 。页面中断分散在请求的整个处理过程中,因此它表明分配语句时间(10us)与整个请求的处理时间(1000us)相比非常小 。
解决办法
将动态内存改为静态分配,或者在启动时,用malloc分配给各个线程,然后保存在threaddata中 。但是由于这个模块的特殊性,静态分配或者启动分配都是不可行的 。另外,Linux下默认栈的大小限制是10M,所以在栈上分配几米内存是有风险的 。
禁止Malloc调用mmap分配内存,禁止内存收缩 。
当该过程开始时,添加以下两行代码:
mall opt(M _ MMAP _马克斯,0);//禁止malloc调用mmap分配内存
mallopt(M_TRIM_THRESHOLD,-1);//禁止内存压缩 。
效果:添加这两行代码后,用ps命令观察 。压力稳定后,majlt和minflt都为0 。进程的系统状态cpu从20下降到10 。
总结
您可以使用命令ps -o majflt,minflt -C程序来查看进程的majflt和minflt的值 。这两个值是累积值,从过程开始时累积 。当对具有高性能需求的程序进行压力测试时,我们可以将这两个值相加 。
如果一个进程使用mmap将一个大的数据文件映射到进程的虚拟地址空,我们需要重点关注majflt的值,因为与minflt相比,majflt对性能是致命的,随机读取磁盘需要几个毫秒,而minflt只有在数量很多的情况下才会对性能产生影响 。

  • 木星有多可怕?数数木星十大可怕之处 。
  • 孩子肥胖吗?如何通过饮食控制肥胖?
  • 在路上车在路上行驶哪些注意事项(路上行驶手动挡车注意事项)
  • 快评!今天长下影线隐藏的三大含义是什么?
  • 听说万圣节剩下的南瓜灯吃了能降血糖吗?
【什么时候为进程分配内存 什么是内存】

    推荐阅读