Linux进程如何使用内存

2024-04-26 19:18
文章标签 linux 内存 进程 使用

本文主要是介绍Linux进程如何使用内存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

进程如何使用内存?

毫无疑问,所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。

对任何一个普通进程来讲,它都会涉及到5种不同的数据段。稍有编程知识的朋友都能想到这几个数据段中包含有“程序代码段”、“程序数据段”、“程序堆栈段”等。不错,这几种数据段都在其中,但除了以上几种数据段之外,进程还另外包含两种数据段。下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区。

代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作——它是不可写的。

数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配[1]的变量和全局变量。

BSS段[2]:BSS段包含了程序中未初始化的全局变量,在内存中 bss段全部置零。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

:栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

进程如何组织这些区域?

上述几种内存区域中数据段、BSS和堆通常是被连续存储的——内存位置上是连续的,而代码段和栈往往会被独立存放。有趣的是,堆和栈两个区域关系很“暧昧”,他们一个向下“长”(i386体系结构中栈向下、堆向上),一个向上“长”,相对而生。但你不必担心他们会碰头,因为他们之间间隔很大(到底大到多少,你可以从下面的例子程序计算一下),绝少有机会能碰到一起。

下图简要描述了进程内存区域的分布:

“事实胜于雄辩”,我们用一个小例子(原形取自《User-Level Memory Management》)来展示上面所讲的各种内存区的差别与位置。

#include<stdio.h>

#include<malloc.h>

#include<unistd.h>

int bss_var;

int data_var0=1;

int main(int argc,char **argv)

{

  printf("below are addresses of types of process's mem\n");

  printf("Text location:\n");

  printf("\tAddress of main(Code Segment):%p\n",main);

  printf("____________________________\n");

  int stack_var0=2;

  printf("Stack Location:\n");

  printf("\tInitial end of stack:%p\n",&stack_var0);

  int stack_var1=3;

  printf("\tnew end of stack:%p\n",&stack_var1);

  printf("____________________________\n");

  printf("Data Location:\n");

  printf("\tAddress of data_var(Data Segment):%p\n",&data_var0);

  static int data_var1=4;

  printf("\tNew end of data_var(Data Segment):%p\n",&data_var1);

  printf("____________________________\n");

  printf("BSS Location:\n");

  printf("\tAddress of bss_var:%p\n",&bss_var);

  printf("____________________________\n");

  char *b = sbrk((ptrdiff_t)0);

  printf("Heap Location:\n");

  printf("\tInitial end of heap:%p\n",b);

  brk(b+4);

  b=sbrk((ptrdiff_t)0);

  printf("\tNew end of heap:%p\n",b);

return 0;

 }

它的结果如下

below are addresses of types of process's mem

Text location:

   Address of main(Code Segment):0x8048388

____________________________

Stack Location:

   Initial end of stack:0xbffffab4

   new end of stack:0xbffffab0

____________________________

Data Location:

   Address of data_var(Data Segment):0x8049758

   New end of data_var(Data Segment):0x804975c

____________________________

BSS Location:

   Address of bss_var:0x8049864

____________________________

Heap Location:

   Initial end of heap:0x8049868

   New end of heap:0x804986c

利用size命令也可以看到程序的各段大小,比如执行size example会得到

text data bss dec hex filename

1654 280   8 1942 796 example

但这些数据是程序编译的静态统计,而上面显示的是进程运行时的动态值,但两者是对应的。

进程的内存空间:

Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。该空间是块大小为4G的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能起到保护操作系统的效果(用户不能直接访问物理内存),而且更重要的是,用户程序可使用比实际物理内存更大的地址空间 

在讨论进程空间细节前,这里先要澄清下面几个问题:

l         第一、4G的进程地址空间被人为的分为两个部分——用户空间与内核空间。用户空间从0到3G(0xC0000000),内核空间占据3G到4G。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。只有用户进程进行系统调用(代表用户进程在内核态执行)等时刻可以访问到内核空间。

l         第二、用户空间对应进程,所以每当进程切换,用户空间就会跟着变化;而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间地址有自己对应的页表(init_mm.pgd),用户进程各自有不同的页表。

l         第三、每个进程的用户空间都是完全独立、互不相干的。不信的话,你可以把上面的程序同时运行10次(当然为了同时运行,让它们在返回前一同睡眠100秒吧),你会看到10个进程占用的线性地址一模一样。

进程所能直接操作的地址都为虚拟地址。当进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存,获得的仅仅是对一个新的线性地址区间的使用权。实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请求页机制”产生“缺页”异常,从而进入分配实际页面的例程 。

当应用程序访问一个虚拟地址时,首先必须将虚拟地址转化成物理地址,然后处理器才能解析地址访问请求。地址的转换工作需要通过查询页表才能完成,概括地讲,地址转换需要将虚拟地址分段,使每段虚地址都作为一个索引指向页表,而页表项则指向下一级别的页表或者指向最终的物理页面。每个进程都有自己的页表。进程描述符的pgd域指向的就是进程的页全局目录。

物理内存管理(页管理)

物理内存是由Linux内核通过分页机制管理实现的,它将整个内存划分成无数个4k(在i386体系结构中)大小的页,从而分配和回收内存的基本单位便是内存页了。利用分页管理有助于灵活分配内存地址,因为分配时不必要求必须有大块的连续内存,系统可以东一页、西一页的凑出所需要的内存供进程使用。虽然如此,但是实际上系统使用内存时还是倾向于分配连续的内存块,因为分配连续内存时,页表不需要更改,因此能降低TLB的刷新率(频繁刷新会在很大程度上降低访问速度)。

 

 

 

 

这篇关于Linux进程如何使用内存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/938429

相关文章

使用图网络和视频嵌入预测物理场

文章目录 一、说明二、为什么要预测?三、流体动力学模拟的可视化四、DeepMind神经网络建模五、图形编码六、图形处理器七、图形解码器八、具有不同弹簧常数的轨迹可视化九、预测的物理编码和推出轨迹 一、说明 这是一篇国外流体力学专家在可视化流体物理属性的设计实现。我们知道物理模型多半是微分方程,而将微分粒子看成是训练数据,能否得出解的近似形式?本篇给出这样的启发。对于重事科研工作的

Python代码生成类型注解库之monkeytype使用详解

概要 MonkeyType 是由 Instagram 开发的一个 Python 库,用于自动为 Python 代码生成类型注解。通过监控运行时的类型信息,MonkeyType 能够帮助开发者为现有的 Python 代码库增加类型提示,从而提高代码的可读性和健壮性。 安装 安装 MonkeyType 非常简单,可以通过 pip 进行安装: pip install monkey

程序运行内存占用不断增大

1.考虑有没有日志类,如果程序在运行时不断向控件中写日志,而又没有采用控制日志条目,则内存会一直增大。

Linux-shell编程之声明变量类型与变量四则运算

一 、declare声明变量类型 在shell中如果不声明变量类型默认是字符串类型,但是字符串是无法进行四则运算的。所以要进行运算须声明变量为数字类型。 格式:declare [+/-][选项] 变量名  -             # 给变量设定类型属性 +             # 取消变量的类型属性 -a            # 将变量声明为数组型 -i

Split参数StringSplitOptions.RemoveEmptyEntries的使用

string test = "程$晓$";   使用:string[] temp = test.Split(new string[] { "$" }, StringSplitOptions.RemoveEmptyEntries);   输出结果:数组长度为2 temp[0]="程" temp[1]="晓";     使用:string[] temp = test.Split

渗透测试工具--AppInfoScanner 的安装与使用(一)

简洁 ApplicationScanner是一个快速稳定的App代码扫描工具,其主要功能是对ipa和apk文件进行扫描,以快速发现存在风险的代码。这款工具基于Python 3.7实现其主要功能,同时apk检测部分需要JDK 11的支持,因此它具备较好的跨平台特性,目前支持在Linux和Mac系统上使用,但暂不支持Windows。 通过ApplicationScanner进行扫描,其检测项目与等

远程监控linux系统信息内存,cpu

一、主要指令 当需要监控linux的系统运行状态时,不免要获取部分系统信息如内存cpu等,主要指令有: 1.top 用于获取cpu等信息,类似于windows上面的任务管理器 2.free 用于获取内存信息,包括可用内存等 3.df用于获取瓷盘信息,如瓷盘的使用空间和剩余空间等 二、命令执行 这些指令要如何通过windows上的程序下发到Linux执行并获取返回值呢,这里需要用到ssh

.net 中使用Quartz任务调度类

Quartz.net是作业调度框架 Quartz可以用来做什么? Quartz是一个任务调度框架。比如你遇到这样的问题 想每月25号,信用卡自动还款想每年4月1日自己给当年暗恋女神发一封匿名贺卡想每隔1小时,备份一下自己的爱情动作片 学习笔记到云盘 这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一

Linux实操之常用指令详解

文章目录 vi 和 vimvi 和 vim 基本使用 开机、重启和用户登录注销关机&重启命令用户登录和注销 用户管理基本介绍基本语法细节说明修改密码删除用户查询用户信息指令切换用户查看当前用户/登录用户用户组 实用指令指定运行级别帮助指令文件目录类时间和日期类搜索查找类压缩和解压类 组管理和权限管理Linux 组基本介绍权限的基本介绍 vi 和 vim 正常模式 以 vim

linux远程登陆、远程root账户登陆及端口号更改

远程登录linux 远程登陆需要开启ssh服务,开启完成后,即可用命令行登陆。 安装ssh服务命令如下,安装完成后重启ssh服务或重启机器。 sudo apt-get install openssh-server 远程登陆命令为 ssh username(用户名)@hostip(主机ip),此时使用端口为默认端口22ssh -p port username@hostip,此时使用的端