linux c学习 day03

内存管理
程序是存在文件中的(硬盘),一个运行的程序是需要加载到内存中的,加载到内存中的程序叫进程。
    STL --> 内存是自动分配和回收
     |
    C++ --> new/delete,会调用malloc和free
     |
   C语言 --> malloc/free
     |
   Unix/Linux系统调用 -> brk/sbrk
     |
   内存映射  -> mmap
     |                         (用户层)
   ------------------------------------
    kmalloc/vmalloc            (内核层)
     |
    get_free_page()



#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
class Sample{
  public:
    int x;
Sample(){cout<<"Sample()"<<endl;}
~Sample(){cout<<"~Sample()"<<endl;}
void show(){cout<<"x="<< x <<endl;}
void* operator new(size_t sz){
 cout<<"new("<<sz<<")"<<endl;
 return malloc(sz);
}
void* operator new[](size_t sz){
 cout<<"new("<<sz<<")"<<endl;
 return malloc(sz);
}
void operator delete(void* p){
 cout << "delete"<<endl;
 free(p);
}
void operator delete[](void* p){
 cout << "delete"<<endl;
 free(p);
}
};



int main(){
  Sample* s = new Sample;
  s->x = 100;
  s->show();
  delete s;
  printf("---------------------\n");
  Sample* s2 = (Sample*)malloc(sizeof(Sample));
  s2->x = 200;
  s2->show();
  free(s2);
  printf("-----------------------\n");
  Sample* s3 = new Sample[5];
  delete[] s3;
  printf("-------------------\n");
  Sample s4[5];
}





虚拟内存技术:
  在Unix/Linux中,用虚拟内存技术管理内存。
  每个进程都有0-4G的虚拟地址空间,虚拟地址必须映射到物理内存后才能使用,否则引发段错误。所谓的内存分配其实就是映射物理内存。程序员所接触到只能是虚拟内存,无法直接接触物理内存。
  0-3G是用户层使用的,叫用户空间,3G-4G是内核使用的,叫内核空间。用户程序只能直接访问用户空间,无法直接访问内核空间,但可以通过Unix/Linux系统提供的系统调用(一系列的函数)进入内核空间。内存管理的最小单位是一个内存页,大小4096(4k)。
  
 进程内存的分区:
  代码区:程序代码(函数)放入代码区,只读区
  全局区:保存全局变量/static变量
  BSS段: 保存未初始化的全局变量
   注:全局区和bss段 在main执行之前都会分配内存,但bss段会自动清0。
  栈区:保存局部变量(非malloc分配),包括函数的参数,内存分配和回收自动进行
  堆区:也叫自由区,内存管理是程序员执行。new/malloc分配的内存。
  注:在代码区附近有一个只读变量区,一般和代码区合并。
  #include <stdio.h>
#include <stdlib.h>
int i1 = 10;//全局
int i2 = 20;//全局
int i3;//bss
static int i4 = 40;//全局
const int i5 = 50;//代码(只读变量)
void fa(int i6){//栈
  int i7 = 70;//栈
  static int i8 = 80;//全局
  const int i9 = 90;//栈
  int *pi = malloc(4);//堆
  char * str1 = "abc";//只读变量
  char str2[] = "abc";//栈
  printf("&i6=%p\n",&i6);
  printf("&i7=%p\n",&i7);
  printf("&i8=%p\n",&i8);
  printf("&i9=%p\n",&i9);
  printf("pi=%p\n",pi);
  printf("str1=%p\n",str1);
  printf("str2=%p\n",str2);
}
int main(){
  printf("&i1=%p\n",&i1);
  printf("&i2=%p\n",&i2);
  printf("&i3=%p\n",&i3);
  printf("&i4=%p\n",&i4);
  printf("&i5=%p\n",&i5);
  fa(100);
  printf("fa=%p\n",fa);//函数地址,代码区
  while(1);
  return 0;
  
}


  段错误的原因:
   1 虚拟内存没有映射物理内存就使用
   2 操作某些没有权限的内存区域(修改只读区)
   3 释放内存时缺少必要的附加信息
  malloc的特点:
   1 申请小内存时(不足33个内存页),默认映射33个内存页。用完后再申请不一再给33个内存页。
   2 申请大内存时,映射稍多一点的内存页。
   3 申请内存时,会额外多分配一点内存,用于记录相关信息。
   注:malloc不一定映射新的物理内存,free也不一定真正释放物理内存。
   进程之间同样的虚拟地址对应 不同的物理内存。

#include <string.h>

#include <stdio.h>


int main(){
  char * str1 = "abcd";
  char str2[] = "abcd";
  str1 = "1234";//改变str1的指向,而不是改"abcd"
  //str1[0] = '1';//改只读区 段错误
  //str2 = "1234"; //错误
  strcpy(str2,"1234");
  printf("str1=%s\n",str1);
  printf("str2=%s\n",str2);
  int size = getpagesize();
  printf("size=%d\n",size);

}


#include <stdio.h>
#include <stdlib.h>


int main(){
  int a,b,c,d;
  printf("a=%p,b=%p,c=%p,d=%p\n",&a,&b,&c,&d);
  int * pi = malloc(4);//分配33个内存页
  *pi = 100;
  //*(pi+1) = 200;//内存不稳定(数据无保障)
  int *pi2 = malloc(4);//内存稳定(数据有保障)
  int *pi3 = malloc(4);//malloc会附加一些内存
  int *pi4 = malloc(4);//附加内存记录信息
  printf("*pi=%d\n",*pi);
  //printf("*(pi+1)=%d\n",*(pi+1));
  printf("pid=%d\n",getpid());//取进程id
  printf("pi=%p\n",pi);
  printf("pi2=%p\n",pi2);
  printf("pi3=%p\n",pi3);
  printf("pi4=%p\n",pi4);
  *(pi+1) = 100; *(pi+2)=200;*(pi+3)=300;
  free(pi);
  free(pi2);
  free(pi3); free(pi4);
  while(1);
}

#include <stdio.h>
#include <stdlib.h>
int main(){
  int x = 100;
  int *p = malloc(4);
  *p = 200;
  printf("&x=%p\n",&x);
  printf("p=%p\n",p);
  while(1);
}

#include <stdio.h>


int main(){
  int *p = 0x94fe008;//0xbf808e9c;
  printf("*p=%d\n",*p);
}





#include <stdio.h>
#include <stdlib.h>


int main(){
  int *pi = malloc(4096*2);
  int *pi2 = malloc(4096*30);
  int *pi3 = malloc(4097);
  printf("pi=%p\n",pi);
  printf("pi2=%p\n",pi2);
  printf("pi3=%p\n",pi3);
  printf("pid=%d\n",getpid());
  sleep(20);
  printf("sleep over\n");
  free(pi); free(pi2); free(pi3);
  while(1);
}






  brk/sbrk 函数是系统提供分配内存/回收内存的。
  一般用sbrk分配内存,用brk回收。
  void* sbrk(int size)
  size == 0 取当前位置
  size >0 分配内存,并返回之前的位置
  size <0 释放内存
  
  int brk(void* p)
  p就是指定位置,返回-1代表错误


  brk/sbrk 底层需要维护一个位置
#include <stdio.h>
#include <unistd.h>


int main(){
  void* p = sbrk(0);
  void* p1 = sbrk(4);//以一个内存页作为映射的单位
  void* p2 = sbrk(4);
  void* p3 = sbrk(4);void* p4 = sbrk(4);
  printf("p=%p,p1=%p,p2=%p,p3=%p,p4=%p\n",
p,p1,p2,p3,p4);
  printf("pid=%d\n",getpid());
  sbrk(-4);sbrk(-8);//释放内存
  p = sbrk(0);
  printf("p=%p\n",p);
  sleep(20);
  printf("all free\n");
  sbrk(-4);
  while(1);

}


#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
  void* p = sbrk(0);//取位置
  int r = brk(p+4);//分配了4个
  if(r == -1) perror("brk");
  brk(p+8);//再次分配4个
  brk(p+4);//释放了4个
  int *pi = p;
  *pi = 100;
  char *str = sbrk(0);
  brk(str+10);
  strcpy(str,"abcdef");
  printf("int=%d,str=%s\n",*pi,str);
  brk(p);//全部释放
}//练习:改良代码,用sbrk分配内存,用brk释放

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
  void *p = sbrk(4);//分配内存,同时返回起始位置
  int *pi = p;
  *pi = 100;
  char * str = sbrk(10);
  strcpy(str,"abcdef");
  printf("int=%d,str=%s\n",*pi,str);
  brk(p);
}//一般情况下,sbrk分配内存,brk释放内存






  mmap用于映射虚拟内存地址,可能用物理内存/文件
   void* mmap(void* addr,size_t size,int prot,
   int flags,int fd,off_t offset)
   返回虚拟内存的首地址;addr可以指定首地址,一般NULL交给内核指定。size是映射大小,以内存页为单位。prot权限,flags指定配置,fd文件描述符,offset偏移量。

 munmap 解除映射

#include <stdio.h>
#include <sys/mman.h>
#include <string.h>


int main(){
  void* p = mmap(0,//内核指定首地址
4,//大小4个字节,会扩充到1页4k
PROT_READ|PROT_WRITE,//|经常用于连接多个选项
MAP_PRIVATE|MAP_ANONYMOUS,//映射物理内存
    0,0 //文件描述符和文件偏移量
  );
  if(p == MAP_FAILED){perror("mmap");return -1; }
  int *pi = p;
  int i;
  for(i=0;i<50;i++){
    pi[i] = i+100;  
  }
  char * str = p+250;
  strcpy(str,"abcdefgh");
  char* ch = p;
  for(i=0;i<300;i++){
if(i%10==0) printf("\n");
printf("%d ",ch[i]);
  }
  munmap(p,4);//取消映射
}


 
 系统调用 就是 内核对外的接口。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值