从内核角度看,进程由用户内存空间和一系列内核数据结构组成,其中用户内存空间包含了程序代码及代码所使用的变量,而内核数据结构则用于维护进程状态信息。
为了使进程摆脱系统内存的制约,用户进程运行在虚拟内存之上。
实现虚拟内存的关键在于建立虚拟地址与物理地址之间的关系,因为无论如何数据要存储到物理内存中才能被记录下来。
图1 虚拟内存与物理内存之间的对应关系
如图1,进程1和进程2拥有完整的虚拟地址空间,虚拟地址空间分为了用户空间和内核空间,对于不同的进程面对的都是同一内核,所以其内核空间的VA K都映射到了物理内存的PA K地址。而不同的进程的用户空间是不同的,进程1和进程2的用户空间的虚拟地址VA 1和VA 2分别映射到了不同的物理地址PA 1 和 PA 2上。
一、进程内存布局
图2 进程内存布局
上图中灰色区域表示这些范围在进程虚拟地址空间中不可用。
Linux下一个进程在内存里有三部分的数据,就是"代码段"、“堆栈段"和"数据段”。
其实学过汇编语言的人一定知道,一般的CPU都有上述三种段寄存器,以方便操作系统的运行。这三个部分也是构成一个完整的执行序列的必要的部分。
1、“代码段”,顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。
2、“堆栈段”,存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。
3、“数据段”,则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。
系统如果同时运行数个相同的程序,它们之间就不能使用同一个堆栈段和数据段。
逻辑上将一个进程划分为以下几部分(也称为段):
- 文本:程序的机器语言指令。文本段具有只读属性,以防止进程通过错误指针意外修改自身指令。因为多个进程可同时运行同一程序,所以又将文本段设为可共享
- 数据:程序使用的静态变量。当程序加载到内存时,从可执行文件中读取这些变量的值。
- 未初始化数据段(BBS):包含了未进行显示初始化的全局变量和静态变量。程序启动之前,系统将本段内所有内存初始化为0。“block started by symbol”。将经过初始化的全局变量和静态变量与未初始化的全局变量和静态变量分开存放,主要原因是程序在磁盘上存储时,没有必要为未经初始化的变量分配存储空间。相反,可执行文件只需记录未初始化数据段的位置及所需大小,直到运行时再由程序加载器来分配空间。
- 堆:程序可从该区域动态分配额外内存。在运行时为变量动态进行内存分配的一块区域。
- 栈:随函数调用、返回而增减的一片内存,由栈帧组成。系统会为每个当前调用的函数分配一个栈帧。栈帧存储了函数的局部变量、实参和返回值。
在大多数Unix(包括Linux)中的C语言编程环境提供了3个全局符号(symbol):etext(程序文本段结尾处下一字节的地址)、edata(初始化数据段结尾处下一字节的地址)、end(非初始化数据段结尾处下一字节的地址)
为了使进程摆脱系统内存的制约,用户进程运行在虚拟内存之上,每个用户进程都拥有完整的虚拟地址空间,互不干涉。
例子:代码虽多但是很简单,看代码时请不要带有负担
AndroidManifest.xml:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--可以看出SecondActivity是在另一个进程中的-->
<activity android:name=".SecondActivity"
android:process=".secondActivity"/>
<activity android:name=".ThirdActivity"/>
ToolUtils.java:
public class ToolUtils {
public static int constantNum = 666;
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
Button btn_2, btn_3;