【操作系统】LinuxFedora13当new一个新内存空间的时候操作系统如何分配

来源:互联网 发布:pcm-d50淘宝 编辑:程序博客网 时间:2024/05/16 04:54

C和C++程序上的内存分配释放函数如下:


cat /proc/pids/maps命令


cat /proc/pid/status命令


====================================================================================

首先尝试申请一个128MB空间,观察maps和status文件的变化。

#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p[8];char s[8];int pid = getpid();sprintf(s,"%d",pid);cout<<"分配内存前的maps和status情况"<<endl;maps(s);status(s);cout<<"先分配1个128MB的内存"<<endl;//一个int占4个字节//128MB = 128 * 1024 KB = 131072 KB//128MB = 128 * 1024 * 1024 字节(byte) = 134217728 Byte//int数组长度为 128 * 1024 * 1024 / 4p[1] = new int[33554432];maps(s);status(s);delete[] p[1];return 0;}
创建128MB内存前后程序执行命令cat /proc/pid/maps,发现maps信息中有一个地址段发生变化。




b7848000 (16) – af847000 (16) = 134221824(10) =128MB + 4KB

创建128MB内存程序前后执行命令cat/proc/pid/status,发现status信息有变化


VmPeak进程地址空间大小增加了131016KB  = 128MB– 56KB

VmSize进程虚拟地址空间的大小增加了131076KB= 128MB + 4KB

VmHWM程序得到分配到物理内存的峰值,文件内存映射和匿名内存映射的大小增加了36KB

VmRSS应用程序正在使用的物理内存的大小增加了36KB

VmData程序数据段的大小增加了131076KB= 128MB + 4KB

VmPTE该进程的页表的大小增加了4KB

voluntary_ctxt_swicthes进程主动切换的次数增加了2次


② 连续申请分配六个128MB空间(记为1~6号),然后释放第2、3、5号的128MB空间。

<span style="font-size:12px;">#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p[8];char s[8];int pid = getpid();sprintf(s,"%d",pid);cout<<"分配内存前的maps和status情况"<<endl;maps(s);status(s);cout<<"先分配6个128MB的内存,然后删除235号"<<endl;//一个int占4个字节//128MB = 128 * 1024 KB = 131072 KB//128MB = 128 * 1024 * 1024 字节(byte) = 134217728 Byte//int数组长度为 128 * 1024 * 1024 / 4p[0] = new int[33554432];p[1] = new int[33554432];p[2] = new int[33554432];p[3] = new int[33554432];p[4] = new int[33554432];p[5] = new int[33554432];delete[] p[1];delete[] p[2];delete[] p[4];maps(s);status(s);delete[] p[0];delete[] p[3];delete[] p[5];return 0;}</span><span style="font-size: 24px;"></span>

6号128MB内存段(存在):

8f781000(16) – 87780000(16)= 134221824(10) B = 128MB + 4KB

5128MB内存段(释放):

97782000(16) – 8f781000(16)= 134221824(10) B = 128MB + 4KB

4号128MB内存段(存在):

9f783000(16) – 97782000(16)= 134221824(10) B = 128MB + 4KB

3号和2128MB内存段(释放):

af785000(16) - 9f783000(16)= 268443648(10) B = 2 * (128MB + 4KB)

1号128MB内存段(存在):

b7788000(16) – af785000(16)= 134230016(10) B = (128MB + 4KB) + 8MB

存在的内存段:

6号128MB内存段:87780000-8f781000

4号128MB内存段:97782000-9f783000

1号128MB内存段:af785000-b7788000

VmPeak进程地址空间大小增加了786396KB  = 128 MB*6 – 36 KB

VmSize进程虚拟地址空间的大小增加了393228KB = 128 MB * 3 + 12KB

VmHWM文件内存映射和匿名内存映射的大小增加了56KB

VmRSS应用程序正在使用的物理内存的大小增加了44KB

VmData程序数据段的大小增加了393228KB = 128 MB * 3 + 12KB

VmPTE该进程的页表的大小增加了20KB

voluntary_ctxt_swicthes进程主动切换的次数增加了2次


③ 连续申请分配六个128MB空间(记为1~6号),释放第2、3、5号的128MB空间,然后再分配1024MB。

<span style="font-size:12px;">#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p[8];char s[8];int pid = getpid();sprintf(s,"%d",pid);cout<<"分配内存前的maps和status情况"<<endl;maps(s);status(s);cout<<"先分配6个128MB的内存,然后删除235号"<<endl;//一个int占4个字节//128MB = 128 * 1024 KB = 131072 KB//128MB = 128 * 1024 * 1024 字节(byte) = 134217728 Byte//int数组长度为 128 * 1024 * 1024 / 4p[0] = new int[33554432];p[1] = new int[33554432];p[2] = new int[33554432];p[3] = new int[33554432];p[4] = new int[33554432];p[5] = new int[33554432];delete[] p[1];delete[] p[2];delete[] p[4];//1024MB = 128MB * 8cout<<"分配一个1024MB内存..."<<endl;p[6] = new int[33554432*8];maps(s);status(s);delete[] p[0];delete[] p[3];delete[] p[5];delete[] p[6];return 0;}</span><span style="color: rgb(255, 0, 0);font-size:24px;"></span>

6号128MB内存段和1024MB内存段(存在):

8f8dd000 – 478db000 = 1207967744= 128MB + 4KB + 1024MB + 4 KB

5128MB内存段(已释放)

978de000 – 8f8dd000 =134221824 = 128MB + 4KB
4号128内存段(存在):

9f8df000 – 978de000 = 134221824= 128MB + 4KB

3号和2128MB内存段(已释放)

af8e1000 – 9f8df000 =268443648 = 2 * (128MB + 4KB)

1号128MB内存段(存在):

b78e4000 – af8e1000 = 134230016= (128MB + 4KB) + 8MB

其它变量:

b78f3000 – b78e4000 = 61440 =60KB

b78f5000 – b78f3000 = 8192 = 8KB


VmPeak进程地址空间大小增加了

1441748KB  = 128 MB *3 +1024MB– 44 KB

VmSize进程虚拟地址空间的大小增加了

1441808KB = 128 MB * 3 +1024MB + 16KB

VmHWM程序得到分配到物理内存的峰值,文件内存映射和匿名内存映射的大小增加了56KB

VmRSS应用程序正在使用的物理内存的大小增加了48KB

VmData程序数据段的大小增加了

1441808KB = 128 MB * 3 +1024MB + 16KB

VmPTE该进程的页表的大小增加了24KB

voluntary_ctxt_swicthes进程主动切换的次数增加了2次


④ 比较三次结果:


推测:再添加64MB,这64MB会放在128MB+1024MB的内存段后面。


⑤ 连续申请分配六个128MB空间(记为1~6号),释放第2、3、5号的128MB空间,分配1024MB。再分配64M内存

#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p[8];char s[8];int pid = getpid();sprintf(s,"%d",pid);cout<<"分配内存前的maps和status情况"<<endl;maps(s);status(s);cout<<"先分配6个128MB的内存,然后删除235号"<<endl;//一个int占4个字节//128MB = 128 * 1024 KB = 131072 KB//128MB = 128 * 1024 * 1024 字节(byte) = 134217728 Byte//int数组长度为 128 * 1024 * 1024 / 4p[0] = new int[33554432];p[1] = new int[33554432];p[2] = new int[33554432];p[3] = new int[33554432];p[4] = new int[33554432];p[5] = new int[33554432];delete[] p[1];delete[] p[2];delete[] p[4];//1024MB = 128MB * 8cout<<"分配一个1024MB内存..."<<endl;p[6] = new int[33554432*8];//64MB = 128MB / 2cout<<"分配一个64MB的内存..."<<endl;p[7] = new int[33554432/2];maps(s);status(s);delete[] p[0];delete[] p[3];delete[] p[5];delete[] p[6];delete[] p[7];return 0;}


6号128MB内存段+1024MB内存段

8f709000 – 47707000 = 1207967744= 128MB + 4KB + 1024MB + 4 KB

4号128MB内存段

9f70b000 – 9770a000 = 134221824= 128MB + 4KB

1号128MB内存段+ 64MB内存段

b7710000 – ab70c000 = 201342976= 128MB + 8KB +64MB + 8KB

由此发现前面的推测错误,实际上是使用首次适应算法来创建新的内存段。

1. 因为1号和4号内存段之间有2、3号两个128MB的内存段,总计256MB内存段。4号到6号之间有5号一个内存   段,共128MB内存段。

2. 由于1024MB大于23号的256MB也大于5号的128MB,所以放在6号之后了。

3. 而64MB在碰到23号的256MB刚好合适,所以就直接放入这块了。

4. 如果是最佳适应算法64MB应该是放在5号的128MB内存段,而不是23号的256MB。


VmPeak进程地址空间大小增加了

1507288 KB  = 128 MB *3+1024MB + 64MB – 40 KB

VmSize进程虚拟地址空间的大小增加了

1507348 KB = 128 MB * 3 +1024MB + 64MB + 20KB

VmHWM程序得到分配到物理内存的峰值,文件内存映射和匿名内存映射的大小增加了56 KB

VmRSS应用程序正在使用的物理内存的大小增加了52 KB

VmData程序数据段的大小增加了

1507348 KB = 128 MB * 3 +1024MB + 64MB + 20KB

VmPTE该进程的页表的大小增加了28 KB

voluntary_ctxt_swicthes进程主动切换的次数增加了2次

四次结果进行比较:


结论:


====================================================================================

⑥ 设计一个程序测试出你的系统单个进程所能分配到的最大虚拟内存空间为多大。

#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p[10000] = { NULL };char s[8];int pid = getpid();sprintf(s,"%d",pid);cout<<"分配内存前的maps和status情况"<<endl;maps(s);status(s);int i = 0;try{do{p[i] = new int[33554432];}while(i<10000 && p[i++]!=NULL);}catch(std::bad_alloc&){cout<<"耗光虚拟内存后的maps和status情况 "<<i<<endl;maps(s);status(s);int max = i-1;for(i=-1;++i<max;){delete[]p[i];}}return 0;}

VmPeak进程地址空间大小增加了

2885660KB  ≈2.751979827880859375 GB

VmSize进程虚拟地址空间的大小增加了

2884696KB  ≈ 2.75106048583984375GB

VmHWM程序得到分配到物理内存的峰值,文件内存映射和匿名内存映射的大小增加了212KB

VmRSS应用程序正在使用的物理内存的大小增加了212KB

VmData程序数据段的大小增加了

2884696KB = 2.75106048583984375 GB

VmPTE该进程的页表的大小增加了88KB

voluntary_ctxt_swicthes进程主动切换的次数增加了2次

 

分配到的虚拟内存空间的最大峰值VmPeak是:

2888780 KB ≈2.754955291748046875 GB

实际上使用的虚拟内存大小VmSize是

2887756 KB ≈2.753978729248046875 GB

所以当前系统,单个进程所能分配到的最大虚拟内存空间约为 2.75 GB

而该Linux虚拟机的设定内存大小是2 GB

====================================================================================

⑦ 编写一个程序,分配256MB内存空间(或其他足够大的空间),检查分配前后/proc/pid/status文件中关于虚拟内存和物理内存的使用情况,然后每隔4KB间隔将相应地址进行读操作,再次检查/proc/pid/status文件中关于内存的情况,对比前后两次内存情况,说明所分配物理内存(物理内存块)的变化。然后重复上面操作,不过此时为写操作,再观察其变化。

一、读操作

<span style="font-size:12px;">#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p;char s[8];int pid = getpid();sprintf(s,"%d",pid);//一个int占4个字节//128MB = 128 * 1024 KB = 131072 KB//128MB = 128 * 1024 * 1024 字节(byte) = 134217728 Byte//int数组长度为 128 * 1024 * 1024 / 4//256MB = 128M * 2cout<<"分配256MB内存,看maps和status的情况..."<<endl;int sum = 33554432 * 2;p = new int[sum];status(s);cout<<"进行读操作"<<endl;//相隔4KB进行读写就是相隔1024个int进行读写int i  = 0;for(;i<sum;i += 1024){p[i];}cout<<endl;status(s);delete[] p;return 0;}</span><span style="font-size: 24px;"></span>

发现Vm内存没有变化。voluntart_ctxt_switches加1.

二、写操作:

#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}int main(){int *p;char s[8];int pid = getpid();sprintf(s,"%d",pid);//一个int占4个字节//128MB = 128 * 1024 KB = 131072 KB//128MB = 128 * 1024 * 1024 字节(byte) = 134217728 Byte//int数组长度为 128 * 1024 * 1024 / 4//256MB = 128M * 2cout<<"分配256MB内存,看maps和status的情况..."<<endl;int sum = 33554432 * 2;p = new int[sum];status(s);cout<<"进行写操作"<<endl;//相隔4KB进行读写就是相隔1024个int进行读写int i  = 0;for(;i<sum;i += 1024){p[i] = i;}cout<<endl;status(s);delete[] p;return 0;}

VmHWM程序得到分配到物理内存的峰值,文件内存映射和匿名内存映射的大小增加了261900KB =256 MB – 244 KB

VmRSS应用程序正在使用的物理内存的大小增加了261900KB

VmPTE该进程的页表的大小增加了508KB

voluntary_ctxt_swicthes进程主动切换的次数增加了1次

nonvoluntary_ctxt_switches进程被动切换的次数增加了32次

将写操作指向的地址输出来:


添加以下代码:

void statusvmHWM(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[] = {"/status | grep VmRSS"};strcat(str,l);system(str);}
修改成以下代码:
int i  = 0;for(;i<sum;i+=1024){p[i] = 1;statusvmHWM(s);sleep(1);}

运行结果:



发现每写一次,VmRSS增加4KB,VmHWM也一样,它们最终的值也一样。

修改循环,间隔4096int,即16KB的间隔进行读取。

for(;i<sum;i+=4096){ /*…*/ }

运行结果:


VmHWM程序得到分配到物理内存的峰值,文件内存映射和匿名内存映射的大小增加了65544KB =64 MB + 8 KB

VmRSS应用程序正在使用的物理内存的大小增加了65544KB

VmPTE该进程的页表的大小增加了508KB

voluntary_ctxt_swicthes进程主动切换的次数增加了1次

nonvoluntary_ctxt_switches进程被动切换的次数增加了12次

====================================================================================

⑧ 编写并运行(在第5步的程序未退出前)另一进程,分配等于或大于物理内存的空间,然后每隔4KB间隔将相应地址的字节数值增1,此时再查看前一个程序的物理内存变化,观察两个进程竞争物理内存的现象。

#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <time.h>using namespace std;//命令 cat /proc/pid/mapsvoid maps(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/maps"};strcat(str,l);cout<<str<<endl;system(str);}//命令 cat /proc/pid/statusvoid status(char s[8]){char str[100] = {"cat /proc/"};strcat(str,s);char l[8] = {"/status"};strcat(str,l);cout<<str<<endl;system(str);}void statusvmHWM(char s[8]){char str[100] = {"echo '('$(date +%H:%M:%S)')' $(cat /proc/"};strcat(str,s);char l[] = {"/status | grep VmHWM)"};strcat(str,l);system(str);}int main(){int *p;char s[8];int pid = getpid();sprintf(s,"%d",pid);int sum = 2.74 * 1024 * 1024 * 1024 / 4;p = new int[sum];//相隔4KB进行读写就是相隔1024个int进行读写int i  = 0;for(;i<sum;i += 1024){p[i]++;}delete[] p;return 0;}

先运行本题程序再运行五题程序

本题程序输出结果


nonvoluntary_ctxt_switches进程被动切换的次数增加了1394

第五题程序(没有进程竞争)和本题程序(有进程竞争)的比较:


nonvoluntary_ctxt_switches进程被动切换的次数多了1383

==================================================================

⑨ 分配足够大的内存空间,其容量超过系统现有的空闲物理内存的大小,1)按4KB的间隔逐个单元进行写操作,重复访问数遍(使得程序运行时间可测量);2)与前面访问总量和次数不变,但是将访问分成16个连续页为一组,逐组完成访问,记录运行时间。观察系统的状态,比较两者运行时间,给出判断和解释。

代码一、

#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <sys/time.h>using namespace std;int main(){timeval starttime,endtime;int *p;char s[8];int pid = getpid();sprintf(s,"%d",pid);int sum = 2.74 * 1024 * 1024 * 1024 / 4;p = new int[sum];//相隔4KB进行读写就是相隔1024个int进行读写int i,j;gettimeofday(&starttime,0);for(i=0;i<sum;i += 1024){p[i]++;}gettimeofday(&endtime,0);double timeuse = 1000000*(endtime.tv_sec - starttime.tv_sec) + endtime.tv_usec - starttime.tv_usec;timeuse /=2000;//mscout<<timeuse<<"ms"<<" = "<<timeuse/1000<<"s"<<endl;delete[] p;return 0;}
代码二、
#include <iostream>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <pthread.h>#include <sys/time.h>using namespace std;int sum = 2.74 * 1024 * 1024 * 1024 / 4;int *p  = new int[sum];;int a = 0;void* visit(void* arg){int i = *(int *)arg;int end = i + sum/16;for(;i < end;i += 1024){++p[i];}++a;}int main(){timeval starttime,endtime;pthread_t id[16];int i,ret;int pc[16];int x = sum /16;for(i=-1;++i<16;){pc[i] = i * x;}char s[8];int pid = getpid();sprintf(s,"%d",pid);cout<<"进入计时状态"<<endl;gettimeofday(&starttime,0);for(i=-1;++i<16;){ ret=pthread_create(&id[i],NULL,visit,&pc[i]);}while(a!=16){}gettimeofday(&endtime,0);cout<<"结束计时状态"<<endl;double timeuse = 1000000*(endtime.tv_sec - starttime.tv_sec) + endtime.tv_usec - starttime.tv_usec;timeuse /=1000;//mscout<<timeuse<<"ms"<<" = "<<timeuse/1000<<"s"<<endl;delete[] p;return 0;}
代码一结果:

代码二结果:

代码1是顺序访问数组,代码2是将数组切割成十六份,每份由一个线程负责访问,总共十六个线程。

代码2比代码1慢。



0 0