嵌入式学习之路(二十四)——UC高级(2)
来源:互联网 发布:apache进程几十兆 编辑:程序博客网 时间:2024/04/29 15:42
嵌入式学习之路(二十四)——UC高级(2)
一.Unix/Linux的内存管理1. 相关函数
1.1 STL ---> 也是自动管理内存的
1.2 C++ ---> 的new和delete(运算符号)
1.3 C语言--> malloc()/free()
1.4 Unix系统函数 -->sbrk()和brk()
1.5 Unix 系统函数 → mmap()/munmap()
2. 一个进程的内存空间
2.1 程序和进程的概念:
2.1.1. 程序是 保存在硬盘上的可执行文件
2.1.2. 进程是 运行在内存中的程序
2.1.3. 链接的成品是程序,运行起来的程序就叫进程
2.2 进程的内存空间(从小到大的顺序)
2.2.1. 代码区:存放函数的代码,只读区,函数指针就是函数在代码区的地址
2.2.2. 只读常量区:存放字符串字面值、const修饰的全局变量
很多人都把他们合一起,因为他们离得很近,大家可以选择自己喜欢的去记
2.2.3. 全局区:存放全局变量和static修饰的变量
2.2.4. BSS段:存放 没有初始化的全局变量,main函数执行之前,自动清零bss段
2.2.5. 栈区:存放局部变量,包括函数的形参,栈区的内存管理是系统自动完成的
2.2.6. 堆区:也叫自由区,new,delete,malloc,free在堆区扽坏和回收,有程序员管理
3. Unix/Linux内存的管理机制
下面这几点,本人感觉还是挺重要的,大家好好理解,记住
3.1 Unix/Linux采用虚拟内存机制管理内存
3.1.1. 每个进程都先天具备0到4G的虚拟内存地址空间
3.1.2. 虚拟内存地址 其实就是一个整数,本身不对应任何的物理内存/硬盘
因此虚拟内存地址先天存在,但先天不能存数据
3.1.3. 虚拟内存地址 映射物理内存/硬盘才能真正存储数据
3.1.4. 这个映射的过程就是内存分配
3.1.5. 程序员只能接触到虚拟内存地址,而不能接触物理内存的真实地址
3.2 虚拟内存分为两块,用户空间(0-3G)和内核空间(3-4G)
程序员只能直接访问用户层,用户层不能直接访问内核层
3.3 内存的管理单位是字节,内存映射的基本单位是内存页,
一个内存页多大呢?我们可以用过getpagesize()来获得当前系统是多少字节
映射一次,必须映射内存页的整数倍
3.4 如果没有映射物理内存/硬盘文件就直接使用虚拟内存地址,会引发段错误
4. 内存的查看
4.1 我们知道,在Linux系统中,几乎一切都被看成了文件
目录,内存设备都看成了文件
4.2 每个进程的内存都在/proc/进程id/maps下可以查看内存页
5. 下面我们来看一个程序,看一下内存空间的分配
/** 内存空间分区演示**/#include <stdio.h>#include <stdlib.h>int i1 = 1;/*全局*/int i2;/*bss段*/static int i3 = 3;/*全局*/const int i4 = 4;/*只读常量区*/void fa(int i5)/*栈区*/{ int i6 = 6;/*栈区*/ static int i7 = 7;/*全局*/ const int i8 = 8;/*栈区*/ int *pi = malloc(4);/*堆区*/ char *s1 = "abc";/*s1指向只读常量区*/ char s2[] = "abc";/*栈区,把"abc的值复制在栈"*/ printf("i5 = %p\n",&i5); printf("i6 = %p\n",&i6); printf("i7 = %p\n",&i7); printf("i8 = %p\n",&i8); printf("pi = %p\n",pi); printf("s1 = %p\n",s1); printf("s2 = %p\n",s2);}int main(){ printf("i1 = %p\n",&i1); printf("i2 = %p\n",&i2); printf("i3 = %p\n",&i3); printf("i4 = %p\n",&i4); printf("fa = %p\n",fa);/*打印函数地址,代码区*/ fa(10); printf("pid = %d\n",getpid()); while(1);/*用cat /proc/pid/maps可以直接查看*/}
大家都可以看注释理解变量定义在哪一个区,也可以看看内存页的情况
6. 接下来我们进入真正的正题malloc()函数6.1 malloc()分配的是堆区的内存,malloc()分配的内存必须由free()释放
否则就等到进程结束
6.2 malloc()其实一次映射了33个内存页,如果一次申请了内存超过33个内存页
会分配比申请的内存多一点的内存页,具体多几页我们不知道
6.3 malloc()在分配内存时,会额外存储一些附加信息,比如说分配大小
int *p = malloc(4);
free(p);
int *p = malloc(8);
free(p);
这里我们知道,free()怎么知道要回收多少字节?所以会附加一些信息
6.4 所以我们得出一个结论,malloc()分配的内存,地址是不连续的,要存附加信息
6.5 free()只是释放虚拟地址内存,未必解除内存的映射,最后33个内存页free()不会解除
6.6 free()其实就是把已经使用的虚拟地址的状态改成未使用
不一定会解除与无力内存/硬盘文件的映射(最后33页不会解除)
6.7 经验之谈:
6.7.1. 在应用的时候,只需要分配内存用malloc(),释放内存用free();
6.7.2. malloc()分配内存的时候,不要越界使用,否则会毁坏附加数据,影响free();
6.8 malloc()演示程序
/* malloc演示*/#include <stdio.h>#include <stdlib.h>int main(){ printf("pid =%d\n",getpid()); int a,b,c,d; printf("%p %p %p %p\n",&a,&b,&c,&d); int *p1 = malloc(4);//malloc映射了33内存页 int *p2 = malloc(4);//第二次不映射,只是分配 int *p3 = malloc(4);//第三次也不映射,只是分配 printf("p1=%p,p2=%p,p3=%p\n",p1,p2,p3); //*(p1+100) = 50;//没有超过,所以能用 //printf("%d\n",*(p1+100)); // *(p1+1024*33-1) = 50;//已经超过33页,所以越界 //printf("%d\n",*(p1+1024*33-1)); //*(p1+1024*33-4) = 50;//没有超过33页,所以越界 //printf("%d\n",*(p1+1024*33-4)); free(p3); free(p2); free(p1); sleep(1); int *p4 = malloc(1); printf("p4 =%p\n",p4); free(p4); sleep(1); while(1); return 0;}
7. sbrk()------Unix的系统函数(windows下面不能使用)
7.1 sbrk()和brk()函数本身都同时具备分配和回收的内存的功能7.2 但是呢?sbrk()分配内存更方便,brk()回收内存更方便
7.3 他们都是通过底层维护的位置进行内存的分配和回收
7.4 void *sbrk(int increment)
参数:increment 就是内存的大小
正数代表分配increment字节内存
负数代表回收increment 字节内存
0代表既不回收也不分配,获取当前的位置
返回值:返回移动之前的位置,对increment是负数没有意义,因为已经释放掉了
7.5 sbrk()/brk()映射内存都是一页
7.6 sbrk()/brk()在分配内存时,一一个内存页为基本单位,超了就多一页,释放了就少一页
全部释放就没有内存页,映射也随之接触,这和malloc是不一样的
8. brk()-------Unix的系统函数
8.1 int brk(void *new);
brk()就是位置移动到new这个位置
成功返回0,失败返回-1
8.2 sbrk()分配内存
brk()回收内存
sbrk()/brk()程序演示
/*
合理使用brk()和sbrk()函数演示
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int *pi = sbrk(4);//int是4个字节的
double *pd = sbrk(8);//double是8个字节的
char *pst = sbrk(10);//10个char是10个字节的
*pi = 100;
*pd = 12000.00;
strcpy(pst,"zhangfei");
printf("%d,%lg,%s\n",*pi,*pd,pst);
brk(pi);//释放全部内存
return 0;
}
9. mmap()和munmap()-用户层中最底层
主要用于虚拟内存地址和物理内存/硬盘文件的映射
9.1 mmap():
如果多个选项想拼起来,用位或“|”
void *mmap(void *addr,size_t size,int prot,int flags,int fd,off_t offset)
参数addr 一般给0即可,内核选定首地址
size是映射的大小,以内存页为基本单位
prot是权限,一般PROT_READ|PROT_WRITE
flags是选项,主要包括:
MAP_PRIVATE MAP_SHARED 私有/共享 对映射物理内存没区别,二选一
MAP_ANONYMOUS代表映射物理内存,mmap默认映射 硬盘文件
fd和offset映射文件用,给0即可
返回映射的首地址,失败返回MAP_FAILED就是void(*)-1.
程序演示
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int main()
{
void *p = mmap(0/*让内核选择*/,2/*映射内存大小,会补成页*/,PROT_READ|PROT_WRITE/*权限*/,MAP_PRIVATE|MAP_ANONYMOUS/*私有物理内存*/,0,0);/*文件描述符和偏移量*/
if(p == MAP_FAILED)
{
perror("mmap");
return -1;
}
printf("pid=%d",getpid());
printf("%p",p);
int *pi = p;
*pi = 100;
char *st = p+10;/*映射了一个内存页*/
strcpy(st,"abcd");/*但是不推荐这种用法*/
printf("%p",st);
printf("*pi=%d,st=%s\n",*pi,st);
while(1);
munmap(p,4);//首地址和大小
return 0;
}
内存管理的函数我们差不多都讲好了,大家也好好消化一下,写程序来调试一下!!
0 0
- 嵌入式学习之路(二十四)——UC高级(2)
- 嵌入式学习之路(二十三)——UC高级(1)
- React学习之高级ReactDOM(二十四)
- 嵌入式学习之路(十四)——C语言学习(9)
- swift学习- 高级操作符(二十四)
- 嵌入式学习日记(十四)
- uC/OS 的任务——uC/OS学习笔记(二)
- 嵌入式学习之路(二十)——数据结构(2)
- 网络流二十四题之二十四 —— 骑士共存问题(KNI)
- (十四)高级排序—希尔排序
- 嵌入式学习之路(二)——Unix命令的学习(1)
- HEVC学习(十四) —— SAO函数解析之二
- HEVC学习(二十四) —— 熵编码之五
- HEVC学习(十四) —— SAO函数解析之二
- 黑马程序员——黑马学习日志之二十四 Java高新技术(六)
- 机器学习基础(二十四)—— Random Forest
- Oracle学习笔记(二十四)——函数
- Python学习笔记(十四):模块高级
- 让VS2012自动生成我们自己的注释
- dhtmlxCombo(div非数据库取值),通过getActualValue()利用ajax传值后台判断,encodeURI防止乱码
- Ubuntu之VI的用法
- linux下tomcat6.0与jdk安装详细步骤
- VS2010环境中安装boost_1_48_0
- 嵌入式学习之路(二十四)——UC高级(2)
- Hive日志分析实践例子
- POS58热敏打印解决方法
- 网络管理软件
- jquery 插件添加的几种访求
- oracle 判断字段是否为数值型
- 「索引」《Python基础教程》学习笔记
- (一)简单工厂模式
- 读书笔记:“集体智慧编程”之第三章:带偏好条件的聚类及聚类的展示方式