爱钻牛角尖的毛病又来了。仅仅是因为以前的一个c语言free内存报错。
我们知道,malloc分配的内存是从堆里获得的,而堆是由操作系统维护的,也就是说,在没有malloc前,内存空间是不属于该进程空间的,malloc函数务必调用了操作系统的系统函数(参考前边linux内存管理heap篇可知,是brk和sbrk)。也就是说,brk和sbrk是linux的系统调用,在没有linux系统之前,是没有这两个函数的。比如我之前写过的DSP裸机驱动,虽然也是用的c语言,但是是不可能有brk和sbrk及malloc函数的。brk和sbrk函数向操作系统申请内存时,操作系统会打破该进程原有的虚拟地址空间(准确的说是增加虚拟地址空间),操作系统负责维护新增的虚拟地址和物理地址的映射(需要硬件MMU等的支持,这里暂不讨论)这时候,申请过的对空间就和该进程空间融合到一起了。内存使用完毕后,调用free函数释放内存时,其实没有被真正的释放,操作系统维护堆内存的方式是通过chunks,free时仅仅是把chunks标记为可用状态(参考上一篇博客),操作系统维护这些chunks是用了一个结构体:
struct mem_control_block { int is_available; //这是一个标记? int size; //这是实际空间的大小 };
当我们free时,实质是:
void free(void *ptr) { struct mem_control_block *free; free = ptr - sizeof(struct mem_control_block); free->is_available = 1; return; }
当该进程下一次通过malloc申请内存时,操作系统首先会检查该进程空间的chunks,如果有chunks可用,就直接使用,否则才会调用brk和sbrk系统调用来重新分配内存,这样做的好处时,可以减少不必要的系统调用,提高效率,因为系统调用是很耗系统资源的。
由此,再加上我做DSP裸机驱动的经验,可以大概探测出整个计算机硬件和操作系统及编译器之间的联系:硬件支持二进制指令,通过二进制指令开发出一种程序,这种程序可以翻译我敲进去的会变得代码,这个程序就是汇编编译器。通过汇编语言开发一种程序,这种程序可以把c源码翻译成汇编,再交由汇编编译器翻译成为二进制,这种程序就是c语言编译器。通过c语言编译器开发一种程序,这种程序可以对计算机硬件进行管理,并且可以有多进程的功能……这种程序就是计算机操作系统比如linux。开发linux时可能也是有一个c语言的主函数,但是不会有brk和sbrk、malloc、free、printf等等这些函数,因为这些函数都是起作用在linux之上的。那为什么我们在linux系统之写c源码时,就可以使用printf、brk、malloc等等这些函数了呢?开发linux系统时不能使用,现在为什么就能使用了呢?问这个问题的人还是混淆了编译器和库(也就是写好的调用,写好的程序)的概念。linux操作系统上的编译器还是开发那个开发linux系统时的编译器,功能一点都没有增加,但是多了许多库可以调用。就好比,一个人来到一个荒漠,他通过自己的能力构建了一个豪华别墅,并且把别墅预留了很多接线口,水龙头等,虽然说,人还是那个人,还是只有能的功能,但是这时候他已经可以通过。