一.关于虚拟内存
问题:
一个程序不能访问另外一个程序的地址指向的空间.
理解:
1.每个程序的开始地址ox8048000(?可由objdump 反汇编得到)
2.程序中使用的地址不是物理地址,而是逻辑地址(虚拟内存).
逻辑地址仅仅是编号.编号使用int 4字节整数表示.
4294967296=4G
每个程序提供了4G的访问能力(32位机,下同)
问题:
逻辑地址与物理地址关联才有意义:过程称为内存映射.
背景:
虚拟内存的提出:禁止用户直接访问物理存储设备.
有助于系统的稳定.
结论:
虚拟地址与物理地址映射的时候有一个基本单位:至少会映射4K
4k 1000 内存页.
段错误:无效访问. 那段内存没有映射
合法访问:比如malloc分配的空间之外的空间可以访问,但访问非法.因是越界访问
内存访问分两种:一个是可以访问,但不一定是合法的,比如malloc几个字节,
内存会给你映射4K空间,int*p=malloc(0); *(p+1000)=9999;理论说这是可以访问,但是非法的,它可能破坏维护malloc分配的数据结构,就跟虚表指针一样。
二.虚拟内存的分配
栈:编译器自动生成代码维护
堆:地址是否映射?映射的空间是否被管理?
1.brk/sbrk内存映射函数
分配释放内存:
int brk(void *end);//分配空间,释放空间
void *sbrk(int size);//返回空间地址
应用:
1.使用sbrk分配空间
2.使用sbrk得到没有映射的虚拟地址.
第一次调用sbrk,sbrk(0)得到的是没有映射的虚拟首地址。
3.使用brk分配空间
4.使用brk释放空间
理解:
sbrk(int size)
如果是第一次运行,则返回没有映射的空闲空间首地址,同时产生一个数据:指向地址
sbrk与brk后台系统维护一个指针.
指针默认是null.
调用sbrk,判定指针是否是0,
是:得到大块空闲空间的首地址初始化指针.同时把指针+size
否:返回指针,并且把指针位置+size
demo:
#include <stdio.h>
#include <unistd.h>
main()
{
int *p=sbrk(0); //返回的是空闲空间的首地址,然后系统给映射一个页
//*p=800; //段错误
int *p1=sbrk(4);//返回空闲地址,然后系统给映射4K 并修改指针为+size
//先看一下指针是否为空,如果是空,就返回空闲空间的首地址,
//然后再看里面的参数是几个字节,再看一下这几个字节是否有空间,
//没空间的话就映射空间,然后把指针+4
*(p1+10)=4000; //可以访问,反正系统给映射了4K空间大小,但是是非法的
int *p2=sbrk(0); //返回的是首地址+4
p2=sbrk(200);
int *p3=sbrk(0); //得到的地址是首地址+204
int *p4=sbrk(-4);//释放4个字节的空间
int *p5=sbrk(-4);
printf("%p\n",p);
printf("%p\n",p1);
printf("%p\n",p2);
printf("%p\n",p3);
printf("%p\n",p4);
printf("%p\n",p5);
printf("%d\n",getpid());
/*
int *p=sbrk(0);
brk(p+1);//brk改变的是绝对位置
*p=800;
*/
brk(p);
//*p=99;//段错误
//while(1);
}
/*输出:
0x8909000
0x8909000
0x8909004
0x89090cc
0x89090cc
0x89090c8
15945
*/
应用案例:
写一个程序查找1-10000之间所有的素数.
并且存放到缓冲,然后打印.
缓冲的实现使用sbrk/brk
流程:
循环
判定是否素数(isPrimer)
是,分配空间存放
不是,继续下步.
#include <stdio.h>
#include <unistd.h>
int isPrimer(int a)
{
int i;
for(i=2;i<a;i++){
if(a%i==0)
return 1;
}
return 0;
}
main()
{
int i=2;
int b;
int *r;
int *p;
p=sbrk(0);
r=p;
for(;i<100;i++){
b=isPrimer(i);
if(b==0){
brk(r+1);
*r=i;
r=sbrk(0);
}
}
i=0;
r=p;
while(r!=sbrk(0)){
printf("%d\n",*r);
r++;
}
brk(p);//free
}
2.mmap/munmap内存映射函数
没有任何额外维护数据的内存分配。
mmap(分配)/unmap(释放)
1.函数说明
void *mmap(
void *start,//指定映射的虚拟地址 0由系统指定开始位置)
size_t length,//映射空间大小pagesize倍数
int prot,//映射权限 PROT_NONE | PROT_READ PROT_WRITE PROT_EXEC
intflags,//映射方式
int fd,//文件描述符号
offset_t off);//文件中的映射开始位置(必须是pagesize的倍数)
映射方式:
内存映射:匿名映射。
文件映射:映射到某个文件
只有文件映射最后两个参数有效。
MAP_ANONYMOUS
MAP_SHARED MAP_PRIVATE(二选一)
2.demo
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
main()
{
int *p=mmap(
NULL,
getpagesize(),
PROT_READ,
MAP_ANONYMOUS|MAP_SHARED,
0,0);
*p=20;
*(p+1)=30;
*(p+2)=40;
printf("%d\n",p[2]);
munmap(p,4096);
}
三.总结:
智能指针(指针池)
STL
new
malloc (小而多数据)
brk/sbrk (同类型的大块数据,动态移动指针)
mmap/munmap(控制内存访问/使用文件映射/控制内存共享)
异常处理
int brk(void*)
void *sbrk(int);
如果成功.brk返回0 sbrk返回指针
失败 brk返回-1 sbrk返回(void*)-1