《操作系统导论》第14章读书笔记:插叙:内存操作API

news2024/11/26 6:18:25

《操作系统导论》第14章读书笔记:插叙:内存操作API

效果图

效果图

—— 杭州 2024-03-30 夜


文章目录

  • 《操作系统导论》第14章读书笔记:插叙:内存操作API
    • 1.内存类型
      • 1.1.栈内存:它的申请和释放操作是编译器来隐式管理的,所以有时也称为自动(automatic)内存。
      • 1.2.堆(heap)内存:其中所有的申请和释放操作都由程序员显式地完成。
    • 2.malloc()调用
    • 3.free()调用
    • 4.常见错误
      • 4.1.忘记分配内存
      • 4.2.没有分配足够的内存:缓冲区溢出(buffer overflow)
      • 4.3.忘记初始化分配的内存
      • 4.4.忘记释放内存:内存泄露(memory leak)
      • 4.5.在用完之前释放内存:悬挂指针(dangling pointer)
      • 4.6.反复释放内存
      • 4.7.错误地调用free()
      • 4.8.补充:为什么在你的进程退出时没有内存泄露
    • 5.底层操作系统支持
    • 6.其他调用和小结
    • 7.补充笔记:malloc()、calloc()、realloc()比较

1.内存类型

1.1.栈内存:它的申请和释放操作是编译器来隐式管理的,所以有时也称为自动(automatic)内存。

void func() {
    int x; // declares an integer on the stack
    ...
}

1.2.堆(heap)内存:其中所有的申请和释放操作都由程序员显式地完成。

void func() {
    int *x = (int *) malloc(sizeof(int));
    ...
}
  • 关于这一小段代码有两点说明。首先,你可能会注意到栈和堆的分配都发生在这一行:首先编译器看到指针的声明(int * x)时,知道为一个整型指针分配空间,随后,当程序调用malloc()时,它会在堆上请求整数的空间,函数返回这样一个整数的地址(成功时,失败时则返回NULL),然后将其存储在栈中以供程序使用。

在这里插入图片描述

2.malloc()调用

  • malloc 函数非常简单:传入要申请的堆空间的大小,它成功就返回一个指向新申请空间的指针,失败就返回NULL。man 手册展示了使用malloc 需要怎么做,在命令行输入man malloc,你会看到:
#include <stdlib.h>
...
void *malloc(size_t size);
  • 你也可以传入一个变量的名字(而不只是类型)给sizeof(),但在一些情况下,可能得不到你要的结果,所以要小心使用。例如,看看下面的代码片段:
int *x = malloc(10 * sizeof(int));
printf("%d\n", sizeof(x));
  • 在第一行,我们为10个整数的数组声明了空间,这很好,很漂亮。但是,当我们在下一行使用sizeof()时,它将返回一个较小的值,例如4(在32位计算机上)或8(在64 位计算机上)。原因是在这种情况下,sizeof()但为我们只是问一个整数的指针有多大,而不是我们动态分配了多少内存。
    但是,有时sizeof()的确如你所期望的那样工作:
int x[10];
printf("%d\n", sizeof(x));
  • 在这种情况下,编译器有足够的静态信息,知道已经分配了40个字我。另一个需要注意的地方是使用字符串。如果为一个字符串声明空间,请使用以下习惯用法:malloc(strlen(s) + 1),它使用函数strlen()获取字符串的长度,并加上1,以便为字符串结束符留出空间。这里使用sizeof()可能会导致麻烦。
  • 你也许还注意到malloc()返回一个指向void类型的指针。这样做只是C中传回地址的方式,让程序员决定如何处理它。程序员将进一步使用所谓的强制类型转换(cast),在我们上面的示例中,程序员将返回类型的malloc()强制转换为指向double的指针。强制类型转换实实上没干什么事,只是告诉编译器和其他可能正在读你的代码的程序员:“是的,我知道我在做什么。”通过强制转换malloc()的结果,程序员只是在给人一些信心,强制转换不是程序正确所必须的。

在这里插入图片描述

在这里插入图片描述

3.free()调用

  • 事实证明,分配内存是等式的简单部分。知道何时、如何以及是否释放内存是困难的部分。要释放不再使用的堆内存,程序员只需调用free():
int *x = malloc(10 * sizeof(int));
...
free(x);
  • 该函数接受一个参数,即一个由malloc()返回的指针。因此,你可能会注意到,分配区域的大小不会被用户传入,必须由内存分配库本身记录追踪。

在这里插入图片描述

4.常见错误

4.1.忘记分配内存

许多例程在调用之前,都希望你为它们分配内存。例如,例程strcpy(dst, src)将源字符串中的字符串复制到目标指针。但是,如果不小心,你可能会这样做:

char *src = "hello";
char *dst; // oops! unallocated
strcpy(dst, src); // segfault and die

运行这段代码时,可能会导致段错误(segmentation fault)。

  • 仅仅因为程序编译过了甚至正确运行了一次或多次,并不意味着程序是正确的。

在这个例子中,正确的代码可能像这样:

char *src = "hello";
char *dst = (char *) malloc(strlen(src) + 1);
strcpy(dst, src); // work properly

或者你可以用strdup(),让生活本加轻松。

4.2.没有分配足够的内存:缓冲区溢出(buffer overflow)

char *src = "hello";
char *dst = (char *) malloc(strlen(src)); // too small!
strcpy(dst, src); // work properly

奇怪的是,这个程序通常看起来会正确运行,这取决于如何实现malloc 和许多其他细节。在某些情况下,当字符串拷贝执行时,它会在超过分配空间的末尾处写入一个字节,但在某些情况下,这是无害的,可能会覆盖不再使用的变量。在某些情况下,这些溢出可能具有令人难以置信的危害,实实上是系统中许多安全漏洞的来源。在其他情况下,malloc库总是分配一些额外的空间,因此你的程序实际上不会在其他某个变量的值上涂写,并且工作得很好。还有一些情况下,该程序确实会发生故障和崩溃。

  • 一个宝贵的教训:即使它正确运行过一次,也不意味着它是正确的。

4.3.忘记初始化分配的内存

4.4.忘记释放内存:内存泄露(memory leak)

另一个常见错误称为内存泄露(memory leak),如果忘记释放内存,就会发生。

  • 在长时间运行的应用程序或系统(如操作系统本身)中,这是一个巨大的问题,因为缓慢泄露的内存会导致内存不足,此时需要重新启动。因此,一般来说,当你用完一段内存时,应该确保释放它。请注意,使用垃圾收集语言在这里没有什么帮助:如果你仍然拥有对某块内存的引用,那么垃圾收集器就不会释放它,因此即使在较现代的语言中,内存泄露仍然是一个问题。
  • 在某些情况下,不调用free()似乎是合理的。例如,你的程序运行时间很短,很块就会退出。在这种情况下,当进程死亡时,操作系统将清理其分配的所有页面,因此不会发生内存泄露。虽然这肯定“有效”(请参阅后面的补充),但这可能是一个坏习惯,所以请谨慎选择这样的策略。长远来看,作为程序员的目标之一是养成良好的习惯。其中一个习惯是理解如何管理内存,并在C这样的语言中,释放分配的内存块。即使你不这样做也可以逃脱惩罚,建议还是养成习惯,释放显式分配的每个字节。

4.5.在用完之前释放内存:悬挂指针(dangling pointer)

有时候程序会在用完之前释放内存,这种错误称为悬挂指针(dangling pointer),正如你猜测的那样,这也是一件坏事。随后的使用可能会导致程序崩溃或覆盖有效的内存(例如,你调用了free(),但随后再次调用malloc()来分配其他内容,这重新利用了错误释放的内存)。

4.6.反复释放内存

4.7.错误地调用free()

4.8.补充:为什么在你的进程退出时没有内存泄露

  • 当你编写一个短时间运行的程序时,可能会使用malloc()分配一些空间。程序运行并即将完成:是否需要在退出前调用几次free()?虽然不释放似乎不对,但在真正的意义上,没有任何内存会“丢失”。原因很简单:系统中实际存在两级内存管理。

  • 第一级是由操作系统执行的内存管理,操作系统在进程运行时将内存交给进程,并在进程退出(或以其他方式结束)时将其回收。第二级管理在每个进程中,例如在调用malloc()和free()时,在堆内管理。即使你没有调用free()(并因此泄露了堆中的内存),操作系统也会在程序结束运行时,收回进程的所有内存(包括用于代码、栈,以及相关堆的内存页)。无论地址空间中堆的状态如何,操作系统都会在进程终止时收回所有这些页面,从而确保即使没有释放内存,也不会丢失内存。

  • 因此,对于短时间运行的程序,泄露内存通常不会导致任何操作问题(尽管它可能被认为是不好的形式)。如果你编写一个长期运行的服务器(例如Web 服务器或数据库管理系统,它永远不会退出),泄露内存就是很大的问题,最终会导致应用程序在内存不足时崩溃。当然,在某个程序内部泄露内存是一个更大的问题:操作系统本身。这再次向我们展示:编写内核代码的人,工作是辛苦的……

在这里插入图片描述

在这里插入图片描述

5.底层操作系统支持

  • 你可能已经注意到,在讨论malloc()和free()时,我们没有讨论系统调用。原因很简单:它们不是系统调用,而是库调用。因此,malloc库管理虚拟地址空间内的空间,但是它本身是建立在一些系统调用之上的,这些系统调用会进入操作系统,来请求本多内存或者将一些内容释放回系统。

  • 最后,你还可以通过mmap()调用从操作系统获取内存。通过传入正确的参数,mmap()可以在程序中创建一个匿名(anonymous)内存区域——这个区域不与任何特定文件相关联,而是与交换空间(swapspace)相关联,稍后我们将在虚拟内存中详细讨论。这种内存也可以像堆一样对待并管理。阅读mmap()的手册页以获取本多详细信息。

在这里插入图片描述

6.其他调用和小结

在这里插入图片描述

7.补充笔记:malloc()、calloc()、realloc()比较

当涉及到动态内存分配时,malloc(), calloc(), 和 realloc() 是 C 语言标准库中的三个重要函数。以下是这三个函数的比较表格:

特征/函数malloc()calloc()realloc()
功能分配指定大小的内存块分配指定数量的连续内存块,并将每一块初始化为 0改变先前分配的内存块的大小
参数需要分配的内存块大小(以字节为单位)内存块的数量和每个块的大小(以字节为单位)指向先前分配的内存块的指针以及新的内存大小
返回值指向分配的内存块的指针,如果分配失败则返回 NULL指向分配且初始化的内存块的指针,如果分配失败则返回 NULL指向重新分配的内存块的指针,如果分配失败则返回 NULL
初始化不初始化内存块,内存块的内容不确定将内存块的每个字节都初始化为 0不初始化新分配的内存部分,旧内存内容保持不变至新大小
性能通常比 calloc() 快,因为不初始化内存可能比 malloc() 慢,因为初始化内存性能依赖于给定的内存块和分配大小
适用性当不需要初始化内存时使用当需要初始化数组或多个相同类型的对象时使用当需要调整先前分配的内存大小时使用
示例代码int *ptr = (int*)malloc(sizeof(int) * n);int *ptr = (int*)calloc(n, sizeof(int));ptr = (int*)realloc(ptr, sizeof(int) * n);

注意

  • malloc()calloc() 在失败时都会返回 NULL,因此在使用这些函数后应该检查返回值以确认内存分配是否成功。
  • realloc() 在扩大内存块时,可能会移动内存块到新的位置,如果是这样,它会复制旧内存内容到新位置并释放旧内存。
  • 如果 realloc() 的第一个参数是 NULL,它等价于 malloc()
  • 在使用完分配的内存后,你应该使用 free() 函数释放它,以避免内存泄漏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1557342.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FebHost:意大利.IT域名一张意大利网络名片

.IT域名是意大利的国家顶级域名&#xff0c;对于意大利企业和个人而言,拥有一个属于自己的”.IT”域名无疑是件令人自豪的事。这个被誉为意大利互联网标志性代表的域名,不仅隐含着浓厚的意大利文化特色,还为使用者在当地市场的推广铺平了道路。 对于那些希望在意大利市场建立强…

2核4G服务器可以承载多少用户?卡不卡?

腾讯云轻量应用服务器2核4G5M配置性能测评&#xff0c;腾讯云轻量2核4G5M带宽服务器支持多少人在线访问&#xff1f;并发数10&#xff0c;支持每天5000IP人数访问&#xff0c;腾讯云百科txybk.com整理2核4G服务器支持多少人同时在线&#xff1f;并发数测试、CPU性能、内存性能、…

手把手在K210上部署自己在线训练的YOLO模型

小白花了两天时间学习了一下K210&#xff0c;将在线训练的模型部署在K210&#xff08;代码后面给出&#xff09;上&#xff0c;能够识别卡皮巴拉水杯&#xff08;没错&#xff0c;卡皮巴拉&#xff0c;情绪稳定&#xff0c;真的可爱&#xff01;&#xff09;。数据集是用K210拍…

C语言例1-11:语句 while(!a); 中的表达式 !a 可以替换为

A. a!1 B. a!0 C. a0 D. a1 答案&#xff1a;C while()成真才执行&#xff0c;所以!a1 &#xff0c;也就是 a0 原代码如下&#xff1a; #include<stdio.h> int main(void) {int a0;while(!a){a;printf("a\n");} return 0; } 结果如…

数字化转型导师坚鹏:新质生产力发展解读、方法与案例

新质生产力发展解读、方法与案例 课程背景&#xff1a; 很多学员存在以下问题&#xff1a; 不知道如何理解新质生产力&#xff1f; 不清楚如何发展新质生产力&#xff1f; 不知道新质生产力发展案例&#xff1f; 课程特色&#xff1a; 有实战案例 有原创观点 有…

Linux课程____selinux模式

一、是什么 它叫做“安全增强型 Linux&#xff08;Security-Enhanced Linux&#xff09;”&#xff0c;简称 SELinux&#xff0c;它是 Linux 的一个安全子系统 二、有啥用 就是最大限度地减小系统中服务进程可访问的资源&#xff08;根据的是最小权限原则&#xff09;。避免…

leetcode:392. 判断子序列

题目&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {} }; 题解&#xff1a; 很巧妙的题解&#xff1a;循环遍历两个字符串&#xff0c;两个字符串都没遍完就继续遍历&#xff0c;字符串s先遍历完结果为true&#xff0c;字符串t先遍历完结果为…

2024第17届计算机设计大赛开始啦(保研竞赛)

中国大学生计算机设计大赛是面向高校本科生的竞赛&#xff0c;旨在培养创新型、复合型、应用型人才。2024年大赛的主题包括软件应用、微课与教学辅助等11个大类。参赛队由1&#xff5e;3名本科生组成&#xff0c;指导教师不多于2人。在组队和选题方面&#xff0c;强调团结协作和…

Linux学习记录18——用户的基本组、扩展组和文件的所有者、所属组之间的关系

一.学习的内容 经过前两次的学习&#xff0c;我对文件的所有者和所属组的概念不是很清晰。即什么才是文件的所有者&#xff1f;什么才是文件的所属组&#xff1f;它俩和用户的基本组、扩展组有联系吗&#xff1f;文件的所有者、所属组和其他用户都对文件有相应的rwx权限&#x…

双端队列的插入与删除操作的实现及其时间复杂度分析

双端队列(deque,全称为double-ended queue)是一种支持在两端插入和删除元素的数据结构。与栈和队列不同,双端队列提供了更加灵活的操作方式。在实现双端队列时,我们可以采用数组作为底层数据结构,以保证插入和删除操作的时间复杂度为O(1)。 一、双端队列的基本概念 双…

Vue3 + Vite + TS + Element-Plus + Pinia创建新项目(1)

1、cmd进入命令行后&#xff0c;输入npm create vite 2、使用vs code打开文件夹 3、在VS Code的终端里面输入命令&#xff1a;npm i 安装依赖 4、安装依赖库 npm i vue-router 路由安装 npm i pinia 全局状态管理 npm i axios 请求库 npm i element-p…

Matlab之提高交叉定位点的定位精度

通过测向交叉定位的方法&#xff0c;按理只需2根测向线即可得出定位点的位置。但由于误差的存在&#xff0c;求出的定位点位置存在一定的偏差。为了得到更加精确的定位点位置&#xff0c;需要对定位点进行冗余测量&#xff0c;从而得到多个定位点&#xff0c;然后通过定位点估计…

ssm007亚盛汽车配件销售业绩管理统+jsp

亚盛汽车配件销售业绩管理系统设计与实现 摘 要 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;因此传统管理方式就不适合。为了让亚盛汽车配件销售信息的管理模式进行升级&#xff0c;也为了更好的维护亚盛汽车配件销售信息&am…

【Linux系统】信号量实现同步和互斥

一.回顾 在这之前已经讲解了System V版本的信号量&#xff0c;主要内容为以下3点&#xff1a; 信号量本质是一把计数器申请信号量本质就是预订资源PV操作(申请和释放)是原子的 今天我们要学习的是POSIX版本的信号量&#xff0c;以上三点同样遵循 二.信号量VS互斥锁 1.联系&…

统信 UOS V20 一键安装 Oracle 12CR2(220118)单机版

Oracle 一键安装脚本&#xff0c;演示 统信 UOS V20 一键安装 Oracle 12CR2&#xff08;220118&#xff09;单机版过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&#xff1a;…

【MySQL】事务是什么?事务的特性又是什么?

文章目录 ✍事务是什么&#xff1f;✍事务的特性&#xff08;四个&#xff09;✍事务并发时出现的问题✍事务的隔离性 ✍事务是什么&#xff1f; 事务是由一个或多个SQL语句构成的&#xff0c;在事务中&#xff0c;这些的SQL不可分割&#xff0c;是一个整体&#xff0c;整个事…

阿里云短信服务免费100条

阿里云短信服务免费100条申请入口 aliyunbaike.com/go/sms 最新阿里云短信优惠&#xff0c;2元可以购买200条短信&#xff0c;如下图&#xff1a; 阿里云短信服务免费100条 另外&#xff0c;阿里云短信服务免费100条是指买2000条短信可以赠送100条&#xff0c;买5000条赠300条&…

算法学习——LeetCode力扣图论篇1

算法学习——LeetCode力扣图论篇1 797. 所有可能的路径 797. 所有可能的路径 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特…

RPC(Remote Procedure Call)远程过程调用

定义 RPC&#xff08;Remote Procedure Call&#xff09;即远程过程调用&#xff0c;是一种计算机通信协议&#xff0c;它允许程序在不同的计算机之间进行通信和交互&#xff0c;就像本地调用一样。 为什么需要 RPC&#xff1f; 回到 RPC 的概念&#xff0c;RPC 允许一个程序…

Pytorch最全详细安装指南

一. 安装Anaconda 敬请期待。。。 二. 安装Pytorch 1 打开Anaconda prompt&#xff0c;创建pytorch环境 输入 conda create -n pytorch python3.7 运行结束&#xff0c;若出现proceed 输入y 运行结束&#xff0c;创建pytorch环境成功 2 安装pytorch 输入nvidia-smi 查…