💭 写在前面
本系列博客为复习操作系统导论的笔记,内容主要参考自:
- Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau, Operating Systems: Three Easy PiecesA. Silberschatz, P. Galvin, and G. Gagne,
- Operating System Concepts, 9th Edition, John Wiley & Sons, Inc., 2014, ISBN 978-1-118-09375-7.Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .
0x00 内存API概述(Overview of Memory APIs)
前置知识:芝士雪豹, 动态内存的开辟与释放
🔍 前置知识:【维生素C语言】第十三章 - 动态内存管理
💭 malloc 函数:
#include <stdlib.h> void* malloc(size_t size)
Allocate a memory region on the heap. 用于在堆上开辟一块内存空间。
- size_t size : 内存块的大小(字节数)
- size_t 为无符号整数类型
- 开辟成功:返回一个指向由 malloc 分配的内存块的void类型的指针。
- 开辟失败 : 返回空指针 (null ptr)
When a process asks for dynamic memory (e.g., through malloc()), it doesn’t get additional physical memory; instead, it gets the right to use a new range of virtual address space
当一个进程申请内存时(比如通过 malloc),它并没有得到额外的物理内存,而是 得到了使用一段新的虚拟地址空间的权限
Allocate a logically contiguous memory but the mapped physical memory may not be contiguous.
开辟的内存地址只是逻辑上是连续的,但实际的物理内存地址不一定是连续的。
int *x = malloc(10 * sizeof(int)); printf(“%d\n”, sizeof(x));
🚩 运行结果:4
int x[10]; printf(“%d\n”, sizeof(x))
🚩 运行结果:40
* 老生常谈:数组名是首元素地址,sizeof 数组名是一个例外,是整个数组大小。
💭 free 函数:
#include <stdlib.h> void free(void* ptr)
该函数用于释放程序员在堆上动态开辟的内存。
- void *ptr:传要释放的空间的指针
- 无返回值
0x01 常见错误:忘记开空间了
char *src = “hello”; //character string constant char *dst; //unallocated strcpy(dst, src); //segfault and die
char *src = “hello”; //character string constant char *dst (char *)malloc(strlen(src) + 1 ); // allocated strcpy(dst, src); //work properly
这里 strlen(src) + 1,是给 \0 的。
0x02 常见错误:空间没开够
下面的代码有可能会寄,也可能不会寄的代码,纯靠人品:
char *src = “hello”; //character string constant char *dst (char *)malloc(strlen(src)); // too small strcpy(dst, src); //work properly
0x03 常见错误:忘记初始化分配的内存
你虽然正确的 malloc 了,但忘记在新分配的数据类型中填写一些值了,Dont do that!!!
如果你忘记了,你的程序会遇到未初始化读取(uninitialized read)。
他会读取一些未知的数据,这就不可控了。鬼知道那里可能会有什么?
如果走运,读到的程序仍然有效(0),如果不走运,就会使一些随机的、有害的东西,直接寄。
int *x = (int *)malloc(sizeof(int)); // allocated printf(“*x = %d\n”, *x); // uninitialized memory access
0x04 常见错误:忘记释放内存
内存泄露问题,这个话题以及是在熟悉不过的陈芝麻烂谷子了,这里就不赘述了。
记住一定要 free 就完事了,free 了之后顺便再把指针置NULL。
常见错误:悬空指针
有时候程序会在用完之前释放内存,这种错误被称为悬空指针(dangling pointer)。
如果在用完前就提前释放了内存,就会导致程序崩溃或覆盖有效的内存。
比如你调用了 free,但随后再次调用 malloc 来分配其他内容,这重新利用了错误释放的内存。
0x05 常见错误:反复释放内存
程序有时还会不止一次的释放内存,这被称为重复释放(double free)。
这么做的结果是未定义的。内存分配库可能会改到困惑,并且会做各种奇奇怪怪的事情,其中崩溃是最常见的结果。
int *x = (int *)malloc(sizeof(int)); // allocated free(x); // free memory free(x); // free repeatedly
free 了没开的内存(瞎free):
int *x = (int *)malloc(sizeof(int)); // allocated free(x+12); // free memory
0x06 brk / sbrk 系统调用
#include <unistd.h> int brk(void *addr) void *sbrk(intptr_t increment);
- 可以要求操作系统扩大堆的空间。
- malloc 用的就是 brk 系统调用。
- break:堆的末端在地址空间中的位置。
- brk 将新的 break 设置为 addr 所指定的值。
- sbrk 与 brk 类似,但 sbrk 是通过增量字节(increment bytes)来增加或减少 break。
- sbrk是系统调用,是 Unix/Linux 系统提供的接口(只能在Unix/Linux系统下才能用的)。C 程序员不应该直接调用 brk 或 sbrk,这不是我们该碰的。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main() { void* curr_brk, * tmp_brk = NULL; printf("Welcome to sbrk example:%d\n", getpid()); /* sbrk(0) gives current program break location */ tmp_brk = curr_brk = sbrk(0); printf("Program Break Location1:%p\n", curr_brk); /* brk(addr) inc/dec program break location */ brk(curr_brk + 4096); curr_brk = sbrk(0); printf("Program break Location2:%p\n", curr_brk); brk(tmp_brk); curr_brk = sbrk(0); printf("Program Break Location3:%p\n", curr_brk); return 0; }
0x07 mmap 创建匿名映射区域
mmap:Creating Anonymous Mapping Region
#include <sys/mman.h> void *mmap(void *ptr, size_t length, int prot, int flags, int fd, off_t offset)
分配一个长度为 ptr 的内存区域(不在堆中)。
一些 malloc 实现会使用 mmap 来分配大块。
fd 参数应该是 -1,offset 应该是0。
0x08 mmap 创建以文件为基础的映射区域
Creating File-Backed Mapping Region
ptr = mmap(0, 40, flag, MAP_SHARED, fd, 0)) ; if ( ptr == MAP_FAILED ) exit(EXIT_FAILURE);
使用长度字节映射文件描述符 fd 所指的文件内容,从文件的偏移量开始,偏移量必须是页面大小的倍数。
#include <stdio.h> /* include more header files : stdlib.h, fcntl.h, unistd.h, sys/types.h, sys/mmap.h, sys/stat.h errno.h */ … int main(int argc, char* argv[]) { int fd, offset; char* data; struct stat sbuf; … if ((fd = open("mmapdemo.c", O_RDONLY)) == -1) { perror("open"); exit(1); } // stat 获取文件大小 if (stat("mmapdemo.c", &sbuf) == -1) { perror("stat"); exit(1); } offset = atoi(argv[1]); /* get offset */ … // 分配一个新的虚拟地址空间范围,并将其映射到进程中。 data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0)); … printf("byte at offset %d is '%c'\n", offset, data[offset]); return 0; }
$ mmapdemo 30
byte at offset 30 is e
📌 [ 笔者 ] 王亦优 📃 [ 更新 ] 2022. ❌ [ 勘误 ] /* 暂无 */ 📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免, 本人也很想知道这些错误,恳望读者批评指正!
📜 参考资料 Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau, Operating Systems: Three Easy Pieces A. Silberschatz, P. Galvin, and G. Gagne, Operating System Concepts, 9th Edition, John Wiley & Sons, Inc., 2014, ISBN 978-1-118-09375-7. Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. |