Unix/Linux:进程基本概念

转载自:https://blog.csdn.net/weixin_49858366/article/details/119779397


一、进程和程序的区别

简单一句话说明,进程是一个可执行程序的实例
程序包含了一系列信息的文件,这些信息描述了如何在运行时创建一个进程。

一般情况下程序包含以下信息:
  • 二进制格式标识:每个程序文件都包含用于描述可执行文件格式的元信息
    (metainformation)。内核(kernel)利用此信息来解释文件中的其他信息。历史上,UNIX
    可执行文件曾有两种广泛使用的格式,分别为最初的 a.out(汇编程序输出)和更加复
    杂的 COFF(通用对象文件格式)。现在,大多数 UNIX 实现(包括 Linux)采用可执行
    连接格式(ELF),这一文件格式比老版本格式具有更多优点。
  • 机器语言指令:对程序算法进行编码。
  • 程序入口地址:标识程序开始执行时的起始指令位置。
  • 数据:程序文件包含的变量初始值和程序使用的字面常量(literal constant)值(比
    如字符串)。
  • 符号表及重定位表:描述程序中函数和变量的位置及名称。这些表格有多种用途,其
    中包括调试和运行时的符号解析(动态链接)。
  • 共享库和动态链接信息:程序文件所包含的一些字段,列出了程序运行时需要使用的
    共享库,以及加载共享库的动态链接器的路径名。
  • 其他信息:程序文件还包含许多其他信息,用以描述如何创建进程。

从内核角度看,进程由用户内存空间(user-space memory)和一系列内核数据结构组成,其中用户内存空间包含了程序代码及代码所使用的变量,而内核数据结构则用于维护进程状态信息。

通过readelf指令,可以看到以下程序包含的信息:
elf程序包含的信息
下面介绍以下比较常见的一些段:

  • debug段:顾名思义,用于保存调试信息。
  • dynamic段:用于保存动态链接信息。
  • fini段:用于保存进程退出时的执行程序。当进程结束时,系统会自动执行这部分代码。
  • init段:用于保存进程启动时的执行程序。当进程启动时,系统会自动执行这部分代码。
  • rodata段:用于保存只读数据,如const修饰的全局变量、字符串常量。
  • symtab段:用于保存符号表。

其中,对于与调试相关的段,如果不使用-g选项,则不会生成,但是与符号相关的段仍然会存在,这时可以使用strip去掉符号信息。一般在嵌入式中,为了减少程序占用的空间,都会使用strip去掉非必要的段。

二、进程的内存布局

每个进程所分配的内存由下面5部分组成:

  • 文本段:包含了进程运行的程序机器语言指令。文本段具有只读属性,以防止进程通过错
    误指针意外修改自身指令。
  • 初始化数据段:包含显式初始化的全局变量和静态变量。
  • 未初始化数据段:包含了未进行显式初始化的全局变量和静态变量。程序启动之前,系统将本段内所有内存初始化为 0。这一段也就是我们常说的BBS段(block started by symbol)。之所以将数据按照初始化和未初始化分开来存储的原因,程序没有必要为没有初始化的变量分配存储空间,只需要记录好未初始化数据段的位置以及大小,等程序运行起来之后,再分配这一段空间。
  • 栈(stack):是一个动态增长和收缩的段,由栈帧(stack frames)组成。系统会为每个当前调用的函数分配一个栈帧。栈帧中存储了函数的局部变量(所谓自动变量)、实参和返回值。
  • 堆(heap):是可在运行时(为变量)动态进行内存分配的一块区域。

在 Linux/x86-32 中典型的进程内存结构

三、虚拟内存管理

Linux,像多数现代内核一样,采用了虚拟内存管理技术(上面我们提到了进程内存布局,这一布局就存在于虚拟内存中)。该技术利用了大多数程序的一个典型特征,即访问局部性(locality of reference),以求高效使用 CPU 和 RAM(物理内存)资源。大多数程序都展现了两种类型的局部性。
  • 空间局部性(Spatial locality):是指程序倾向于访问在最近访问过的内存地址附近的内存(由于指令是顺序执行的,且有时会按顺序处理数据结构)。
  • 时间局部性(Temporal locality):是指程序倾向于在不久的将来再次访问最近刚访问过的内存地址(由于循环)。

正是由于访问局部性特征,使得程序即便仅有部分地址空间存在于 RAM 中,依然可能得以执行。

虚拟内存的规划之一是将每个程序使用的内存切割成小型的、固定大小的“页”(page)单元。相应地,将 RAM 划分成一系列与虚存页尺寸相同的页帧。任一时刻,每个程序仅有部分页需要驻留在物理内存页帧中。这些页构成了所谓驻留集(resident set)。程序未使用的页拷贝保存在交换区(swap area)内—这是磁盘空间中的保留区域,作为计算机 RAM 的补充—仅在需要时才会载入物理内存。若进程欲访问的页面目前并未驻留在物理内存中,将会发生页面错误(page fault),内核即刻挂起进程的执行,同时从磁盘中将该页面载入内存。

为支持这一组织方式,内核需要为每个进程维护一张页表(page table)(见下图)。该页表描述了每页在进程虚拟地址空间(virtual address space)中的位置(可为进程所用的所有虚拟内存页面的合)。页表中的每个条目要么指出一个虚拟页面在 RAM 中的所在位置,要么表明其当前驻留在磁盘上。

虚拟内存-页表

在进程虚拟地址空间中,并非所有的地址范围都需要页表条目。通常情况下,由于可能存在大段的虚拟地址空间并未投入使用,故而也无必要为其维护相应的页表条目。若进程试图访问的地址并无页表条目与之对应,那么进程将收到一个 SIGSEGV 信号。

由于内核能够为进程分配和释放页(和页表条目),所以进程的有效虚拟地址范围在其生命周期中可以发生变化。这可能会发生于如下场景。

  • 由于栈向下增长超出之前曾达到的位置
  • 当在堆中分配或释放内存时,通过调用 brk()、sbrk()或 malloc 函数族来提升 program break 的位置。
  • 当调用 shmat()连接 System V 共享内存区时,或者当调用 shmdt()脱离共享内存区时
  • 当调用 mmap()创建内存映射时,或者当调用 munmap()解除内存映射时

虚拟内存的实现需要硬件中分页内存管理单元(PMMU)的支持。PMMU 把要访问的每个虚拟内存地址转换成相应的物理内存地址,当特定虚拟内存地址所对应的页没有驻留于 RAM 中时,将以页面错误通知内核。

虚拟内存管理使进程的虚拟地址空间与 RAM 物理地址空间隔离开来,这带来了许多的优点:

  • 进程与进程、进程与内核相互隔离,所以一个进程不能读取或修改另一进程或内核的内存。这是因为每个进程的页表条目指向 RAM(或交换区)中截然不同的物理页面集合。
  • 适当情况下,两个或者更多进程能够共享内存。这是由于内核可以使不同进程的页表条目指向相同的 RAM 页。例如,使用 shmget()和 mmap()共享内存或者通过程序的多个进程可以共享一份程序代码副本
  • 因为需要驻留在内存中的仅是程序的一部分,所以程序的加载和运行都很快。而且,一个进程所占用的内存(即虚拟内存大小)能够超出 RAM 容量。由于每个进程使用的 RAM 减少了,RAM 中同时可以容纳的进程数量就增多了,大大提高了CPU的利用率。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值