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

相关文章
|
2月前
|
编译器 C++
C/C++动态内存开辟(详解)
C/C++动态内存开辟(详解)
|
3月前
|
API
Poi 中文API文档 「40种操作 Excel文件的姿势」
Poi 中文API文档 「40种操作 Excel文件的姿势」
122 0
|
1天前
|
存储 缓存 运维
DataWorks操作报错合集之DataWorks根据api,调用查询文件列表接口报错如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
9 1
|
2天前
|
缓存 运维 Serverless
Serverless 应用引擎产品使用之阿里函数计算中。将本地电脑上的项目文件部署到阿里云函数计算(FC)上并实现对外提供API和WebUI如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
17 1
|
2月前
|
编译器 C语言 C++
【C语言】calloc()函数详解(动态内存开辟函数)
【C语言】calloc()函数详解(动态内存开辟函数)
25 0
|
2月前
|
存储 缓存 安全
深入理解内存映射:mmap映射的背后原理以及和共享内存的差异
深入理解内存映射:mmap映射的背后原理以及和共享内存的差异
99 0
|
2月前
|
存储 安全 Linux
【Linux 创建临时文件 API】编程中的瞬息之光:临时文件的艺术与智慧 tmpnam,tmpfile,mkstemp,mkdtemp等函数解析...
【Linux 创建临时文件 API】编程中的瞬息之光:临时文件的艺术与智慧 tmpnam,tmpfile,mkstemp,mkdtemp等函数解析...
24 0
|
2月前
|
存储 算法 Linux
深入理解Linux内存管理brk 和 sbrk 与以及使用C++ list实现内存分配器
深入理解Linux内存管理brk 和 sbrk 与以及使用C++ list实现内存分配器
37 0
|
2月前
|
Java 数据库连接 API
Java 学习路线:基础知识、数据类型、条件语句、函数、循环、异常处理、数据结构、面向对象编程、包、文件和 API
Java 是一种广泛使用的、面向对象的编程语言,始于1995年,以其跨平台性、安全性和可靠性著称,应用于从移动设备到数据中心的各种场景。基础概念包括变量(如局部、实例和静态变量)、数据类型(原始和非原始)、条件语句(if、else、switch等)、函数、循环、异常处理、数据结构(如数组、链表)和面向对象编程(类、接口、继承等)。深入学习还包括包、内存管理、集合框架、序列化、网络套接字、泛型、流、JVM、垃圾回收和线程。构建工具如Gradle、Maven和Ant简化了开发流程,Web框架如Spring和Spring Boot支持Web应用开发。ORM工具如JPA、Hibernate处理对象与数
95 3
|
2月前
|
分布式计算 DataWorks 大数据
DataWorks常见问题之使用API删除之前的部署文件失败如何解决
DataWorks是阿里云提供的一站式大数据开发与管理平台,支持数据集成、数据开发、数据治理等功能;在本汇总中,我们梳理了DataWorks产品在使用过程中经常遇到的问题及解答,以助用户在数据处理和分析工作中提高效率,降低难度。