天津市网站建设 网页制作,在线简易网页制作网站,全国企业信用信息公示系统河南,潍坊建设公司目录
前言#xff1a;
进程优先级
基本概念
查看系统进程
PRI与NI
查看进程优先级信息
通过top命令更改进程的nice值
通过renice命令更改进程的nice值
四个重要概念
环境变量
基本概念
常见环境变量
查看环境变量的方法
测试PATH
测试HOME
测试SHELL
编辑 …目录
前言
进程优先级
基本概念
查看系统进程
PRI与NI
查看进程优先级信息
通过top命令更改进程的nice值
通过renice命令更改进程的nice值
四个重要概念
环境变量
基本概念
常见环境变量
查看环境变量的方法
测试PATH
测试HOME
测试SHELL
编辑 和环境变量相关的命令
环境变量的组织方式
通过代码获取环境变量
通过系统调用获取环境变量
如何理解本地变量只会在本bash内部有效不会被子进程继承访问
程序地址空间 进程地址空间 前言
在Linux进程概念-详细版一中我们解释了什么是进程以及进程的各种状态已经对进程有了一定的认识那么这篇文章将会继续补全上篇文章剩余没有说到的进程优先级环境变量程序地址空间进程地址空间以及调度队列。
进程优先级
基本概念
什么是优先级
优先级实际上就是获取某种资源的先后顺序而进程优先级实际上就是进程获取CPU资源分配的先后顺序就是指进程的优先权priority优先权高的进程有优先执行的权力。
优先级存在的原因
优先级存在的主要原因就是资源是有限的目前大部分的电脑是只有一个CPU的所有单个电脑的CPU资源是有限。但的在规定上一个CPU在同一时刻只能执行一个进程的指令但这并不意味着它只能运行一个进程而进程是可以有多个的所以需要存在进程优先级来确定进程获取CPU资源的先后顺序。
查看系统进程
在Linux或者Unix操作系统中用ps -l命令会类似输出以下几个内容这在我们一中已经详细介绍了
ps -l 列出的信息当中有几个重要的信息如下
UID代表执行者的身份。PID代表这个进程的代号。PPID代表这个进程是由哪个进程发展衍生而来的亦即父进程的代号。PRI代表这个进程可被执行的优先级其值越小越早被执行。NI代表这个进程的nice值。后面会介绍什么是nice值
PRI与NI
PRI代表进程的优先级priority通俗点说就是进程被CPU执行的先后顺序该值越小进程的优先级别越高。NI代表的是nice值其表示进程可被执行的优先级的修正数值。PRI值越小越快被执行当加入nice值后将会使得PRI变为PRI(new) PRI(old) NI。若NI值为负值那么该进程的PRI将变小即其优先级会变高。调整进程优先级在Linux下就是调整进程的nice值。NI的取值范围是-20至19一共40个级别。 注意 在Linux操作系统当中PRI(old)默认为80即PRI(new) 80(old) NI。
查看进程优先级信息
当我们创建一个进程后我们可以使用ps -al命令查看该进程优先级的信息。
ps -al 通过top命令更改进程的nice值
top命令就相当于Windows操作系统中的任务管理器它能够动态实时的显示系统当中进程的资源占用情况。
top
输入top指令后会看到如下 然后按“r”键会要求你输入待调整nice值的进程的PID。 输入进程PID并回车后会要求你输入调整后的nice值。 输入nice值后按“q”即可退出如果我们这里输入的nice值为10那么此时我们再用ps命令查看进程的优先级信息即可发现进程的NI变成了10PRI变成了9080NI。
注意 若是想将NI值调为负值也就是将进程的优先级调高需要使用sudo命令提升权限。
通过renice命令更改进程的nice值
使用renice命令后面跟上更改后的nice值和进程的PID即可。 注意 若是想使用renice命令将NI值调为负值也需要使用sudo命令提升权限。
四个重要概念
竞争性 系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便有了优先级。独立性 多进程运行需要独享各种资源多进程运行期间互不干扰。并行 多个进程在多个CPU下分别同时进行运行这称之为并行。并发 多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发。
环境变量
基本概念
环境变量environment variables一般是指在操作系统中用来指定操作系统运行环境的一些参数。 例如我们编写的C/C代码在各个目标文件进行链接的时候从来不知道我们所链接的动静态库在哪里但是照样可以链接成功生成可执行程序原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途并且在系统当中通常具有全局特性。 其实在一开始我听到环境变量时也是很懵懵的但是后面也是听了两三遍才反应过来这里为了大家更好理解就拿我们Windows下的环境进行举一个游戏例子说明一下。 首先我们查看我们Windows下已经下载的环境变量 打开设置中的系统点开高级系统设置 点击环境变量 就可以看到你的电脑的环境变量了。 我本人的电脑是安装了c/python/java/c#的环境变量的。 这里就拿我的世界举例说明是环境变量。 使用 PCL 启动器如 TLauncher 或 MultiMC 等玩 Minecraft Java 版 时通常需要确保你的电脑上已经安装了适当的 Java 环境。Java 环境变量的设置在大多数情况下是必需的尤其是在使用 PCL 启动器 时。 为什么需要配置 Java 环境变量 Minecraft Java 版需要 Java Runtime Environment (JRE) 或 Java Development Kit (JDK) 来运行。通常Minecraft 会自动识别和配置 Java但如果你的系统上没有正确设置环境变量或者如果你安装了多个 Java 版本可能会导致 Minecraft 无法找到 Java或者使用错误的 Java 版本。 又比如说我们使用vs编译器适配c的编译环境的时候其实在vs上下载的时候就适配好了c的环境变量。 所以说环境变量其实就是为了让在各个目标文件进行链接的时候告诉电脑所链接的动静态库在哪里使得生成可执行程序。 在Linux下
常见环境变量
PATH 指定命令的搜索路径。HOME 指定用户的主工作目录即用户登录到Linux系统中的默认所处目录。SHELL 当前Shell它的值通常是/bin/bash。
查看环境变量的方法
我们可以通过echo命令来查看环境变量方式如下
echo $NAME //NAME为你要待查看的环境变量的名称
例如查看环境变量PATH。 测试PATH
在学习Linux下你是否有这样的疑惑就是ls与我们自己生成的c语言的可执行程序二者都是一个待执行的想要执行起来应该都是用相同的方法但是ls不像我们自己生成的c语言可执行程序一样不需要带 ./ 只需要ls就可以。而我们自己生成的可执行程序必须要在前面带上 ./ 才可以执行
容易理解的是./ 中的 . 表示当前路径。要执行一个可执行程序必须要先找到它在哪里既然不带./就可以执行ls命令说明系统能够通过ls名称找到ls的位置而系统是无法找到我们自己的可执行程序的所以我们必须带上./以此告诉系统该可执行程序位于当前目录下。
而系统就是通过环境变量PATH来找到ls命令的查看环境变量PATH我们可以看到如下内容
可以看到环境变量PATH当中有多条路径这些路径由冒号隔开当你使用ls命令时系统就会查看环境变量PATH然后默认从左到右依次在各个路径当中进行查找。 而ls命令实际就位于PATH当中的某一个路径下所以就算ls命令不带路径执行系统也是能够找到的。 这时候就会有人有疑问了那可不可以让我们自己的可执行程序也不用带路径就可以执行呢
答案是可以的
当然可以下面给出两种方式
方式一将可执行程序拷贝到环境变量PATH的某一路径下。
既然在未指定路径的情况下系统会根据环境变量PATH当中的路径进行查找那我们就可以将我们的可执行程序拷贝到PATH的某一路径下此后我们的可执行程序不带路径系统也可以找到了。
sudo cp proc /usr/bin 方式二将可执行程序所在的目录导入到环境变量PATH当中。
将可执行程序所在的目录导入到环境变量PATH当中这样一来没有指定路径时系统就会来到该目录下进行查找了。
export PATH$PATH:/home/haha/code/12_11 // pwd 显示的路径可以不带 ./ 直接运行
测试HOME
任何一个用户在运行系统登录时都有自己的主工作目录家目录环境变量HOME当中即保存的该用户的主工作目录。
普通用户展示 超级用户展示 测试SHELL
我们在Linux操作系统当中所敲的各种命令实际上需要由命令行解释器进行解释而在Linux当中有许多种命令行解释器例如bash、sh我们可以通过查看环境变量SHELL来知道自己当前所用的命令行解释器的种类。 而该命令行解释器实际上是系统当中的一条命令当这个命令运行起来变成进程后就可以为我们进行命令行解释。 和环境变量相关的命令
1、echo显示某个环境变量的值。 注意echo $ 是显示本地变量
2、export设置一个新的环境变量。 注意如果将本部分代码去掉export是添加本地变量添加
3、env显示所有的环境变量。 部分环境变量说明
环境变量名称表示内容PATH命令的搜索路径HOME用户的主工作目录SHELL当前ShellHOSTNAME主机名TERM终端类型HISTSIZE记录历史命令的条数SSH_TTY当前终端文件USER当前用户MAIL邮箱PWD当前所处路径LANG编码格式LOGNAME登录用户名
4、set显示本地定义的shell变量和环境变量。 5、unset清除环境变量。 环境变量的组织方式
在系统当中环境变量的组织方式如下 每个程序都会收到一张环境变量表环境表是一个字符指针数组每个指针指向一个以’\0’结尾的环境字符串最后一个字符指针为空。
通过代码获取环境变量
我们在一开始也说了一个文件想要生成可执行程序就需要通过链接找到静态库与动态库那么在c语言中我们使用main函数的时候是怎么样找到的呢这里就需要提一个疑问你知道main函数其实是有参数的吗
在 C 语言中main 函数不仅可以有参数而且它的确是通过链接器找到的。这些都涉及到程序的启动过程、链接过程以及 main 函数的定义和调用。
其中main函数有三个参数只是我们平时基本不用它们所以一般情况下都没有写出来。 我们可以在Windows下的编译器进行验证当我们调试代码的时候若是一直使用逐步调试那么最终会来到调用main函数的地方。 在这里我们可以看到调用main函数时给main函数传递了三个参数。
我们先来说说main函数的前两个参数。 首先先提一点第一个参数argc其实就是count第二个数向量表也就是value 在Linux操作系统下编写以下代码生成可执行程序并运行。 运行结果如下 现在我们来说说main函数的前两个参数main函数的第二个参数是一个字符指针数组数组当中的第一个字符指针存储的是可执行程序的位置其余字符指针存储的是所给的若干选项最后一个字符指针为空而main函数的第一个参数代表的就是字符指针数组当中的有效元素个数。 下面我们可以尝试使用刚才讲到的知识点编写一个简单的代码该代码运行起来后会根据你所给选项给出不同的提示语句。
#include stdio.h
#include string.h
int main(int argc, char* argv[])
{if (argc 1){if (strcmp(argv[1], -a) 0){printf(you used -a option...\n);}else if (strcmp(argv[1], -b) 0){printf(you used -b option...\n);}else{printf(you used unrecognizable option...\n);}}else{printf(you did not use any option...\n);}return 0;
}
运行效果 现在我们来说说main函数的第三个参数。
main函数的第三个参数接收的实际上就是环境变量表我们可以通过main函数的第三个参数来获取系统的环境变量。 例如编写以下代码生成可执行程序并运行打印所有的环境变量。 运行结果就是各个环境变量的值 注意我们这里使用到的第三方变量environ来获取每一个第三个参数来获取环境变量。
同样也可以不使用直接访问env也可以。
注意 libc中定义的全局变量environ指向环境变量表environ没有包含在任何头文件中所以在使用时要用extern进行声明。 扩展 在规定上一个程序要运行起来是至少需要两个表的 命令行参数表argv环境变量表env 但是我们在一开始将前两个参数的时候是没有传第三个参数env的但是在实际上并没有妨碍我们运行甚至来说我们在平时写c语言代码的时候一个都没有传它还可以运行。 这是因为 虽然 envp 是 main 函数的一个可选参数环境变量 对程序来说是至关重要的但操作系统负责提供这些环境变量并将其与当前进程关联。即使你在程序中不显式地使用 envp 参数你仍然可以通过库函数如 getenv访问这些环境变量。操作系统会自动为你管理并提供这些信息保证程序能够正常运行。这也在证明我们写的c语言代码其实最外层不是main函数还有别的函数在调用main函数。对于大多数常见的程序它们可以不显式地接收 envp操作系统会自动将环境变量加载到进程中且程序可以在需要时通过标准库函数如 getenv访问这些环境变量。当你在 main 函数中只定义了 argc 和 argv 时操作系统仍然会设置并提供环境变量。这些环境变量会自动关联到当前进程的地址空间。当你使用诸如 getenv(HOME) 等标准库函数时它们会在内存中查找环境变量表。 通过系统调用获取环境变量
除了通过 main 函数的第三个参数envp和第三方变量 environ 来获取环境变量外我们还可以通过系统调用 getenv 函数来获取环境变量。getenv 函数根据所给定的环境变量名在环境变量表中进行查找并返回一个指向相应值的字符串指针。
例如使用 getenv 函数获取环境变量 PATH 的值可以如下所示 运行结果 如何理解本地变量只会在本bash内部有效不会被子进程继承访问
比如说我直接在Linux下输入以下指令
my_varI am a local variable
他的意思就是在bash进程中添加本地变量my_var。
然后我运行指令
echo $my_var 他就会 输出: I am a local variable。
以上操作就是在bash内部添加了本地变量然后再打印本地变量my_var的值但是我们再创建一个进程。
我们已经知道所有的进程的父进程祖宗进程全都是bashbash进程的PID是1是所有进程的父进程祖宗进程。但是我们在创建的进程中是无法访问/继承父进程bash内部定义的/已有的本地变量只可以继承访问环境变量。
同样一个道理如果我们在刚创建的进程中fork一个子进程那么改子进程也不会继承其父进程的本地变量。
所以结论
本地变量 是仅在当前 shell 会话或进程内有效的变量。它们不会被子进程继承也不会出现在 env 或其他子进程的环境中。环境变量 是通过 export 设置的它们可以在父进程和所有子进程之间共享。
所以本地变量只会在当前 shell或进程内有效而 子进程 只能继承 环境变量无法继承父进程的 本地变量。
程序地址空间
32位的计算机系统理论上最大的内存为4G。
其推导过程为32位计算机理论上的最大可寻址内存量为2^32字节
1字节Byte 8位bit所以可以寻址的最大内存为 2^32 字节即
2^32字节4,294,967,296 字节4 GB。
所以你一定看过这个图就算没见过原图也一定见过类似的图 在Linux操作系统中我们可以通过以下代码对该布局图进行验证 #include stdio.h
#include stdlib.hint g_unval; // 未初始化的全局变量
int g_val 100; // 已初始化的全局变量int main(int argc, char* argv[], char* envp[]) {int i;// 打印代码段地址printf(code addr: %p\n, main); // main函数的地址// 打印只读常量区地址char* str hello world; // 字符串字面量存储在只读数据区printf(read only addr: %p\n, str); // 字符串常量的地址// 打印已初始化数据段的地址printf(init addr: %p\n, g_val); // 已初始化全局变量g_val的地址// 打印未初始化数据段的地址printf(uninit addr: %p\n, g_unval); // 未初始化全局变量g_unval的地址// 打印堆区地址int* p (int*)malloc(10 * sizeof(int)); // 动态分配10个int类型的内存if (p NULL) {printf(Memory allocation failed!\n);return 1; // 如果内存分配失败则退出}printf(heap addr: %p\n, p); // 堆区的地址// 打印栈区地址printf(stack addr: %p\n, str); // 栈上局部变量str的地址printf(stack addr: %p\n, p); // 栈上局部变量p的地址// 打印命令行参数的地址for (i 0; i argc; i) {printf(args addr: %p\n, argv[i]); // 打印每个命令行参数的地址}// 打印环境变量的地址i 0;while (envp[i]) {printf(env addr: %p\n, envp[i]); // 打印每个环境变量的地址i;}// 释放动态分配的内存free(p);return 0;
}
运行结果 可以观察到栈空间的开辟是从高地址向低地址的顺序开辟空间 堆是由低地址向高地址的顺序开辟空间 各个空间的地址大小顺序也符合上图的展示 下面我们来看一段奇怪的代码 #include stdio.h
#include stdlib.h
#include sys/types.h
#include unistd.hint g_val 100;int main() {pid_t id fork(); // 创建一个新的进程if (id 0) { // 子进程printf(child: PID: %d, PPID: %d, g_val: %d, g_val: %p\n, getpid(), getppid(), g_val, g_val);}else if (id 0) { // 父进程printf(father: PID: %d, PPID: %d, g_val: %d, g_val: %p\n, getpid(), getppid(), g_val, g_val);}else { // fork失败perror(fork failed);exit(1);}return 0;
}
在Linux下运行的效果如下
可以看到父进程与子进程的全局变量g_val的地址空间其实是一样的那么按照我们c语言学的知识那么父进程与子进程其实是公用一个地址的数据的也就代表着如果父进程修改了其值按道理子进程的值也会跟着修改。 我们对原有的代码上进行修改使得子进行先修改其g_val的值然后打印子进程执行完后在让父进程执行打印对应的数据。
按照上面的解释按道理父进程的 g_val 的值应该会由原来的100变为200. 那么我们看看实际运行效果吧 运行结果
但实际上只有子进程的进行了修改这就显得矛盾了。
毕竟我们观察到现在 g_val 的父子进程的地址还是相同啊这是为什么 那么我们只能总结出来
如果说我们是在同一个物理地址处获取的值那必定是相同的而现在在同一个地址处获取到的值却不同这只能说明我们打印出来的地址绝对不是物理地址
实际上我们在语言层面上打印出来的地址都不是物理地址而是虚拟地址。物理地址用户一概是看不到的是由操作系统统一进行管理的。
所以就算父子进程当中打印出来的全局变量的地址虚拟地址相同但是两个进程当中全局变量的值却是不同的。 注意 虚拟地址和物理地址之间的转化由操作系统完成。 此回答来自chatGTP 扩展 为什么要设置虚拟内存直接存物理内存不行么 设置虚拟内存的主要目的是为了简化计算机系统的内存管理增强系统的灵活性、安全性和可扩展性。虽然直接使用物理内存似乎是一个直观的选择但实际操作系统和硬件环境中使用虚拟内存有以下几个关键的理由 . 隔离与保护 (Memory Protection) 虚拟内存允许每个进程都有独立的虚拟地址空间避免不同进程之间直接访问彼此的内存。这样一个进程无法直接修改或影响其他进程的内存从而增强了系统的安全性。如果没有虚拟内存多个进程可能会使用相同的物理地址空间这样一个进程的错误如越界写入可能会破坏其他进程的数据甚至导致系统崩溃。 2. 简化编程模型 对程序员来说虚拟内存简化了内存管理程序员无需关心物理内存的实际布局。每个程序都假设自己拥有一个连续的内存空间虚拟地址空间而不需要了解背后复杂的物理内存细节。这样程序可以像运行在独立的机器上一样运行不用担心内存碎片、物理内存的分配和释放等复杂问题。 3. 内存共享 (Memory Sharing) 虚拟内存允许多个进程共享同一块物理内存。例如多个进程可以共享只读的库文件如 C 标准库或共享某些数据结构。每个进程在虚拟地址空间中有独立的地址映射但是它们可以映射到相同的物理内存区域。如果直接使用物理内存进程之间的共享将更加复杂且不易实现高效的共享机制。 4. 地址空间分离 使用虚拟内存操作系统可以给每个进程分配一个完整的虚拟地址空间即使多个进程的虚拟地址空间重叠它们也不会干扰彼此。比如进程A和进程B可能都有虚拟地址 0x4000但它们实际上指向的是不同的物理地址。这与直接使用物理内存不同直接物理内存的管理会导致地址空间的重叠问题。 5. 程序加载和动态链接 在没有虚拟内存的情况下程序加载可能受限于物理内存的大小和布局程序需要在启动时被完全加载到物理内存中。虚拟内存通过分页机制允许程序在运行时动态加载并按需加载和卸载代码和数据。这种按需分页机制意味着一个大程序不必一次性加载到内存中而是可以分段加载只加载当前需要执行的部分。 6. 交换空间和页面交换 (Swapping and Paging) 虚拟内存使得操作系统可以利用硬盘或其他存储设备作为扩展内存的“交换空间”。当物理内存不足时操作系统可以将不活跃的内存页面写入硬盘中的交换文件或交换分区然后将需要的页面从硬盘加载到物理内存中。这使得系统可以运行比实际物理内存更大的程序或处理更多的任务。没有虚拟内存操作系统就无法实现这一点因为程序的物理地址和物理内存是绑定的。 7. 内存分配和回收的灵活性 操作系统可以根据需求动态地管理物理内存。它可以将物理内存分配给正在运行的进程并根据需要回收不再使用的内存例如通过内存交换机制。虚拟内存的使用使得这种动态管理变得更加简单和高效。如果没有虚拟内存操作系统需要复杂地跟踪物理内存的每一部分并直接分配和回收这样会增加管理的难度。 8. 支持更大内存空间 虚拟内存允许程序访问比物理内存更大的地址空间。例如现代计算机系统中的 64 位操作系统可以支持数 TB 的虚拟内存地址空间理论上最大可寻址 16 EB即使物理内存只有几 GB 或几十 GB。这使得开发人员可以编写支持大量数据集和复杂计算的程序而不必担心物理内存的限制。如果没有虚拟内存程序只能访问有限的物理内存这对需要大量内存的应用程序如数据库、大型数据分析来说是一个严重的限制。 进程地址空间
我们之前将那张布局图称为程序地址空间实际上是不准确的那张布局图实际上应该叫做进程地址空间进程地址空间本质上是内存中的一种内核数据结构在Linux当中进程地址空间具体由结构体mm_struct实现。
进程地址空间就类似于一把尺子尺子的刻度由0x00000000到0xffffffff尺子按照刻度被划分为各个区域例如代码区、堆区、栈区等。而在结构体mm_struct当中便记录了各个边界刻度例如代码区的开始刻度与结束刻度如下图所示 划分完区域后比如 brk_start 的值为 1000 brk_end 的值为 5000 。那么 [10005000] 之间的区域就叫做虚拟地址或线性地址。对于区域的扩大或缩小操作只需要改变 start 与 end 的数值就可以了。
在结构体mm_struct当中各个边界刻度之间的每一个刻度都代表一个虚拟地址这些虚拟地址通过页表映射与物理内存建立联系。由于虚拟地址是由0x00000000到0xffffffff线性增长的所以虚拟地址又叫做线性地址。 每个进程被创建时其对应的进程控制块task_struct和进程地址空间mm_struct也会随之被创建。而操作系统可以通过进程的task_struct找到其mm_struct因为task_struct当中有一个结构体指针存储的是mm_struct的地址。 例如父进程有自己的task_struct和mm_struct该父进程创建的子进程也有属于其自己的task_struct和mm_struct父子进程的进程地址空间当中的各个虚拟地址分别通过页表映射到物理内存的某个位置如下图 其实这也说明了如果有多个进程(但实际上真实地址空间只有一份)
而当子进程刚刚被创建时子进程和父进程的数据和代码是共享的即父子进程的代码和数据通过页表映射到物理内存的同一块空间。只有当父进程或子进程需要修改数据时才将父进程的数据在内存当中拷贝一份然后再进行修改。
例如子进程需要将全局变量g_val改为200那么此时就在内存的某处存储g_val的新值并且改变子进程当中g_val的虚拟地址通过页表映射后得到的物理地址即可。 此时可以理解为什么会发生同一块空间能读取到不同值的现象了
父子进程有着各自的 mm_struct其成员起始值一致对于同一个变量如果未改写则两者的虚拟地址通过 页表 MMU 转换后指向同一块空间发生改写行为此时会在真实空间中再开辟一块空间拷贝变量值让其中一个进程的虚拟地址空间映射改变这种行为称为 写时拷贝
当我们创建子进程后OS以父进程的PCB为模板创建了子进程的PCB结构。所以子进程也拥有自己的 mm_struct且在同样的位置存在与父进程相同的虚拟地址 当我们对子进程或父进程的数据不做修改的时候父子进程读取的变量数据与变量地址都是相同的。而当我们修改父进程或子进程的数据时先修改哪一个哪一个就会发生写时拷贝即在物理内存中另外再开辟一块空间并修改页表的映射关系使之映射到新空间 总结同一个变量地址相同其实是虚拟地址相同内容不同其实是被映射到了不同的物理地址 1、为什么数据要进行写时拷贝 进程具有独立性。多进程运行需要独享各种资源多进程运行期间互不干扰不能让子进程的修改影响到父进程。 2、为什么不在创建子进程的时候就进行数据的拷贝 子进程不一定会使用父进程的所有数据并且在子进程不对数据进行写入的情况下没有必要对数据进行拷贝我们应该按需分配在需要修改数据的时候再分配延时分配这样可以高效的使用内存空间。 3、代码会不会进行写时拷贝 90%的情况下是不会的但这并不代表代码不能进行写时拷贝例如在进行进程替换的时候则需要进行代码的写时拷贝。 所以到这里我们对进程又有了更深了理解 对于一个进程想要运行起来至少要有两表命令行参数表环境变量表对于一个进程的创建实际上伴随着其进程控制块task_struct、进程地址空间mm_struct以及页表的创建。