一、系统调用(System Call):
在Linux中,4G内存可分为两部分——内核空间1G(3~4G)与用户空间3G(0~3G),我们通常写的C代码都是在对用户空间即0~3G的内存进行操作。而且,用户空间的代码不能直接访问内核空间,因此内核空间提供了一系列的函数,实现用户空间进入内核空间的接口,这一系列的函数称为系统调用(System Call)。比如我们经常使用的open、close、read、write等函数都是系统级别的函数(man 2 function_name),而像fopen、fclose、fread、fwrite等都是用户级别的函数(man 3 function_name)。不同级别的函数能够操作的内存区域自然也就不同。

我们用一幅图来描述函数的调用过程:

对于C++中new与delete的底层则是用malloc和free实现。而我们所用的malloc()、free()与内核之间的接口(桥梁)就是sbrk()等系统函数;当然我们也可以直接调用系统调用(系统函数),达到同样的作用。我们可以用下面这幅图来描述基本内存相关操作之间的关系:

虽然使用系统调用会带来一定的好处,但是物极必反,系统调用并非能频繁使用。由于程序由用户进入内核层时,会将用户层的状态先封存起来,然后到内核层运行代码,运行结束以后,从内核层出来到用户层时,再把数据加载回来。因此,频繁的系统调用效率很低。今天我们就系统调用层面来对内存操作做进一步的了解。

二、内存管理(Memory Management)系统调用:
1、brk()与sbrk():
(1)、函数原型与实现:
//函数原型:
#include<unistd.h>
int brk(void * addr);
void * sbrk(intptr_t increment);

由于sbrk()与brk()这两个系统函数有点所谓怪异,我们先来看看man手册对于sbrk()与brk()的描述:

DESCRIPTION

brk() and sbrk() change the location of the program break, which
defines the end of the process's data segment (i.e., the program
break is the first location after the end of the uninitialized data
segment). Increasing the program break has the effect of allocating
memory to the process; decreasing the break deallocates memory.

brk() sets the end of the data segment to the value specified by
addr, when that value is reasonable, the system has enough memory,
and the process does not exceed its maximum data size (see
setrlimit(2)).

sbrk() increments the program's data space by increment bytes.
Calling sbrk() with an increment of 0 can be used to find the current
location of the program break.

RETURN VALUE

On success, brk() returns zero. On error, -1 is returned, and errno
is set to ENOMEM.

On success, sbrk() returns the previous program break. (If the break
was increased, then this value is a pointer to the start of the newly
allocated memory). On error, (void *) -1 is returned, and errno is
set to ENOMEM.

描述:
brk()和sbrk()改变程序间断点的位置。程序间断点就是程序数据段的结尾。(程序间断点是为初始化数据段的起始位置).通过增加程序间断点进程可以更有效的申请内存 。当addr参数合理、系统有足够的内存并且不超过最大值时brk()函数将数据段结尾设置为addr,即间断点设置为addr。sbrk()将程序数据空间增加increment字节。当increment为0时则返回程序间断点的当前位置。

返回值:
brk()成功返回0,失败返回-1并且设置errno值为ENOMEM(注:在mmap中会提到)。
sbrk()成功返回之前的程序间断点地址。如果间断点值增加,那么这个指针(指的是返回的之前的间断点地址)是指向分配的新的内存的首地址。如果出错失败,就返回一个指针并设置errno全局变量的值为ENOMEM。

总结:
这两个函数都用来改变 “program break” (程序间断点)的位置,改变数据段长度(Change data segment size),实现虚拟内存到物理内存的映射。
brk()函数直接修改有效访问范围的末尾地址实现分配与回收。sbrk()参数函数中:当increment为正值时,间断点位置向后移动increment字节。同时返回移动之前的位置,相当于分配内存。当increment为负值时,位置向前移动increment字节,相当与于释放内存,其返回值没有实际意义。当increment为0时,不移动位置只返回当前位置。参数increment的符号决定了是分配还是回收内存。而关于program break的位置如图所示:

(2)、简单测试:
对于分配好的内存,我们只要有其首地址old与长度MAX*MAX即可不越界的准确使用(如下图所示),其效果与malloc相同,只不过sbrk()与brk()是C标准函数的底层实现而已,其机制较为复杂(测试中,死循环是为了查看maps文件,不至于进程消亡文件随之消失)。

虽然,sbrk()与brk()均可分配回收兼职,但是我们一般用sbrk()分配内存,而用brk()回收内存,上例中回收内存可以这样写:

int err = brk(old);
/**或者brk(p);效果与sbrk(-MAX*MAX);是一样的,但brk()更方便与清晰明了。**/
if(-1 == err){
perror("brk");
exit(EXIT_FAILURE);
}

2、mmap()与munmap():
mmap函数(地址映射):mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零(Linux堆空间未使用内存均清零)。这里我们只研究mmap的内存映射,而暂时不讨论文件方面的问题。关于mmap的文件映射的更详细的内容可参考认真分析mmap:是什么 为什么 怎么用

//函数原型:
#incldue<sys/mman.h>
void * mmap(void * addr, size_t length,int prot,int flags,int fd,off_t offset);

参数:

(1)、addr:
起始地址,置零让系统自行选择并返回即可.
(2)、length:
长度,不够一页会自动凑够一页的整数倍,我们可以宏定义#define MIN_LENGTH_MMAP 4096为一页大小
(3)、prot:
读写操作权限,PROT_READ可读、PROT_WRITE可写、PROT_EXEC可执行、PROT_NONE映射区域不能读取。(注意PROT_XXXXX与文件本身的权限不冲突,如果在程序中不设定任何权限,即使本身存在读写权限,该进程也不能对其操作)
(4)、flags常用标志:
①MAP_SHARED【share this mapping】、MAP_PRIVATE【Create a private copy-on-write mapping】
MAP_SHARED只能设置文件共享,不能地址共享,即使设置了共享,对于两个进程来说,也不会生效。而MAP_PRIVATE则对于文件与内存都可以设置为私有。
②MAP_ANON【Deprecated】、MAP_ANONYMOUS:匿名映射,如果映射地址需要加该参数,如果不加默认映射文件。MAP_ANON已经过时,只需使用MAP_ANONYMOUS即可
(5)、文件描述符:fd
(6)、文件描述符偏移量:offset
(fd和offset对于一般性内存分配来说设置为0即可)

返回值:

失败返回MAP_FAILED,即(void * (-1))并设置errno全局变量。
成功返回指向mmap area的指针pointer。

常见errno错误:

①ENOMEM:内存不足;
②EAGAIN:文件被锁住或有太多内存被锁住;
③EBADF:参数fd不是有效的文件描述符;
④EACCES:存在权限错误,。如果是MAP_PRIVATE情况下文件必须可读;使用MAP_SHARED则文件必须能写入,且设置prot权限必须为PROT_WRITE。
⑤EINVAL:参数addr、length或者offset中有不合法参数存在。

munmap函数:解除映射关系
int munmap(void * addr, size_t length);//addr为mmap函数返回接收的地址,length为请求分配的长度。

这张图描述了mmap内存地址映射的位置关系(栈区以上为内核空间)。关于这一点我们可以作以简单的测试(我采用MIN_LENGTH_MMAP宏,当然你也可以用多少申请多少,系统总是以最小1页来映射的,关于内存分页与虚拟地址映射可参考Linux系统内存管理与内存分页机制 ):

mmap映射的地址处于堆区与栈区中间,malloc映射的堆区内存为33页(最小映射大小),而mmap映射的内存为3页,也是4096B的整数倍。
---------------------

https://blog.csdn.net/Apollon_krj/article/details/54565768

系统调用与内存管理(sbrk、brk、mmap、munmap)(转)的更多相关文章

  1. Linux系统调用之内存管理

    brk 改变数据段空间的分配 sbrk 参见brk mlock 内存页面加锁 munlock 内存页面解锁 mlockall 调用进程所有内存页面加锁 munlockall 调用进程所有内存页面解锁 ...

  2. Linux内存管理 (8)malloc

    专题:Linux内存管理专题 关键词:malloc.brk.VMA.VM_LOCK.normal page.special page. 每章问答: malloc()函数是C函数库封装的一个核心函数,对 ...

  3. 【ARM-Linux开发】Linux内存管理:ARM Memory Layout以及mmu配置

    原文:Linux内存管理:ARM Memory Layout以及mmu配置 在内核进行page初始化以及mmu配置之前,首先需要知道整个memory map. 1. ARM Memory Layout ...

  4. 动态内存管理详解:malloc/free/new/delete/brk/mmap

    c++ 内存获取和释放 new/delete,new[]/delete[] c 内存获取和释放 malloc/free, calloc/realloc 上述8个函数/操作符是c/c++语言里常用来做动 ...

  5. 动态内存管理:malloc/free/new/delete/brk/mmap

    这是我去腾讯面试的时候遇到的一个问题——malloc()是如何申请内存的? c++ 内存获取和释放 new/delete,new[]/delete[] c 内存获取和释放 malloc/free, c ...

  6. Linux内存管理 (9)mmap(补充)

    之前写过一篇简单的介绍mmap()/munmap()的文章<Linux内存管理 (9)mmap>,比较单薄,这里详细的梳理一下. 从常用的使用者角度介绍两个函数的使用:然后重点是分析内核的 ...

  7. 转:Linux内存管理之mmap详解

    一. mmap系统调用 1. mmap系统调用 mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零.munmap执行相 ...

  8. [转载] Linux内存管理之mmap详解

    转载自http://blog.chinaunix.net/uid-26669729-id-3077015.html 一. mmap系统调用 1. mmap系统调用 mmap将一个文件或者其它对象映射进 ...

  9. Linux内存管理 (9)mmap

    专题:Linux内存管理专题 关键词:文件映射.匿名映射.私有映射.共享映射 mmap/munmap是常用的一个系统调用,使用场景是:分配内存.读写大文件.连接动态库文件.多进程间共享内存. 更详细解 ...

随机推荐

  1. kubernetes之configmap

    生成容器内的环境变量 1.创建一个名字叫nginx-config的configmap, 变量名nginx_port的值是80, 变量名server_name的值是www.test.com kubect ...

  2. 不同显卡对mrt 的支持

    ios bits限制大概512bits 低端256bits mali 也是bits限制 2017年 Mali-T760 128bits adreno android显卡4 肯定可以 因为deferre ...

  3. BZOJ 3772: 精神污染 (dfs序+树状数组)

    跟 BZOJ 4009: [HNOI2015]接水果一样- CODE #include <set> #include <queue> #include <cctype&g ...

  4. log4j.properties通用配置

    一.Log4j的配置文件分类Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties(key=value)文件,其中properties格式的配置文件最为常用,其有一个固定的 ...

  5. ORM高阶补充:only, defer,select_related

    Queryset官方文档:https://docs.djangoproject.com/en/1.11/ref/models/querysets/ 1.需求1:只取某n列 1.方法1:values 2 ...

  6. [Spring Boot] Set Context path for application in application.properties

    If you were using Microservice with Spring Boot to build different REST API endpoints, context path ...

  7. 解决蓝牙鼠标在 Ubuntu 中单位时间内断开的问题

    1 查询你的鼠标的蓝牙地址 1.1 如:E1:DE:02:05:5E:F5 2 将查询到的设备地址写入配置文件 /etc/bluetooth/main.conf # Use vendor id sou ...

  8. learning armbian steps(6) ----- armbian 源码分析(一)

    为了深入学习armbian,前面已经学习了如何手动构建arm ubuntu rootfs. 由于armbian官方的文档比较的匮乏,所以最终还是决定通过其编译的过程来深入地学习. 为了快速度深入地学习 ...

  9. 洛谷P4317 花神的数论题

    洛谷题目链接 数位$dp$ 我们对$n$进行二进制拆分,于是就阔以像十进制一样数位$dp$了,基本就是套模板.. 接下来是美滋滋的代码时间~~~ #include<iostream> #i ...

  10. jquery.qrcode.js生成二维码(前端生成二维码)

    官网地址:http://jeromeetienne.github.io/jquery-qrcode/ 第一步引入插件: <script type='text/javascript' src='h ...