【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域

简介: 【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域

💭 写在前面

本系列博客为复习操作系统导论的笔记,内容主要参考自:

  • 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/.

相关文章
|
7月前
|
API
Poi 中文API文档 「40种操作 Excel文件的姿势」
Poi 中文API文档 「40种操作 Excel文件的姿势」
326 0
|
7月前
|
存储 算法 关系型数据库
实时计算 Flink版产品使用合集之在Flink Stream API中,可以在任务启动时初始化一些静态的参数并将其存储在内存中吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
129 4
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
68 2
|
3月前
|
监控 Java 大数据
【Java内存管理新突破】JDK 22:细粒度内存管理API,精准控制每一块内存!
【9月更文挑战第9天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的发展趋势和社区的需求,预测细粒度内存管理API可能成为未来Java内存管理领域的新突破。这套API将为开发者提供前所未有的内存控制能力,助力Java应用在更多领域发挥更大作用。我们期待JDK 22的发布,期待Java语言在内存管理领域的持续创新和发展。
|
3月前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
3月前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
4月前
|
开发框架 Unix Linux
LangChain 构建问题之在Unix/Linux系统上设置OpenAI API密钥如何解决
LangChain 构建问题之在Unix/Linux系统上设置OpenAI API密钥如何解决
56 0
|
5月前
|
敏捷开发 缓存 弹性计算
阿里云云效产品使用合集之如何通过API接口往附件中上传文件
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
5月前
|
JSON JavaScript 前端开发
若依修改,若依如何发送get和post请求,发送数据请求的写法,若依请求的API在src的api文件下,建立请求的第一步,在API中新建一个文件,第二步新建JavaScript文件
若依修改,若依如何发送get和post请求,发送数据请求的写法,若依请求的API在src的api文件下,建立请求的第一步,在API中新建一个文件,第二步新建JavaScript文件
|
5月前
|
JavaScript API
前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用
前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用