低价高端网站设计/企业策划方案怎么做
1.程序地址空间分配回顾
在前⾯C语⾔以及C部分介绍过⼆者的内存分配如下图所示:
全局变量区和未初始化全局变量区也被称为数据区,数据区中除了有全局变 量,还有静态变量和常量
使⽤下⾯的代码演示不同的内容所处的地址:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>// 初始化的全局变量
int global = 0;
// 未初始化的全局变量
int value;int main() {// 环境变量char* envi = getenv("PWD");// 堆区int* ptr = (int*)malloc(sizeof(int)*10);// 栈区int a = 0;int b = 0;int c = 0;printf("%p\n", &global);printf("%p\n", &value);printf("%p\n", ptr);// 代码区printf("%p\n", main); // 函数名即函数地址printf("%p\n", envi);printf("%p\n", &a);printf("%p\n", &b);printf("%p\n", &c);return 0;
}输出结果:
0x601048
0x60104c
0x7e1010
0x4005cd
0x7ffc99757f43
0x7ffc9975694c
0x7ffc99756948
0x7ffc99756944
实际上,所谓的程序地址空间就是进程地址空间,进程地址空间是如何产⽣的就是下 ⾯需要探讨的问题
2.进程地址空间
2.1.虚拟地址
前⾯提到,⽗进程和⼦进程会共享代码和数据,尤其是两个进程不进⾏数据修改时, 数据不会产⽣两份,那么这样理解就可以直观地认为当⼦进程修改了数据,对应的变 量内存地址就会发⽣改变,但是改变的是不是程序读取到的地址,看下⾯例⼦的演示 结果:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>// 初始化的全局变量
int global = 0;
// 未初始化的全局变量
// int value;int main() {printf("父进程, pid = %d, &global = %p\n", getpid(), &global);pid_t p = fork();if(p == 0) {// 子进程while(1) {printf("子进程, pid = %d, &global = %p\n", getpid(), &global);// 子进程修改global++;sleep(1);}} else {while(1) {}}return 0;
}输出结果:
父进程, pid = 12969, &global = 0x601050
子进程, pid = 12970, &global = 0x601050
可以看到,尽管⼦进程修改了与⽗进程共享的代码中的变量,⼦进程读取到的变量的 地址与⽗进程读取到的变量的地址是完全相同的。
实际上,在C语⾔程序中使⽤ & 获取到的地址是⼀个虚拟地址(也称线性地址),对 应虚拟地址的就是物理地址。
在上⾯提到的「⼦进程改变代码中的数据,对应的内存地址会发⽣改变」,本质是因 为此处的内存地址指的是物理地址,⽽不是虚拟地址
2.2.地址空间
地址空间,可以理解为是操作系统为每⼀个进程开辟的⼀块运⾏空间,示意图如下:
为了保证所有的进程都有⼀个完整的地址空间,操作系统为每个进程提供⼀个独⽴的 虚拟地址空间。例如,如果操作系统⽀持3GB的虚拟地址空间,每个进程都会认为⾃ ⼰拥有独⽴的3GB空间,不会受到其他进程占⽤内存的影响。这⼀过程暂且可以理解 为操作系统为每⼀个进程“画的⼀张⼤饼”,⽽这个“饼”即为进程地址空间
2.3.区域划分
进程地址空间中存在着多个区域,每⼀个区域有着⾃⼰的作⽤。每⼀块区域的划分实 际上是根据区域的起始值和终⽌值进⾏决定,示意图如下:
为了⽅便管理,在Linux中,操作系统在底层的 task_struct 内部存在着⼀个结构 体指针,该结构体指针的类型是 mm_struct 结构体,该结构体中存在着⼀些变量⽤ 于存储指定区域的起始值和终⽌值。这些值本质也是地址值,但是这个地址并不是实 际的物理内存的地址,⽽是通过映射后的虚拟地址,源码如下:
struct mm_struct {// ...unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;unsigned long rss, total_vm, locked_vm;// ...
};
因为变量的地址都是在这些指定的进程地址空间区域中,所以也可以解释为什么程序 获取到的是虚拟地址⽽⾮物理地址
解释:
3.分页与数据独立
在上⾯探讨了虚拟地址和物理地址,操作系统为了管理这两个地址之间的映射,从⽽ 保证虚拟地址能够正确访问到实际的物理地址对应的内容,在创建⼀个进程PCB时会 同时创建⼀个⻚表。
在执⾏前⾯的代码的过程中,⼦进程未创建之前,⽗进程拥有⾃⼰的⻚表和进程地址 空间,此时全局变量 global 会由操作系统在物理地址上开辟⼀块空间,并将映射后 的虚拟地址填充到⻚表中被进程获取,如下图所示:
当创建⼦进程之后,⼦进程会与⽗进程共享代码和数据,此时意味着⼦进程拷⻉⽗进 程中的 task_struct 和⻚表,对 task_struct 中的内容进⾏针对性地修改,在⼦进 程没有改变复制过来的数据之前,由于⻚表内容相同,所以⼦进程⻚表中的映射与⽗ 进程完全相同,示意图如下:
当⼦进程对 global 变量进⾏修改时,操作系统就会在物理内存中开辟⼀块新的空 间,将共享的 global 数据拷⻉到该空间,这个拷⻉的过程也被称为写时拷⻉。物理 内存虽然开辟了⼀块新的空间,实际上对于⼦进程的⻚表来说,还是通过同样的虚拟 地址映射物理地址:
上⾯的过程也就可以解释为什么最开始的代码中,⼦进程和⽗进程获取到的变量值不 同,但是地址却相同,也就更加验证了⼀个变量中不可能存在两个不同的值
4.页表基本介绍
在Linux中,⻚表不仅仅有虚拟地址和物理地址的映射,还有物理地址的属性 RWX 以 及是否有数据的标识 isExists ,所以更加细致的⻚表应该如下所示:
RWX 属性:代表虚拟地址对应的物理地址是否具有读(R)、写(W)和执⾏权限 (X)。
前⾯提到,每⼀个进程地址空间区域都由指定的起始值和终⽌值进⾏划分,⽽这些区 域有的是可以写,有的不可以写只能读,但是对于物理内存来说,绝⼤部分的空间都 是可以写的,所以对于限制指定的物理地址是否可以写⼊就是通过 RWX 属性进⾏控制
例如,前⾯学习到的栈区和堆区,在程序代码运⾏时,可以在栈区和堆区申请空间并 进⾏写⼊,但是对于字符串常量等具有常性的值就不可以进⾏随意修改和写⼊。⽽之 所以在语⾔层⾯⽆法检测到这种问题,原因就是⻚表并不是在编译期间就创建的,⽽ 是在程序运⾏开始由操作系统创建,但是为了语⾔层⾯更加容易看出这种错误,就可 以把不想被修改的内容或者本身不可以修改的内容修饰为 const ,从⽽让编译器可 以在编译器检查出错误
此刻也便可以解释为什么程序会有野指针的概念,在语⾔层⾯,野指针表示指针中的 地址对应的空间已经被释放并归还给操作系统进⾏管理,此时不可以通过这个指针访 问对应的地址。在此时的系统层⾯,之所以可以限制野指针写⼊就是通过 RWX 属性, 因为虚拟地址对应的物理地址具有的属性已经是R
isExists 属性:代表虚拟地址对应的物理地址是否存在有效数据。在操作系统加载 进程时,会创建对应的进程地址空间和⻚表,在⻚表中将虚拟地址和物理地址进⾏映 射。然⽽,这个过程中某些进程的代码可能特别多,导致正⽂代码区占⽤的空间变得 很⼤,⽽实际使⽤时,可能有很⼤⼀部分的代码⻓时间不会被执⾏,造成资源浪费。 为了解决这个问题,操作系统通常采⽤按需加载(惰性加载,Lazy Loading)策 略。操作系统会先加载⼀部分内容,在运⾏过程中,通过检查⻚表中的有效位 (Valid Bit)来判断虚拟地址访问的物理地址是否已经加载。如果没有加载,则 触发缺⻚中断(Page Fault),操作系统会从磁盘加载相应的数据到内存中,再继 续执⾏。这⼀过程也适⽤于交换分区(Swap Partition),决定何时将数据换⼊ 或换出内存
结合前⾯的两个属性,操作系统就实现先告诉进程⾃⼰已经开辟好了空间,这个空间 的地址由多个虚拟地址组成,但是实际上可能物理地址并没有全部与指定的虚拟地址 对应,当程序运⾏到指定的部分再进⾏开辟映射,这个操作就可以实现将内存空间利 ⽤率最⼤化
5.缺页中断与写时拷贝
前⾯提到,当⼦进程修改了⽗进程的共享变量就会发⽣写时拷⻉,实际上,在这之前 需要进⾏⼀系列的操作检测
当⽗进程开始运⾏,⼦进程还未被创建之前,⽗进程的代码区为只读,但是数据区是 默认为读写的,当⼦进程创建后,代码区依旧是只读,但是⼆者的数据区均变为只 读,⼦进程从创建的位置开始执⾏代码,因为⼦进程会拷⻉ task_struct 中有关程 序计数器部分的内容,此时⽗⼦进程都只是读取对应代码区和数据区的数据,实现数 据和代码共享
注意::!
此处之所以⼦进程不额外直接拷⻉⽗进程的数据,尽管将来可能要对数据进⾏ 修改,是因为可能⽗进程的数据很⼤,但是⼦进程需要修改的数据很⼩,如果在创建 ⼦进程时就将数据拷⻉,此时就会导致空间的浪费
当⼦进程准备修改数据时,因为数据区已经变为只读,⼦进程想要修改就会修改失 败,此时就会触发缺⻚中断错误。但是因为缺⻚中断错误发⽣的原因不只有⼀种,还 有可能是野指针写⼊等情况,所以此时系统需要检测是否是误操作。当系统检测到数 据区本身是读写的,但是现在被设置为只读时,就会认为需要发⽣写时拷⻉
判定完需要进⾏写时拷⻉时,系统就会在物理内存中申请空间,将原始数据拷⻉到新 空间,并对⽗进程和⼦进程的数据区修改为只读,⽗进程和⼦进程代码继续执⾏
这个过程中涉及到原始数据拷⻉⽽不是简单开辟空间使⽤等待⼦进程使⽤新数 据进⾏覆盖是因为可能⼦进程对数据的修改是基于原数据的,例如变量⾃增⾏为等
上述过程简略示意图如下:
⼦进程修改数据::
6.进程地址空间结构初始化时机
任何⼀个结构体在创建时⼀定要进⾏初始化,⽽进程地址空间也是由⼀个结构进⾏描 述,这个结构的初始化由操作系统在可执⾏程序加载到内存时完成,可执⾏程序加载 到内存变成进程时,操作系统可以获取到部分区域的起始值和终⽌值,例如正⽂代码 区、数据区(包括全局变量区、常量区、静态变量区)和命令⾏与环境变量区。所以 这就可以解释为什么静态变量、全局变量和常量⼀直持续到进程结束
但是这其中有两个不同的区域:栈区和堆区,栈区在函数创建时会开辟对应的栈帧, 堆区在申请时会在已有的堆区上额外开辟需要的空间,所以这两个区域在程序刚加载 到内存时是不存在的
7.总结
之所以需要存在⻚表和进程地址空间有以下三个原因:
1. 保护内存:如果让进程直接访问物理内存,会导致在物理层⾯的野指针情况, 并且这个情况在物理层⾯并不容易被发现和阻⽌
2. 存在⻚表可以达到进程管理和内存管理耦合度降低:因为⻚表主要作⽤的是虚 拟地址和物理地址之间的映射,操作系统在创建进程时初始化对应的虚拟地址 即可,但是虚拟地址是否有对应的物理地址可以不⽤关⼼,只要没有被使⽤。 ⽽对于内存管理,操作系统只需要考虑在需要的时候将物理地址加载到⻚表, 此时对应的虚拟地址有映射就可以正常执⾏,在不需要的时候,将物理地址设 置为只读或者给其他进程使⽤
3. 让进程以统⼀的视⻆看待物理内存(⽆序变有序):因为操作系统会为每⼀个 进程开辟⼀个独⽴的⻚表和进程地址空间,让每⼀个进程都认为⾃⼰拥有操作 系统分配的全部内存空间,并且在进程访问地址空间时,实际上这个地址空间 的地址是⼀个虚拟地址,这个虚拟地址可以由操作系统⾃主决定为连续地址, 此时就可以不⽤考虑物理地址是否需要连续,因为进程只能获取到虚拟地址, 只要虚拟地址有物理地址映射并且拥有指定的权限,就不会出现问题,从⽽实 现让「让⽆序的物理内存地址变为有序的地址」
物理地址在开辟时,⼀般也会尽量是连续开辟,保证CPU在缓冲中的数据命中率
相关文章:

【Linux】Linux进程地址空间
1.程序地址空间分配回顾 在前⾯C语⾔以及C部分介绍过⼆者的内存分配如下图所示: 全局变量区和未初始化全局变量区也被称为数据区,数据区中除了有全局变 量,还有静态变量和常量 使⽤下⾯的代码演示不同的内容所处的地址: #includ…...

创建包含可导入浏览器信任的SSL自签名证书
问题:现在的三大浏览器,chrome、edge、firefox 一般都默认启用https检查,这就要求我们自建的局域网内的网址和其他诸如nextcloud、photoprism、tiddlywiki等应用也必须要有证书。解决方法是使用openssl自己生成一个。由此则会再衍生出一个问题…...

[Windows] 很火的开源桌面美化工具 Seelen UI v2.0.2
最近,一款来自Github的开源桌面美化工具突然在网上火了起来,引发了大家的关注,不少小伙伴纷纷开始折腾了起来。而折腾的目的,无非是为了一点点乐趣而已,至于结果如何,并不是最要紧的,反倒是体验…...

华帝携手抖音头部达人,金牌导演李力持量身打造厨电定制微短剧
10月21日,由华帝独家冠名,金牌导演李力持执导,抖音头部达人逆袭丁姐领衔主演,ATV亚洲电视、知行易达、抖音商城联合出品的24集现代家庭治愈美食定制微短剧《女厨神》上线抖音。 该剧讲述了热爱生活和美食的丁姐,为了家…...

监控易监测对象及指标之:JBoss 7.1.x中间件监控
监控易是一款功能全面的监控软件,能够实时追踪和分析各类IT资源的性能数据,提供及时的故障预警和性能报告。本文将对监控易针对JBoss 7.1.x中间件的监控指标进行深入解读,以帮助用户更好地理解和应用这些数据。 JBoss 7.1.x作为一款广泛使用的…...

Java 模拟退火算法
模拟退火算法(Simulated Annealing, SA)是一种用于全局优化的启发式搜索算法,它模仿了物理学中金属退火的过程。该算法在搜索空间中逐步降低“温度”,以寻找全局最优解。下面是一个用Java实现模拟退火算法的简单示例。 假设我们要…...

LeetCode[中等] 80. 删除有序数组中的重复项 II
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 public cl…...

机器学习5
1.1 决策树的定义 决策树是用于分类和回归的机器学习算法。它通过一系列的“是或否”的决策来分类数据。每个决策是基于数据的某个属性进行的,如“色泽是青绿吗?”。决策树的核心是通过树状结构,将一个复杂的问题逐步拆解为多个简单的二元问…...

【Python技术】利用akshare定时获取股票实时价,低于5日线钉钉通知报警
今天看了下大盘,临时有个想法,我想知道某个股票回踩5日线的价格,如果实时价格低于5日线通过钉钉报警通知我。 说干就干,临时撸了下简单的代码,仅做演示。 1、计算5日线思路 很多券商软件的MA5价格是近5个交易日收盘…...

LINUX1.2
1.一切都是一个文件 (硬盘) 2.系统小型 轻量型,300个包 3.避免令人困惑的用户界面 ------------------> 就是没有复杂的图形界面 4.不在乎后缀名,有没有都无所谓,不是通过后缀名来定义文件的类型(win…...

Proximal Distance Algorithm (近段距离算法)
文章目录 第一篇\section*{近端距离算法(Proximal Distance Algorithm)详解}\subsection*{1. MM原理(Majorization-Minimization Principle)}\subsection*{2. 近端距离算法(Proximal Distance Algorithm)}\…...

如何判断一个数是几位数与这个数是否为回文数并打印出其逆序数
1 问题 判断一个数是几位数与这个数是否为回文数并打印出其逆序数。 2 方法 先输入一个少于五位数的数用int的方法打出这个数的个十百千万的数字再用条件语句else-if来判断这个数是几位数,并打印其逆序数最后判断这个数是否为回文数,打印其数 通过实验、…...

Solon 之 STOMP
一、STOMP 简介 如果直接使用 WebSocket 会非常累,就像用 Socket 编写 Web 应用。没有高层级的交互协议,就需要我们定义应用间所发消息的语义,还需要确保连接的两端都能遵循这些语义。 如 HTTP 在 TCP 套接字之上添加了请求-响应模型层一样…...

在掌控板上搭建http服务器
在掌控板上搭建http服务器 打开Arduino IDE,并且已经添加了ESP32的支持库。以下是创建一个基本HTTP服务器的步骤: 包含必要的库: #include <WiFi.h> #include <WebServer.h>配置WiFi: 替换ssid和password为你的WiFi网…...

HCIA复习实验
实验要求 实验拓扑以及实验分析 第一步先划分网段 先对内网划分 192.168.1.0/24划分 192.168.1.0/26---骨干主线路 192.168.1.64/26---骨干备线路 ---192.168.1.128/25--vlan2 3汇总---便于减少路由表条目---在大型网络方便 192.168.1.128/26---vlan2 192.168.1.192/26---vla…...

生信软件39 - GATK最佳实践流程重构,提高17倍分析速度的LUSH流程
1. LUSH流程简介 基因组测序通常用于分子诊断、分期和预后,而大量测序数据在分析时间方面提出了挑战。 对于从FASTQ到VCF的整个流程,LUSH流程在非GVCF和GVCF模式下都大大降低了运行时间,30 X WGS数据耗时不到2 h,从BAM到VCF约需…...

c#编写的各类应用程序、类库的引用(黑白盒)
001 课程简介,C# 语言简介,开发环境准备 (yuque.com)https://www.yuque.com/yuejiangliu/dotnet/timothy-csharp-001 一个Solution里包含多个Project 一、见识 C# 编写的各类应用程序 二、类库的引用(黑/白盒引用) 1、黑盒引用&a…...

计算机网络考研笔记
...

用感性的方式浅要了解什么是AI 与 大模型
什么是人工智能(AI)? 人工智能(Artificial Intelligence,简称 AI)是指由人制造出来的具有一定智能的系统,能够理解和学习人类的行为,并在某些任务上模仿人类的智能行为。这些任务包…...

Linux文件的查找和打包以及压缩
文件的查找 文件查找的用处,在我们需要文件但却又不知道文件在哪里的时候 文件查找存在着三种类型的查找 1、which或whereis:查找命令的程序文件位置 2、locate:也是一种文件查找,但是基于数据库的查找 3、find:针…...

专题十四_哈希表_算法专题详细解答
目录 哈希表简介 1. 两数之和(easy) 解析: 解法一:暴力: 解法二:哈希O(N) 总结: 2. 判断是否互为字符重排(easy) 解析: 哈希: 总结&…...

C++源码生成·序章
文章目录 C源码生成序章1 概述1.1 前言1.2 Python 易用性简介 2 使用 python 生成 c 源码2.1 运行脚本2.2 结果 3 项目启动3.1 项目概述3.2 环境准备3.3 克隆仓库3.4 查看标签(Tags)3.4 根据标签拉取代码3.5 后续步骤 C源码生成序章 1 概述 1.1 前言 …...

Android中的MVP模式
MVP(Model-View-Presenter)架构在 Android 开发中是一种流行的架构模式,它将业务逻辑和 UI 代码分离,通过 Presenter 来处理用户的操作和界面更新。MVP 提高了代码的可维护性和测试性,特别是 Presenter 中的逻辑可以单…...

kebuadm部署k8s集群
官方文档: Installing kubeadm | Kubernetes 切记要关闭防⽕墙、selinux、禁用交换空间, cpu核⼼数⾄少为2 内存4G kubeadm部署k8s⾼可用集群的官方文档: Creating Highly Available Clusters with kubeadm | Kubernetes 你需要在每台…...

Unity3D学习FPS游戏(2)简单场景、玩家移动控制
前言:上一篇的时候,我们已经导入了官方fps的素材,并且对三维模型有了一定了解。接下来我们要构建一个简单的场景让玩家能够有地方移动,然后写一个简单的玩家移动控制。 简单场景和玩家移动 简单场景玩家移动控制玩家模型视野-摄像…...

网上的 AQS 文章让我很失望
一、AQS 很多人都没有讲明白 🤔 翻看了网上的 AQS(AbstractQueuedSynchronizer)文章,质量参差不齐,大多数都是在关键处跳过、含糊其词,美其名曰 “传播知识” 。 大多数都是进行大段的源码粘贴和注释&…...

滑动窗口子串
文章目录 滑动窗口一、无重复字符的最长子串二、找到字符串中所有字母异位词 子串三、和为 K 的子数组四、滑动窗口最大值五、最小覆盖子串 滑动窗口 一、无重复字符的最长子串 题目链接 (方法一:暴力枚举) (方法二ÿ…...

【windows11 提示“Microsoft Visual C++ Runtime Library Runtime Error】
windows11 提示“Microsoft Visual C++ Runtime Library Runtime Error” 问题描述解决方法郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: windows OS:windows11 问题描述 解决方法 下载VisualCppRedist_AIO_x86_x64.exe 安…...

【leetcode|哈希表、动态规划】最长连续序列、最大子数组和
目录 最长连续序列 解法一:暴力枚举 复杂度 解法二:优化解法一省去二层循环中不必要的遍历 复杂度 最大子数组和 解法一:暴力枚举 复杂度 解法二:贪心 复杂度 解法三:动态规划 复杂度 最长连续序列 输入输…...

【人工智能】掌握深度学习中的时间序列预测:深入解析RNN与LSTM的工作原理与应用
深度学习中的循环神经网络(RNN)和长短时记忆网络(LSTM)在处理时间序列数据方面具有重要作用。它们能够通过记忆前序信息,捕捉序列数据中的长期依赖性,广泛应用于金融市场预测、自然语言处理、语音识别等领域…...