linux程序设计——数据管理(第七章)

第七章    数据管理

7.1    内存管理

这篇为linux的内存管理,代码在内存管理代码下载。在所有计算机系统中,内存都是一种稀缺资源。linux为应用程序提供了一个简洁的视图,它能反映一个巨大的可直接寻址的内存空间,此外,linux还提供了内存保护机制,它避免了不同的应用程序之间的互相干扰。如果机器被正确配置并且有足够的交换空间,linux还允许应用程序访问比实际物理内存更大的内存空间。

7.1.1 简单的内存分配

使用标准C语言函数库中的malloc调用来分配内存:

#include <stdlib.h>

void *malloc(size_t size);

编写程序memory1.c

这个程序要求malloc函数给它返回一个指向1MB内存空间的指针。首先检查并确保malloc函数被成功调用,然后通过使用其中的部分内存来表明分配的内存确实已经存在。

由于malloc函数返回的是一个void*指针,因此需要通过类型转换,将其转换为需要的char*类型指针。malloc函数可以保证其返回的内存是地址对齐的,所以它可以被转换成任何类型的指针。

7.1.2 分配大量的内存

编写程序memory2.c

应用程序所分配的内存是由linux内核管理的。每次程序请求内存或者尝试读写它已经分配的内存时,便会由linux内核接管病决定如何处理这些请求。

刚开始时,内核只是通过使用空闲的物理内存来满足应用程序的内存请求,但是当物理内存耗尽时,它便会开始使用所谓的交换空间(swap space)。在linux系统中,交换空间是一个安装系统时分配的独立的磁盘区域。如果熟悉windows操作系统的话,linux交换空间的作用有点像隐藏的windows交换文件。但是与windows不同,linux的交换空间没有局部堆、全局堆或可丢弃内存段等需要在代码中操心的内容——linux内核会为你完成所有的管理工作。

内核会在物理内存和交换空间之间移动数据和程序代码,使得每次读写内存时,数据看起来总像是已存在于物理内存中,而不管在你访问它们之前,它们究竟在哪里。

linux实现了一个“按需换页的虚拟内存系统”,用户程序看到的所有内存都是虚拟的,它并不真正存在于程序使用的物理地址上。linux讲所有的内存都以页为单位进行划分,通常每一页大小为4096字节。每当程序员试图访问内存时,就会发生虚拟内存到物理内存的转换,转换的具体实现和耗费的时间取决于所使用的特定硬件情况。当所访问的内存在物理上并不存在时,就会产生一个页面错误将控制权交给内核。

linux内核会对访问的内存地址进行检查,如果这个地址对于程序来说是合法可用的,内核就会确定需要向程序提供哪一个物理内存页面。然后,如果该页面之前从未被写入过,内核就直接分配它,如果它已经被保存在硬盘的交换空间上,内核就读取包含数据的页面到物理内存(可能需要把一个已有页面从内存中移出硬盘)。接着,在完成虚拟内存地址到物理地址的映射之后,内核允许用户程序继续运行。linux应用程序不需要操心这一过程,因为所有的具体实习那都已隐藏在内核中了。

最终,当应用程序耗尽所有的物理内存和交换空间,或者当最大栈长度被超过时,内核将拒绝此后的内存请求,并可能终止程序的运行。

7.1.3 滥用内存

编写memory4.c

memory4.c中先分配一些内存,然后尝试在分配的内存之后写一些数据,运行结果为:段错误(吐核),见段错误(吐核)详解。linux内存管理系统能保护系统的其他部分免受这种内存滥用的影响。为了确保一个行为恶劣的程序无法破坏任何其他程序,linux会终止其运行。

7.1. 4 空指针

linux对空指针指向地址的读写提供了很强的保护。

编写memory5a.c程序来演示访问空指针时发生的情况

7.1.5 释放内存

linux内存管理系统保证在程序结束时,把分配给它的内存返回给系统。但是,大多数程序需要的并不仅仅是分配一些内存,使用一小段时间,然后就退出。一种更常见的用法是根据需要动态地使用内存。

动态使用内存的程序应该总是通过free调用,来把不用的内存释放给malloc内存管理器。这样做可以将分散的内存块重新合并到一起,并由malloc函数库而不是应用程序来管理它。如果一个运行中的程序自己使用并释放内存,则这些自由内存实际上仍然处于被分配给该进程的状态。在幕后,linux将程序员使用的内存块作为一个物理页面集来管理,通常内存中的每个页面为4K字节。但是如果一个内存页面未被使用,linux内存管理器就可以将其从物理内存置换到交换空间中(换页),从而减轻它对资源使用的影响。如果程序师徒访问位于已置换到交换空间中的内存页中的数据,那么linxu会短暂的暂停程序,将内存页从交换空间再次置换到物理内存,然后允许程序继续运行,就像数据一直存在于内存中一样。

#include <stdlib.h

void free(void *ptr_to_memory);

调用free时使用的指针参数必须是指向由malloc、calloc或realloc调用所分配的内存。

编写程序memory6.c

这个程序调用free来释放内存,free函数带有一个指向先前分配内存的指针参数。

一旦调用free释放了一块内存,它就不再属于这个进程。它将由malloc函数库负责管理。在对一块内存调用free之后,就绝不能再对其进行读写操作了。

7.1.6 其他内存分配函数

另外两个内存分配函数并不像malloc和free使用的那样频繁,calloc和realloc原型为:

#include <stdlib.h>

void *calloc(size_t number_of_elements, size_t element_size);

void *realloc(void *existing_memory, size_t new_size);

calloc函数为一个结构数组分配内存,因此需要把元素个数和每个元素的大小作为其参数。它所分配的内存将全部初始化为0.如果calloc调用成功,将返回指向数组中第一个元素的指针。与malloc调用类似,后续的calloc调用无法保证能返回一个连续的内存空间,因此不能通过重复调用calloc期望返回的内存恰好与之前的内存连续。

realloc函数用来改变已经分配的内存块的长度。它需要传递一个先前通过calloc调用分配的内存的指针,然后根据new_size参数的值来增加或者减少其长度。为了完成这一任务,realloc函数可能不得不移动数据。因此,要确保一旦内存被重新分配之后,必须使用新的指针而不是使用realloc调用之前的那个指针去访问内存。

如果realloc无法调整内存块大小的话,它会返回一个null指针,这就意味着必须避免使用类似下面的代码:

my_ptr = malloc(BLOCK_SIZE);

...

my_ptr = realloc(my_ptr, BLOCK_SIZE * 10);

如果realloc调用失败,它将返回一个空指针,my_ptr将指向null,而先前用malloc分配的内存将无法再通过my_ptr进行访问。因此,在释放老内存块之前,最好的办法是先用malloc请求一块新内存,再通过memcpy调用把数据从老内存块复制到新的内存块。这样即使出现错误,应用程序还是可以继续访问存储在原来内存块中的数据,从而能够实现一个干净的程序终止。

时间: 2024-10-14 21:49:10

linux程序设计——数据管理(第七章)的相关文章

Linux内核分析——第七章 链接

第七章——链接 1.链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载到存储器并执行. 2.链接可以执行于编译时,加载时,运行时. 7.1编译器驱动程序 1.大多数编译系统提供编译驱动程序,它代表用户在需要时调用语言预处理器.编译器.汇编器和链接器. 7.2 静态链接 1.像Unix ld程序这样的静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载运行的可执行目标文件作为输出. 2.输入的可重定位目标文件由各种不同的代码和数据节组成.

鸟哥Linux私房菜第七章习题难题解答

1.找出/etc下面,文件大小介于50KB到60KB之间的文件,并且将权限完整的列出 答案为 find /etc -size +50k -size -60k -exec ls -l {} \; 或写成find /etc \( -size +50k -and -size -60k \) -exec ls -l {} \;也可以 2.找出/etc下面,文件容量大于50KB且文件所有者不是root的文件名,且将权限完整列出 答案为 find /etc -size +50k  ! -user root

学堂在线TsinghuaX: 00740043_2X C++语言程序设计进阶 第七章Lab

第一题:账户类 题目描述 定义一个基类Account,数据成员包含string类变量userName用于保存账户主人姓名,函数成员包括默认构造函数.带参构造函数用于初始化数据成员和输出姓名的成员函PrintName().从Account类派生出CreditAccount类,增加整型数据成员credit用于记录该用户信用额度,函数成员包括带参构造函数用于初始化数据成员和输出账户信息的成员函数PrintInfo().要求:在函数PrintInfo()中需要调用基类的成员函数PrintName().填

JavaScript高级程序设计:第七章 - 函数

六.函数表达式 //把函数当成值来使用的情况下,都可以使用匿名函数 递归 //递归函数是在一个函数通过名字调用自身的情况下构成的 //使用函数声明来定义递归函数可能会出现问题 //这是一个经典的递归阶乘函数 function factorial(num) { if (num<1){ return 1; }else{ return num * factorial(num-1); } } //使用函数声名来定义该递归函数时,函数名和函数体会产生耦合. //当其他指针指向该函数体的时候,由于执行ano

Linux网络服务第七章DNS域名解析服务

端口:53 一.DNS服务器 正向解析:根据域名查IP地址,即将指定的域名解析为相对应的IP地址.域名的正向解析是DNS服务器最基本的功能,也是最常用的功能. 反向解析:根据IP地址查域名,即将指定的IP地址解析为相对应的域名.域名的反向解析不是很常用,只在一些特殊场合才会用到,如可用于反垃圾邮件的验证. 缓存域名服务器:只提供域名解析结果的缓存功能,目的在于提高查询速度和效率,但是没有自己控制的区域地址数据.构建缓存域名服务器时,必须设置根域或指定其他DNS服务器作为解析来源. 主域名服务器:

linux程序设计——个人总结

linux程序设计--个人总结 到今天为止,<linux程序设计>学习基本完成了.从五月下旬开始接触linux,学习安装Ubuntu14.04,六月份开始学习<linux程序设计>,实习考试之余花费了足足快两个月的时间,看完大部分章节,敲了一些代码,文章都在这里--linux程序设计. 仔细看过的章节:第一章入门.第二章shell程序设计.第三章文件操作.第四章linux环境.第七章数据管理.第八章MySQL.第九章开发工具.第十一章进程和信号量.第十二章POSIX线程.第十三章进

linux程序设计——CD唱片应用程序(第七章)

7.4 CD唱片应用程序 这篇为第七章的CD唱片应用程序,代码在CD唱片应用程序代码下载.我们使用dbm数据库对数据存储,改进之前的CD唱片应用程序. 7.4.1 更新设计 虽然在文件中以逗号分隔变量来存储信息是一种在shell中很容易实现的方式,但是这样局限性很大,因为许多CD标题和曲目都包含逗号.可以通过使用dbm数据库来改进这种方法. 将CD资料分为标题和曲目两个部分,并用不同的文件来保存它们. 前面的实现存在一个问题,即将应用程序的数据访问部分和用户接口部分混在了一起,这与程序全实现在一

ROS机器人程序设计(原书第2版)补充资料 (柒) 第七章 3D建模与仿真 urdf Gazebo V-Rep Webots Morse

ROS机器人程序设计(原书第2版)补充资料 (柒) 第七章 3D建模与仿真 urdf Gazebo V-Rep Webots Morse 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中使用. 提供ROS接口的3D软件比较多,本章以最典型的Gazebo介绍为主,从Player/Stage/Gazebo发展而来,现在独立的机器人仿真开发环境,目前2016年最新版本Gazebo7.1配合ROS(kinetic)使用. 补充内容:http://blo

linux &nbsp; 第七章 磁盘和文件系统管理(一)

linux 第七章磁盘和文件系统管理(一) 享受生活 热爱挑战 明远分享 每章一句话: 在别人光鲜的背后有着太多太多,别人不知道的痛苦,自己不喜欢的人,以微笑面对,默默地为他祝福:对于喜欢的人,真情流露,真诚相待.人在做天在看,冥冥中自有因果安排,永远保持一颗善良的心,持续做对的事情,不断地提醒自己,低调做人,高调做事. 要求:    跟着做一下吧 看不清图片就点一下图片 一,关机后添加一块20GB的SCSI磁盘,重新开机进入RHEL 5系统 二,分区并格式化 1, 使用fdisk命令对新硬盘进