虚拟地址空间

        对于每一个进程都会对应一个虚拟地址空间,对于32位的操作系统(其指令的位数最大为32位,因此地址码最多32位),虚拟地址空间的大小为2^{32}B即0~4GB的虚拟地址空间,其中内核空间为1GB,如下所示:

         每一个进程的进程控制块PCB都位于内核区,在每一个进程的PCB中有一个文件描述符表(是一个数组),用于标记该进程所打开的所有文件。从文件描述符表可以看出每一个进程最多能打开1024个文件,其中有三个文件默认是一直处于打开状态的(即进程创建完成时就处于打开状态),分别是:标准输入 STDIN_FILENO,其文件描述符为0;标准输出 STDOUT_FILENO,其文件描述符为1;错误输出 STDERR_FILENO,其文件描述符为2,其中文件描述符0和1可以省略不写。供我们用户打开的文件,只能够占据从3开始的位置(即其文件描述符为3以后的数字,3~1023)。每打开一个文件就会占用一个文件描述符,且使用的是空闲的最小的一个文件描述符。

        Linux下可执行文件的格式为ELF:[root@localhost Calc]# file zsx
                                                                     zsx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x14ef2d34126e7c54141b73c31968bd825ca522ba, not stripped           //可以看出zsx为64位(即机器指令位数为64位,OS位数)的可执行文件,其格式为ELF。

        对于每一个程序在执行时(如上图中的a.out),此时会产生一个相应的进程,系统都会自动为其分配一个0~4G的虚拟地址空间,其中1G的内核空间用于:进程管理、内存管理、设备管理和虚拟文件系统等。下面详细介绍0~3G的用户空间。

         强调一点:以下说明的各段都是与编程相关的,不包括虚拟地址空间的全部。

        0~3G的用户空间。从小到大(从下往上)依次为:保留区(受保护的地址)、代码段、数据段(.data段)、.bss段、堆空间、内存映射段、栈空间、命令行参数和环境变量。下面依次对每一个段做简单的介绍:

1.保留区(受保护的地址)

        保留区即为受保护的地址,大小为0~4K,位于虚拟地址空间的最低部分,未赋予物理地址(不会与内存地址相对应,因此其不会放任何内容)。任何对它的引用都是非法的,用于捕捉使用空指针和小整型值指针引用内存的异常情况。大多数操作系统中,极小的地址通常都是不允许访问的,如NULL。C语言将无效指针赋值为0也是出于这种考虑,因为0地址上正常情况下不会存放有效的可访问数据。将指针赋值为0,意味着该指针将永远不会被使用,从而不会出现野指针情况。#define NULL 0 与 #define NULL (void*)0   在C语言中是等效的,而在C++中,只能用#define NULL 0,后面 #define NULL (void*)0的使用会出错。

2.代码段

        代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。某些架构也允许代码段为可写,即允许修改程序。  

3.数据段(.data段)

        数据段通常用于存放程序中已初始化的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。由于全局变量未初始化时,其默认值为0,因此值为0的全局变量位于.bbs段(不位于数据段)。对于未初始化的局部变量,其值是不可预测的。注意:在代码段和数据段之间还包括其它段:只读数据段和符号段等。

4..bbs段

        该段用于存放未初始化的全局变量和静态局部变量,包括值为0的全局变量。 数据段和.bbs段又称为全局数据区,前者初始化,后者未初始化。

        ELF段包括:代码段、其它段(只读数据段和符号段等)、.data段(数据段)和.bbs段,都属于可执行程序部分。

5.堆空间

        new( )和malloc( )函数分配的空间就属于对空间,用于内存空间的分配,其从下往上。  堆用于存放进程运行时动态分配的内存段,可动态扩张或缩减。堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。当进程调用malloc(C) 和new (C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free(C)/delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减) 。

6.内存映射段(共享库)

        此处,内核将硬盘文件的内容直接映射到内存, 任何应用程序都可通过Linux的mmap()系统调用请求这种映射。内存映射是一种方便高效的文件I/O方式, 因而被用于装载动态共享库。如C标准库函数(fread、fwrite、fopen等)和Linux系统I/O函数,它们都是动态库函数,其中C标准库函数都被封装在了/lib/libc.so库文件中,都是二进制文件。这些动态库函数都是与位置无关的代码,即每次被加载进入内存映射区时的位置都是不一样的,因此使用的是其本身的逻辑地址,经过变换成线性地址(虚拟地址),然后再映射到内存。而静态库不一样,由于静态库被链接到可执行文件中,因此其位于代码段,每次在地址空间中的位置都是固定的。

7.栈空间

        用于存放局部变量(非静态局部变量,C语言称为自动变量),分配存储空间时从上往下。栈和堆都是后进先出的数据结构。

8.命令行参数

        该段用于存放命令行参数的内容:argc和argv。

9.环境变量

        用于存放当前的环境变量,在Linux中用env命令可以查看其值。

10.虚拟地址空间的作用(好处)

        1.方面编译器和操作系统安排程序的地址;2.方便实现各个进程空间之间的隔离,互不干扰,因为每个进程都对应自己的虚拟地址空间;3.实现虚拟存储,从逻辑上扩大了内存。

补充内容:

代码段(.text段)与只读数据段和符号段(.rodata段),都属于只能读的部分,在链接的时候这两部分会链接成为一个整体;而.data段和.bbs段属于可读可写RW的部分。这四个部分都是以页(每页4KB)的形式存放在内存中。进程控制块PCB(又叫进程描述符)放于内核空间

多个进程在并发执行时,这些进程的用户空间都是彼此独立的,因此各个进程的用户空间在映射为内存空间使都是独立的,互不干扰,这是MMU地址变换必须要能够保证的。例如,各个进程的.text段、只读数据段和符号段、.data段和.bbs段等在用户空间中使用到的其它数据信息,都会与页为基本单位放在内存中,各个进程的映射是独立的。而对于内核空间,由于只有一个操作系统,内核空间主要是 机器指令、操作系统内核的各个模块等,它们是公用的,因此每个进程的映射方式一样。强调一点:每个进程用到或即将用到的数据才会调入内存,其余都在磁盘上。但是各个进程内核空间的进程控制块(进程描述符)映射的地点是不一样的,也是相互独立的。共用的模块才是一样的。 这些都是MMU的实现机制所决定的。如果感兴趣,可以看看MMU的实现机制。

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

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

相关文章

Leecode 69. x 的平方根

实现 int sqrt(int x) 函数。 计算并返回 x 的平方根,其中 x 是非负整数。 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 示例 1: 输入: 4 输出: 2 示例 2: 输入: 8 输出: 2 说明: 8 的平方根是 2.82842..., 由于返回类…

1002. 写出这个数 (20)

读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字。 输入格式:每个测试输入包含1个测试用例,即给出自然数n的值。这里保证n小于10100。 输出格式:在一行内输出n的各位数字之和的每一位,拼…

C/C++中NULL指针

先谈一下C/C的强制类型转换Type cast。与强制类型转换相对应的是自动类型转换。或者强制类型转换叫显示类型转换,自动类型转换叫隐式类型转换。自动类型转换会在赋值运算、混合运算、参数传递、返回函数返回值、格式化输出时且当类型出现不一致时发生,转…

1009. 说反话 (20)

给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。 输入格式:测试输入包含一个测试用例,在一行内给出总长度不超过80的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有区…

动态库(共享库)的制作和使用

Linux下的动态库为lib*.so格式的二进制文件(目标文件),对应于Windows下的.dll格式的文件。 (1)命名规则 lib库名.so (2)动态库的制作 1)生成与位置无关的代码(.o&…

孤儿进程、僵尸进进程

一、儿进程与僵尸进程 1、基本概念 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工…

管道 -pipe

gcc编译器将源代码编译成可执行程序的过程中,需要经过许多中间步骤(预处理、编译、汇编、链接),这些过程实际上是由不同的程序来负责完成的(/usr/bin/gcc、cpp、ccl、as和ld等)。在这个过程的每一个阶段中&…

gdb调试器(一)

在默认情况下,gcc在编译时不会把调试符号插入到最终生成的二进制代码(机器代码)中,因为这样会急剧增加可执行程序的大小。如果需要在编译时生成调试符号的信息,则可以采用-g或-ggdb参数。 gcc在产生调试信息时&#x…

1048. 数字加密(20)

本题要求实现一种数字加密方法。首先固定一个加密用正整数A,对任一正整数B,将其每1位数字与A的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对13取余——这里用J代表10、Q代表11、K代表12;对偶数位&…

网络编程套接字API

uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);int inet_pton(int family, const char *strptr, void *addrptr); 分析: 第一个参数可以是AF_INET或AF_INET6&am…

gdb调试器(二)

Linux下的gdb(GNU Debugger)是一个用来调试C、C程序的调试器(命令行方式的调试器),能够在程序运行期间观察程序的内部结构和内存的使用情况。程序员也可以使用gdb来跟踪程序中的错误,从而减少程序员的工作量…

gdb调试器(三)

File/file 装入想要调试的可执行文件 run(r) 执行当前被调试的程序 kill(k) 终止正在调试的程序 quit(q) 退出gdb shell 使用户不离开gdb就可以执行Linux的shell命令 backtrace(bt) 回溯跟踪(当对代码进行调试时,run后…

IO多路复用之poll

1. poll函数原型: int poll(struct pollfd *fds, nfds_t nfds, int timeout);参数: fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件 nfds&#x…

makefile文件的书写规则(make和makefile)

对于makefile,掌握一个规则,两个变量和三个函数。下面介绍一个规则。 makefile的作用:一个项目代码的管理工具。当一个项目的代码文件数(如.c文件)太多,用gcc编译会太麻烦,如果全部文件一次性编…

100. 相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 示例 1: 输入: 1 1 / \ / \ 2 3 2 3 [1,2,3], [1,2,3] 输出: true…

makefile的两个变量(自动变量和普通变量)

(1)普通变量 如: objmain.o add.o sub.o mul.o div.o //将后面的值赋值给obj,obj就是一个普通变量 targetzsx //将zsx赋值给target makefile中已经定义的一些普通变量(通常格式都是大写,类似环境变量,它们都是普通…

【C++ Priemr | 15】虚函数表剖析(二)

一、多重继承&#xff08;无虚函数覆盖&#xff09; 下面&#xff0c;再让我们来看看多重继承中的情况&#xff0c;假设有下面这样一个类的继承关系。注意&#xff1a;子类并没有覆盖父类的函数。 测试代码&#xff1a; class Base1 { public: virtual void f() { cout <…

1074. Reversing Linked List (25)

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K 3, then you must output 3→2→1→6→5→4; if K 4, you must output 4→3→2→1→5→6. Input Spe…

【Leetcode | 47】 222. 完全二叉树的节点个数

给出一个完全二叉树&#xff0c;求出该树的节点个数。 说明&#xff1a; 完全二叉树的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位置。若最…

makefile中的两个函数(wildcard和patsubst)

(1) wildcard函数 作用是查找指定目录下指定类型的文件&#xff0c;并最终返回一个环境变量&#xff0c;需要用$取值赋值给另一个环境变量&#xff01;该函数只有一个参数&#xff0c;如取出当前目录下的所有.c文件&#xff0c;并赋值给allc普通变量&#xff1a; allc$(wildc…