Linux中的fork与进程地址空间

目录

前言

一、进程地址空间

二、fork的值返回

三、高清图链接

总结


前言

        在博主的《进程状态解析》一文中,在讨论进程创建时,提到了一个系统调用接口fork,它在使用过程中表现出对于父子进程不一致的返回结果,而且似乎还具有多次返回的特点,那么本文就来详细解释一下其中的缘由。博文中涉及到的图片如若不清可点击链接查找高清图《博主码云》


一、进程地址空间

        在正式开始讲述之前我们先看一个有意思的现象(博主使用的centos7系统+shell,关于这个问题博主想说的是为了照应系统给我们实现的接口,不同的操作系统可能会有不同的方式,来实现这一目的,如果你对操作系统与硬件接口与我们使用的接口模糊不清的话可以看看博主写的这篇文章《浅谈冯诺依曼体系与Linux操作系统》),在windows平台上和Linux平台上同时执行以下码:

#include <stdio.h>
#include <stdlib.h>

 int global_init_val = 1;
 int global_uninit_val;
 int main()
 {

			 char* p1 = (char*)malloc(10);
			 char* p2 = (char*)malloc(10);
			 char* p3 = (char*)malloc(10);
			 static int static_uninit_val;
			 static int static_init_val = 2;
			 printf("i am stack!!     my address:%p %p %p\n", &p1, &p2, &p3);
			 printf("i am heap!!      my address:%p %p %p\n", p1, p2, p3);
			 printf("i am global!!    my address:%p %p   \n", &global_init_val, &global_uninit_val);
			 printf("i am function!!  my address:%p      \n", main);
			 printf("i am static!!    my address:%p %p   \n", &static_init_val, &static_uninit_val);
			 return 0;
}
图1.1        运行结果

        接下来的叙述将围绕这两张图展开。首先,我们可以看到在Linux平台下,两次运行结果是相同的,但是反观windows平台,两次运行结果得到的变量地址是不同的。这就是平台实现之间的差异,虽然他们都成功完成了对应的功能,但还是可以反映出处理的逻辑是不同的,但是就我们理解,Windows更符合我们的预期,Linux的处理反而让我们疑惑,我们都知道程序运行需要加载的内存,而内存又是随机存储的,所以两次打印数据分布地址是相同的,我们不妨大胆假设,程序显示的地址并非真正的物理内存,而是一种虚拟地址空间!!!!

那么到底是怎么实现的呢?这就要提到Linux处理的一种方式——进程地址空间。

        在进程开始执行的时候,Linux操作系统会同时为进程申请一块名为进程地址空间(就是一块空间)的空间,这个空间储存的是进程数据的虚拟地址(虚拟地址就是可以通过一定映射关系映射到实际唯一物理内存地址的地址,关于其意义稍后解释),操作系统会将虚拟地址表与物理内存地址表建立映射关系,以达到定址访问的效果。

也许你现在听的晕头转向——啥是映射关系?映射关系是如何建立的??

        我们先来解释映射关系,实际上我们常常使用的数组就是一种经典的映射关系。你应该知道,我们可以通过下标的形式来访问数组中的元素,这就是一种朴素的映射关系,就像我们知道如果我们使用下标0就可以访问数组中的第一个元素,使用下标1就可以访问数组中的第二元素一样。这就是一种映射关系(注意:映射关系应该是一 一对应的逻辑,不应该存在多对一、一对多的情况)。

图1.2        数组的映射关系

        所以我们的操作系统是如何将虚拟地址映射为物理内存地址的呢?

参照数组映射关系,操作系统也对虚拟地址和物理内存进行了映射:

图1.3        映射关系与管理解耦

在计算机解读映射关系的时候会将页表读取到CPU中的MMU(内存管理单元)中,而后CPU读取正文代码进程程序的执行。 

这里的管理虚拟地址与物理内存的映射表被称作“页表”,关于页表是什么不是本文的重点,这个内容将在其他文章中进行讲解。那么说了这么多,我们需要考虑一下为什么Linux要这样进行设计。

1.首先因为操作系统使用的虚拟的地址+页表的映射方式,这意味着有心之人是无法通过虚拟的地址对核心数据进行破坏。

2.其次因为进程地址空间是一块连续的空间,这意味着对于数据管理要比非连续空间的轻松的多。

3. 最重要的是,操作系统只需要管理进程地址空间就可以对进程管理,可以无需考虑内存管理的影响——将进程管理与内存管理进行解耦。

二、fork的值返回

        上文解释了这样一个非常重要的点,就是在进程使用的并非是真实的物理内存地址,而是使用的是一种虚拟地址。那么现在我们看一个这样的示例:

图2.1        测试代码与结果

         从上图中我们不难发现,在子进程与父进程中的id变量是同一个。我们知道这样一个事实,进程与进程之间是相互独立运行的,对于父子进程更是如此,虽然父子进程共享fork之后的代码,与数据(需要注意的是页表数据也会被共享)。(页表是随着进程的创建而创建,这里指的共享指的是数据共享,而非页表共享)

但是我们也要考虑:

        当一个进程对变量进行改变时,为了不影响其父子进程,被修改的变量就必须与其父子进程不同的一份变量——这样的行为叫做“进程的写时拷贝”,当父子进程中共享数据改变时,改变数据的进程需要额外申请一块空间来存储改变的数据。具体的关系可参考图2.2

图2.2        写时拷贝数据关系图

        顺带一提为什么即使要更改还是要将原数据拷贝进新开辟的空间中,我们要知道数据不一定是内置类型,他可能是复杂的自定义类型数据,对于这样的数据,有可能我们只是修改自定义类型数据中的一部分,所以还是要将其他没有更改的数据进行拷贝的。

         好了,现在让我们把目光放回到fork本身并在靠近一点观察它,我们知道fork也是一个有返回值的函数,那么在该函数进行返回值之前就一定已经将子进程开辟好并进行管理了,那我们之前说的父子进程共享fork之后的数据实际上可以进行一下更正——父子进程共享子进程完成创建并组织后的数据,所以父子进程也共享返回语句,因为父子进程各自进行一次返回,所以fork才会看起来返回了两次,有两个返回值。那返回值语句都被共享了,那么id变量呢?诚然其也被共享!但父子进程的fork返回对id变量进行了更改,进而引发了我们刚刚说的写时拷贝,由于我们所能看见的是虚拟地址,所以才会出现地址相同内容不同的错觉。

那么我们将父子进程套入到我们说的写时拷贝是什么样子的呢?

图2.3        fork返回值的写时拷贝问题

        到这我们解释清楚了为什么fork会呈现不同的返回值,一个变量为什么会有多个值的原因。

三、高清图链接

本文涉及到了一些不太清晰的图片想要获取这些图片点击博主码云即可获取【点我】


总结

本文解释了有关fork的一些特殊现象并解释了一些有关进程空间的概念。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入学习指针2

前言 hello,我又来了&#xff0c;今天有我继续带领大家深入的学习指针&#xff0c;通过上次的学习&#xff0c;我们已经了解到了指针的基本概念&#xff0c;指针如何使用&#xff0c;指针使用的益处&#xff0c;以及一些相关的概念&#xff0c;那今天我们就继续深入的学习&am…

Docker笔记(七)使用Docker部署Spring Boot项目

本文介绍如何使用Docker打包并部署Spring Boot多模块项目。 其中本文涉及的Docker的私库是用Nexus3搭建的。 使用Docker部署Spring Boot项目有三种方式 &#xff08;1&#xff09;使用 spring-boot-maven-plugin内置的build-image. &#xff08;2&#xff09;使用 Google 的 j…

STM32CubeMX软件使用(超详细)

1、Cube启动页介绍 2、芯片选择页面介绍 3、输入自己的芯片型号&#xff0c;这里以STM32U575RIT6举例 4、芯片配置页码介绍 5、芯片外设配置栏详细说明 6、点击ClockConfiguration进行时钟树的配置&#xff0c;选择时钟树后可以选择自己想使用的时钟源&#xff0c;也可以直接输…

MySQL数据库——基础事务操作-BEGIN-COMMIT-ROLLBACK

DDL CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT COMMENT 学号,createDate datetime DEFAULT NULL,userName varchar(20) DEFAULT NULL,pwd varchar(36) DEFAULT NULL,phone varchar(11) DEFAULT NULL,age tinyint(3) unsigned DEFAULT NULL,sex char(2) DEFAU…

MySQL企业级开发重点之事物和索引

事物 -- 解散学工部 delete from tb_dept where id 1;-- 删除部门下的员工 delete from tb_emp where dept_id 1; 介绍和操作 我们应该将两个语句写成一个语句 -- 开启事物 start transaction ;-- 解散学工部 delete from tb_dept where id 3;-- 删除部门下的员工 delete fr…

Word页脚设置“第X页共X页”的方法【域实现】

Word页脚设置“第X页共X页”的方法【域实现】 在设置Word页码格式的要求中&#xff0c;有时需要设置为“第X页共X页”这种格式&#xff0c;使用Word中的域功能可实现&#xff0c;同时&#xff0c;在某些情况下&#xff0c;可能还需要减去封面的页码&#xff0c;接下来为具体步…

传感器—超声波雷达

声波技术 在讲述超声波雷达之前&#xff0c;先了解一下声波的概念以及超声波和声波之间的关系 什么是声波&#xff1f; 声波是物体机械振动状态&#xff08;或能量&#xff09;的传播形式。所谓振动是指物质的质点在其平衡位置附近进行的往返运动形式&#xff0c;这种振动状…

JAVA文件的简单操作

文件IO&#xff08;Input和Output&#xff09; 文件的输入和输出是人为规定的&#xff0c;那么什么是输入&#xff1f;什么是输出捏&#xff1f;在这里统一已CPU为基准 例如&#xff1a;将文件由内存写入硬盘就是输出&#xff0c;有硬盘写入内存就是输入。可以总结为&#xff…

C语言—深入理解指针(3)

1.字符指针变量 一般使用&#xff1a; 另一种使用方法&#xff1a; “hello world”是一个常量字符串&#xff0c;不能被修改。 上述代码是将字符串中的首字符‘h’赋值给指针pstr&#xff0c;用%s打印字符串的时候&#xff0c;只需要提供首字符的地址就行。&#xff08;如果…

LoadRunner性能测试基本步骤

前言 本文旨在指导初学者使用LoadRunner进行基础的性能测试。 我们在接到一个性能测试任务的时候&#xff0c;需要从以下几点考虑&#xff1a;我们的测试对象是什么&#xff0c;测试要求是什么&#xff0c;测试环境怎么部署的&#xff0c;业务规模如何&#xff0c;哪些业务点是…

这是一关于DSC相关的文档

这是一关于DSC相关的文档 上面这幅图清晰的展示了somewhat flat的像素图示

CRMEB 开源/标准版商城系统客服配置教程

管理后台/设置/系统设置/商城配置/客服端配置 有系统客服/拨打电话/跳转链接可选&#xff0c;系统客服为系统自带的客服系统&#xff0c;拨打电话为用户点击联系客服为拨打客服电话的方式&#xff0c;跳转链接为可以跳转自己开发的客服系统或者第三方的客服系统或者企业微信的…

etcd单机部署和集群部署

1、etcd单实例部署 对于平常的学习&#xff0c;其实搭建一个单机节点是够了的。接下来就讲讲怎么搭建单机节点。 本次部署是在 centos7 系统&#xff0c;cpu 为amd64 上面进行的。 部署是直接使用官方编译好的二进制文件&#xff0c;大家也可以直接看 ectd-releases 界面选择…

开源交互审计系统:功能强大、安全好用【送源码】

在当今信息化时代&#xff0c;网络安全越来越受到重视。传统的远程控制工具&#xff0c;如RDP、SSH、VNC等&#xff0c;虽然方便易用&#xff0c;但存在安全隐患&#xff0c;容易被黑客利用。很多时候我们都需要做一些防护的处理来来保障网络安全。 今天了不起来分享一款开源好…

OSPF链路状态数据库

原理概述 OSPF是一种基于链路状态的动态路由协议&#xff0c;每台OSPF路由器都会生成相关的LSA&#xff0c;并将这些LSA通告出去。路由器收到LSA后&#xff0c;会将它们存放在链路状态数据库LSDB中。 LSA有多种不同的类型&#xff0c;不同类型的LSA的功能和作用是不同的&…

LearnOpenGL(十一)之光源

一、投光物 将光投射(Cast)到物体的光源叫做投光物(Light Caster)。 二、平行光 当一个光源处于很远的地方时&#xff0c;来自光源的每条光线就会近似于互相平行&#xff0c;我们可以称这些光为平行光。当我们使用一个假设光源处于无限远处的模型时&#xff0c;它就被称为定向…

django显示网页步骤

显示网页步骤 小白的django学习笔记 2024/5/6 8:30 文章目录 显示网页步骤创建输入框&#xff08;文本、单选、多选&#xff09;效果如何在django中显示网页写函数配置地址运行&#xff0c;要选择这个工程名的&#xff0c;使用socket复制ip&#xff0c;后面在加上名字,成功&…

Final Draft 12 for Mac:高效专业剧本创作软件

对于剧本创作者来说&#xff0c;一款高效、专业的写作工具是不可或缺的。Final Draft 12 for Mac就是这样一款完美的选择。这款专为Mac用户设计的剧本创作软件&#xff0c;凭借其卓越的性能和丰富的功能&#xff0c;让您的剧本创作更加得心应手。 Final Draft 12支持多种剧本格…

react+antd --- 日期选择器,动态生成日期表格表头

先看一下效果---有当前月的日期 技术: 1: react 2:antd-UI库 -- table 3:moment--时间处理库 代码效果: import { Button, DatePicker, Table } from antd; import { useEffect, useState } from react; import moment from moment;function Club() {const [selecte…

Vue3自定义封装音频播放组件(带拖拽进度条)

Vue3自定义封装音频播放组件&#xff08;带拖拽进度条&#xff09; 描述 该款自定义组件可作为音频、视频播放的进度条&#xff0c;用于控制音频、视频的播放进度、暂停开始、拖拽进度条拓展性极高。 实现效果 具体效果可以根据自定义内容进行位置调整 项目需求 有播放暂停…