free函数在操作系统内存中的实现【转】

news/2024/7/20 12:56:45 标签: 内存管理, 数据结构与算法, 操作系统

转自:http://www.2cto.com/kf/201210/160985.html

我一次性malloc十个单位节点的内存空间出来赋值给L, 现在我想一次性删除从第3个到第6个节点,我是这么做的:
1.将第六个节点的next指针指向NULL
2.将L指针指向第三个单位空间的地址,再free(L)。

等到把代码写完之后,才发现其中的问题:这里我的free(L)用的对吗?

编译运行了一下才发现了问题所在。

为了清楚地看到这个错误,将这个问题简化出来,请看下面的代码:

[cpp] 
#include <stdio.h> 
#include <malloc.h> 
 
int main() 

    char *p = (char*)malloc(sizeof(char) * 10); 
    char *q = p; 
    int i = 0; 
    for (; i < 10; i++) 
        printf("%08X\n", q + i); 
    q = q + 2; 
    free(q); 
 
    return 0; 

编译执行之后结果如下:

0863E008
0863E009
0863E00A
0863E00B
0863E00C
0863E00D
0863E00E
0863E00F
0863E010
0863E011
*** glibc detected *** ./free: free(): invalid pointer: 0x0863e00a ***
======= Backtrace: =========
/lib/libc.so.6[0x4481d9f2]
./free[0x8048487]
/lib/libc.so.6(__libc_start_main+0xf3)[0x447be6b3]
./free[0x8048391]
======= Memory map: ========
......

已放弃(吐核)

 在linux下面可以看到free这句代码是不能被运行的。

那么为什么执行会出错呢?

这里首先要谈谈malloc和free这两个函数的用法了。

首先我们要知道的一个原则就是:malloc 和free是成对使用的。

你可能会奇怪为什么malloc指定了内存分配的长度,而free的时候没有指定释放的内存长度。
因为是成对使用的,所以满足下面两个原则:

1.我们free的指针必须是malloc出来返回给你的指针。

2. 所以,free的长度是你malloc要求分配的长度。
看到这里可能有人会问:你是怎么得出这两个结论的呢?

好吧,现在我们就来深究一下,free函数在操作系统内存中究竟是怎么实现的。

但是毕竟自己实力有限,还是菜鸟一枚,如果让我阅读free 和malloc 的源代码,肯定是不现实的,貌似有1500多行。。。。

 

自己摘除了一点基本信息:

malloc/free 是操作系统提供的接口
不同的系统可能使用不同的库
接口形式相同,但是实现方式可能不同
这主要取决于操作系统内存管理模式

VC使用的CRT,而GCC使用的是libc 

 里面有两段比较经典的话。虽然正确性还有待考证。摘录如下:

1.

内核通过一个红黑树来记录了空闲的内存,malloc就是从树中查找一块大小适合的内存并把地址给你,然后把这个节点从树中摘除,避免被别人分配到产生冲突。这个内存现在归你一个人用了。
free函数是把你的这个内存重新放回到红黑树中,让别人可以申请到这个内存。从逻辑上来说,你现在不能在使用这个内存了,因为它已经不属于你。但是系统的实现上目前没有做到,所以你还是能访问这个地址。
另外,系统也不会帮你覆盖内存中的数据,因为做这一个操作浪费时间,没有必要。

打一个简单的比方。你租了一套房子,后来租期到了,房子回到房东手里,或者又转租给别人。但是你拿着原来的钥匙还是能进入那套房子,虽然这个是不合法的。

 

2.

应该确切讲是不变的。内存管理多数是通过一个MBC链表实现的,及你实际分配的内存空间为:(nSize + 3) / 4 * 4 + sizeof(MBC)的大小,在malloc之后,系统程序实际返回的是分配的MBC地址+sizeof(MBC),释放内存时,free所做的第一个动作是ptr - sizeof(MBC)得到实际的MBC块,在这个MBC块中包含了该内存的大小,内存MBC链的指针等信息;所以,如果你使用了超出实际分配内存大小的空间,会造成整个MBC链的混乱,最直接表现是程序在free时在另一个不相关的地方出现了异常;所以您可以看出来,在执行了free之后,该块内存并没有改变,即使该块内存相邻内存为空,而发生了内存块的合并,您刚才使用的内存空间也没有发生改变(你看到的),改变的是MBC链表;
详细的内存管理程序您可以参考一些库,如dlmalloc,这个库除了使用了MBC链外,还是用了沙箱机制;事实上早期DOS的内存管理程序也是使用的MBC链;

 

3.

1)我注意到之上的回复多数是基于系统端的,(如红黑树,我在Understand Linux kernal中还真没有听说过红黑树,最后居然在国内网站找到了 ---- 不知道是不是只是国内学术名词,是内核的内存管理);系统内存分配涉及属性、内存页面以及是否有MCU;但是注意一点malloc和free并不是直接使用系统内存管理程序,在多数Linux程序中malloc和free一般是通过标准库,及我说过的DL内存管理程序实现的;因此,内核的内存管理和我们在应用层面的内存管理(如malloc/free)不要混为一谈;
2)DL的内存管理从效能上要比直接使用系统的内存管理效能要高,如经过我们实测:在随机大小、随机顺序申请释放 ---- 目的在制造最差情况,DL内存管理程序是直接使用系统的3~3.9倍,考虑使用realloc,DL内存管理程序是系统的10~43.35倍;所以,实际情况下,我们并不是直接使用系统内存管理 ---- 只是我们并不知道。

 

深入理解C语言中的free函数。

转自:http://blog.csdn.net/Leisure512/article/details/4978476

#include<stdio.h>
#include<stdlib.h>
int main(){
    int *test=(int *)calloc(1,sizeof(int));
    if(!test)
        printf("分配内存错误/n");
    else{
        printf("指针地址:%X/n",(unsigned int)test);
        printf("指向内存的内容:%d/n",*test);
        printf("释放它/n");
        free(test);
        printf("释放后,指针的地址:%X/n",(unsigned int)test);
        printf("释放后,指针指向的内存的内容:%d/n",*test);
    }
    return 0;
}
    通过上面的小程序,可以看出,在free前后,指针test并没有改变,那么free到底怎么工作呢?
    查看man 3 free,其中也没有说明白,只是说free释放掉指定参数指针指向的内存,并返回void。其实,free函数只是将参数指针指向的内存归还给操作系统,并不会把参数指针置NULL,为了以后访问到被操作系统重新分配后的错误数据,所以在调用free之后,通常需要手动将指针置NULL。从另一个角度来看,内存这种底层资源都是由操作系统来管理的,而不是编译器,编译器只是向操作系统提出申请。所以free函数是没有能力去真正的free内存的。只是告诉操作系统它归还了内存,然后操作系统就可以修改内存分配表,以供下次分配。


http://www.niftyadmin.cn/n/1779642.html

相关文章

Windows 10离线安装dotnetfx35

工作中&#xff0c;我们经常要在Windows 电脑上安装一些应用&#xff0c;而有些应用需要dotnetfx35组件&#xff0c;当我们下载一个安装包的时候需要连接Windows update进行安装&#xff0c;这样速度非常慢。其实我们可以跳开在线下载离线安装。只要在Windows 10光盘中把source…

URAL 1727. Znaika#39;s Magic Numbers(数学 vector)

主题链接&#xff1a;http://acm.timus.ru/problem.aspx?space1&num1727 1727. Znaikas Magic Numbers Time limit: 0.5 secondMemory limit: 64 MBZnaika has many interests. For example, now he is investigating the properties of number sets. Znaika writes down …

maskView遮罩中多张图片的动画

maskView遮罩中多张图片的动画 说明 用多张图片做遮罩效率极高&#xff0c;非常好理解&#xff0c;而且极其美观&#xff01; 效果图 素材 源码 // // ViewController.m // FeedBack // // Created by YouXianMing on 15/5/6. // Copyright (c) 2015年 YouXianMing. All ri…

Python第一个基本教程6章 抽象的

Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32 Type "copyright", "credits" or "license()" for more information. #6.1 懒惰即美德>>> fibs [0,1] >>> for i in range(8):fibs.ap…

C#数据结构与算法揭秘12

这节&#xff0c;我们来重点讨论一下 这个树形结构的相应的源代码的实现了。 真的我们首先介绍一下无向图邻接表类的实现来说明图的邻接表类的实现。 无向图邻接表的邻接表结点类 adjListNode<T>有两个成员字段&#xff0c;一个是1adjvex&#xff0c;存储邻接顶点的信息…

传统服务器跟云服务器的区别

1、传统服务器有产权&#xff0c;而云主机则只是一种服务而已&#xff0c;没有任何产权&#xff1b; 2、传统服务器是独立的服务器&#xff0c;所有功能由自己完成&#xff0c;而云主机则是一个集群&#xff0c; 他们的功能需要相互之间的协助才能完成&#xff1b; 3、传统服…

执行多条SQL语句,实现数据库事务。(Oracle数据库)

/// <summary>/// 执行多条SQL语句&#xff0c;实现数据库事务。/// </summary>/// <param name"SQLStringList">&#xff08;key为sql语句&#xff0c;value是该语句的OracleParameter[]&#xff09;</param>/// <returns></retur…

linux mysql root密码忘记后重置

1.停止mysqld服务[rootServer huage]# service mysqld stop2.以跳过授权的方式启动mysql[rootServer huage]# mysqld_safe --skip-grant-tables &3.以root用户登录mysql[rootServer huage]# mysql -u root4.进入mysql数据库mysql> use mysql5.更新mysql数据库中的user表…