逻辑地址、虚拟地址、物理地址以及内存管理

2023-05-16

本文涉及的硬件平台是X86,如果是其它平台,嘻嘻,不保证能一一对号入座,但是举一反三,我想是完全可行的。

1、概念

物理地址(physical address)
用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。
——这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽像,内存的寻址方式并不是这样。所以,说它是“与地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。也许错误的理解更利于形而上的抽像。

虚拟内存(virtual memory)
这是对整个内存(不要与机器上插那条对上号)的抽像描述。它是相对于物理内存来讲的,可以直接理解成“不直实的”,“假的”内存,例如,一个0x08000000内存地址,它并不对就物理地址上那个大数组中0x08000000 - 1那个地址元素;
之所以是这样,是因为现代操作系统都提供了一种内存管理的抽像,即虚拟内存(virtual memory)。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。这个“转换”,是所有问题讨论的关键。
有了这样的抽像,一个程序,就可以使用比真实物理地址大得多的地址空间。(拆东墙,补西墙,银行也是这样子做的),甚至多个进程可以使用相同的地址。不奇怪,因为转换后的物理地址并非相同的。
——可以把连接后的程序反编译看一下,发现连接器已经为程序分配了一个地址,例如,要调用某个函数A,代码不是call A,而是call 0x0811111111 ,也就是说,函数A的地址已经被定下来了。没有这样的“转换”,没有虚拟地址的概念,这样做是根本行不通的。
打住了,这个问题再说下去,就收不住了。

逻辑地址(logical address)
Intel为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。以上例,我们说的连接器为A分配的0x08111111这个地址就是逻辑地址。
——不过不好意思,这样说,好像又违背了Intel中段式管理中,对逻辑地址要求,“一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段标识符:段内偏移量],也就是说,上例中那个0x08111111,应该表示为[A的代码段标识符: 0x08111111],这样,才完整一些”

线性地址(linear address)或也叫虚拟地址(virtual address)
跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。


CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。

这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程。之所以这样冗余,Intel完全是为了兼容而已。

2、CPU段式内存管理,逻辑地址如何转换为线性地址

一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节,如图:
这里写图片描述
最后两位涉及权限检查,本文中不包含。

索引号,或者直接理解成数组下标——那它总要对应一个数组吧,它又是什么东东的索引呢?这个东东就是“段描述符(segment descriptor)”,呵呵,段描述符具体地址描述了一个段(对于“段”这个字眼的理解,我是把它想像成,拿了一把刀,把虚拟内存,砍成若干的截——段)。这样,很多个段描述符,就组了一个数组,叫“段描述符表”,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段,我刚才对段的抽像不太准确,因为看看描述符里面究竟有什么东东——也就是它究竟是如何描述的,就理解段究竟有什么东东了,每一个段描述符由8个字节组成,如下图:
这里写图片描述
这些东东很复杂,虽然可以利用一个数据结构来定义它,不过,我这里只关心一样,就是Base字段,它描述了一个段的开始位置的线性地址。

Intel设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。那究竟什么时候该用GDT,什么时候该用LDT呢?这是由段选择符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。

GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。

好多概念,像绕口令一样。这张图看起来要直观些:
这里写图片描述
首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],
1、看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。
2、拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。
3、把Base + offset,就是要转换的线性地址了。

还是挺简单的,对于软件来讲,原则上就需要把硬件转换所需的信息准备好,就可以让硬件来完成这个转换了。OK,来看看Linux怎么做的。

3、Linux的段式管理

Intel要求两次转换,这样虽说是兼容了,但是却是很冗余,呵呵,没办法,硬件要求这样做了,软件就只能照办,怎么着也得形式主义一样。
另一方面,其它某些硬件平台,没有二次转换的概念,Linux也需要提供一个高层抽像,来提供一个统一的界面。所以,Linux的段式管理,事实上只是“哄骗”了一下硬件而已。

按照Intel的本意,全局的用GDT,每个进程自己的用LDT——不过Linux则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用户代码段,对应的,内核中的是内核数据段和内核代码段。
include/asm-i386/segment.h


<code class="hljs cs has-numbering"><span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> GDT_ENTRY_DEFAULT_USER_CS        14</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)</span>

<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> GDT_ENTRY_DEFAULT_USER_DS        15</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)</span>

<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> GDT_ENTRY_KERNEL_BASE        12</span>

<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> GDT_ENTRY_KERNEL_CS                (GDT_ENTRY_KERNEL_BASE + 0)</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)</span>

<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> GDT_ENTRY_KERNEL_DS                (GDT_ENTRY_KERNEL_BASE + 1)</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)</span></code>  

把其中的宏替换成数值,则为:


<code class="hljs cs has-numbering"><span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __USER_CS 115        [00000000 1110  0  11]</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __USER_DS 123        [00000000 1111  0  11]</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __KERNEL_CS 96      [00000000 1100  0  00]</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> __KERNEL_DS 104    [00000000 1101  0  00]</span></code>  

方括号后是这四个段选择符的16位二制表示,它们的索引号和T1字段值也可以算出来了


<code class="hljs axapta has-numbering"> __USER_CS              <span class="hljs-keyword">index</span>= <span class="hljs-number">14</span>   T1=<span class="hljs-number">0</span>
__USER_DS               <span class="hljs-keyword">index</span>= <span class="hljs-number">15</span>   T1=<span class="hljs-number">0</span>
__KERNEL_CS           <span class="hljs-keyword">index</span>=  <span class="hljs-number">12</span>  T1=<span class="hljs-number">0</span>
__KERNEL_DS           <span class="hljs-keyword">index</span>= <span class="hljs-number">13</span>   T1=<span class="hljs-number">0</span></code>  

T1均为0,则表示都使用了GDT,再来看初始化GDT的内容中相应的12-15项(arch/i386/head.S):


<code class="hljs avrasm has-numbering">        <span class="hljs-preprocessor">.quad</span> <span class="hljs-number">0x00cf9a000000ffff</span>        <span class="hljs-comment">/* 0x60 kernel 4GB code at 0x00000000 */</span>
        <span class="hljs-preprocessor">.quad</span> <span class="hljs-number">0x00cf92000000ffff</span>        <span class="hljs-comment">/* 0x68 kernel 4GB data at 0x00000000 */</span>
        <span class="hljs-preprocessor">.quad</span> <span class="hljs-number">0x00cffa000000ffff</span>        <span class="hljs-comment">/* 0x73 user 4GB code at 0x00000000 */</span>
        <span class="hljs-preprocessor">.quad</span> <span class="hljs-number">0x00cff2000000ffff</span>        <span class="hljs-comment">/* 0x7b user 4GB data at 0x00000000 */</span></code>  

照前面段描述符表中的描述,可以把它们展开,发现其16-31位全为0,即四个段的基地址全为0。

这样,给定一个段内偏移地址,按照前面转换公式,0 + 段内偏移,转换为线性地址,可以得出重要的结论,“在Linux下,逻辑地址与线性地址总是一致(是一致,不是有些人说的相同)的,即逻辑地址的偏移量字段的值与线性地址的值总是相同的。!!!”

忽略了太多的细节,例如段的权限检查。呵呵。

Linux中,绝大部份进程并不例用LDT,除非使用Wine ,仿真Windows程序的时候。

4.CPU的页式内存管理

CPU的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,称为页(page),例如一个32位的机器,线性地址最大可为4G,可以用4KB为一个页来划分,这页,整个线性地址就被划分为一个tatol_page[2^20]的大数组,共有2的20个次方个页。这个大数组我们称之为页目录。目录中的每一个目录项,就是一个地址——对应的页的地址。

另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。

这里注意到,这个total_page数组有2^20个成员,每个成员是一个地址(32位机,一个地址也就是4字节),那么要单单要表示这么一个数组,就要占去4MB的内存空间。为了节省空间,引入了一个二级管理模式的机器来组织分页单元。文字描述太累,看图直观一些:
这里写图片描述
如上图,
1、分页单元中,页目录是唯一的,它的地址放在CPU的cr3寄存器中,是进行地址转换的开始点。万里长征就从此长始了。
2、每一个活动的进程,因为都有其独立的对应的虚似内存(页目录也是唯一的),那么它也对应了一个独立的页目录地址。——运行一个进程,需要将它的页目录地址放到cr3寄存器中,将别个的保存下来。
3、每一个32位的线性地址被划分为三部份,面目录索引(10位):页表索引(10位):偏移(12位)
依据以下步骤进行转换:
1、从cr3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器);
2、根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。
3、根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址;
4、将页的起始地址与线性地址中最后12位相加,得到最终我们想要的葫芦;

这个转换过程,应该说还是非常简单地。全部由硬件完成,虽然多了一道手续,但是节约了大量的内存,还是值得的。那么再简单地验证一下:
1、这样的二级模式是否仍能够表示4G的地址;
页目录共有:2^10项,也就是说有这么多个页表
每个目表对应了:2^10页;
每个页中可寻址:2^12个字节。
还是2^32 = 4GB

2、这样的二级模式是否真的节约了空间;
也就是算一下页目录项和页表项共占空间 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么说呢!!!
红色错误,标注一下,后文贴中有此讨论。。。。。。
按<深入理解计算机系统>中的解释,二级模式空间的节约是从两个方面实现的:
A、如果一级页表中的一个页表条目为空,那么那所指的二级页表就根本不会存在。这表现出一种巨大的潜在节约,因为对于一个典型的程序,4GB虚拟地址空间的大部份都会是未分配的;
B、只有一级页表才需要总是在主存中。虚拟存储器系统可以在需要时创建,并页面调入或调出二级页表,这就减少了主存的压力。只有最经常使用的二级页表才需要缓存在主存中。——不过Linux并没有完全享受这种福利,它的页表目录和与已分配页面相关的页表都是常驻内存的。

值得一提的是,虽然页目录和页表中的项,都是4个字节,32位,但是它们都只用高20位,低12位屏蔽为0——把页表的低12屏蔽为0,是很好理解的,因为这样,它刚好和一个页面大小对应起来,大家都成整数增加。计算起来就方便多了。但是,为什么同时也要把页目录低12位屏蔽掉呢?因为按同样的道理,只要屏蔽其低10位就可以了,不过我想,因为12>10,这样,可以让页目录和页表使用相同的数据结构,方便。

本文只介绍一般性转换的原理,扩展分页、页的保护机制、PAE模式的分页这些麻烦点的东东就不啰嗦了……可以参考其它专业书籍。

5.Linux的页式内存管理

原理上来讲,Linux只需要为每个进程分配好所需数据结构,放到内存中,然后在调度进程的时候,切换寄存器cr3,剩下的就交给硬件来完成了(呵呵,事实上要复杂得多,不过偶只分析最基本的流程)。

前面说了i386的二级页管理架构,不过有些CPU,还有三级,甚至四级架构,Linux为了在更高层次提供抽像,为每个CPU提供统一的界面。提供了一个四层页管理架构,来兼容这些二级、三级、四级管理架构的CPU。这四级分别为:

页全局目录PGD(对应刚才的页目录)
页上级目录PUD(新引进的)
页中间目录PMD(也就新引进的)
页表PT(对应刚才的页表)。

整个转换依据硬件转换原理,只是多了二次数组的索引罢了,如下图:
这里写图片描述
那么,对于使用二级管理架构32位的硬件,现在又是四级转换了,它们怎么能够协调地工作起来呢?嗯,来看这种情况下,怎么来划分线性地址吧!

从硬件的角度,32位地址被分成了三部份——也就是说,不管理软件怎么做,最终落实到硬件,也只认识这三位老大。
从软件的角度,由于多引入了两部份,,也就是说,共有五部份。——要让二层架构的硬件认识五部份也很容易,在地址划分的时候,将页上级目录和页中间目录的长度设置为0就可以了。
这样,操作系统见到的是五部份,硬件还是按它死板的三部份划分,也不会出错,也就是说大家共建了和谐计算机系统。

这样,虽说是多此一举,但是考虑到64位地址,使用四层转换架构的CPU,我们就不再把中间两个设为0了,这样,软件与硬件再次和谐——抽像就是强大呀!!!

例如,一个逻辑地址已经被转换成了线性地址,0x08147258,换成二制进,也就是:
0000100000 0101000111 001001011000
内核对这个地址进行划分
PGD = 0000100000
PUD = 0
PMD = 0
PT = 0101000111
offset = 001001011000

现在来理解Linux针对硬件的花招,因为硬件根本看不到所谓PUD,PMD,所以,本质上要求PGD索引,直接就对应了PT的地址。而不是再到PUD和PMD中去查数组(虽然它们两个在线性地址中,长度为0,2^0 =1,也就是说,它们都是有一个数组元素的数组),那么,内核如何合理安排地址呢?

从软件的角度上来讲,因为它的项只有一个,32位,刚好可以存放与PGD中长度一样的地址指针。那么所谓先到PUD,到到PMD中做映射转换,就变成了保持原值不变,一一转手就可以了。这样,就实现了“逻辑上指向一个PUD,再指向一个PDM,但在物理上是直接指向相应的PT的这个抽像,因为硬件根本不知道有PUD、PMD这个东西”。

然后交给硬件,硬件对这个地址进行划分,看到的是:
页目录 = 0000100000
PT = 0101000111
offset = 001001011000
嗯,先根据0000100000(32),在页目录数组中索引,找到其元素中的地址,取其高20位,找到页表的地址,页表的地址是由内核动态分配的,接着,再加一个offset,就是最终的物理地址了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

逻辑地址、虚拟地址、物理地址以及内存管理 的相关文章

  • Error处理: “非法字符: \65279”的解决办法

    从Eclipse中复制项目到Android源码环境中 xff0c 编译时遇到 非法字符 xff1a 65279 的报错 出错内容是 xff1a java 1 非法字符 xff1a 65279 javac xfeff package com
  • 手把手教你VMware虚拟机详细安装Debian 11 图文教程

    目录 一 准备工作 二 创建安装Debain的虚拟机 三 安装 Debian 11 过程 一 准备工作 1 虚拟机软件 VMware 16 https www vmware com go getworkstation win 2 Debia
  • Python3利用Pandas类库生成多Sheet的Excel

    一 Pandas Pandas 是 Python 语言的一个扩展程序库 xff0c 可以对各种数据进行运算操作 xff0c 比如归并 再成形 选择 xff0c 还有数据清洗和数据加工特征 官方地址 xff1a https pandas py
  • Debian 11 安装并开启SSH服务实现允许root用户使用SecureCRT远程登录

    虚拟机安装完成Debian11系统后 xff0c 系统默认是没有安装SSH服务 xff0c 如需要开启远程登录则需要安装相应的服务 登录系统后切换至Root用户身份并执行下面相关操作 一 更新Debian系统数据软件源 apt get up
  • Java利用poi 3.9对excel进行读写操作的工具类支持多Sheet

    一 什么是POI POI全称PoorObfuscation Implementation xff0c 是Apache组件的一个开源项目 xff0c 可以对微软的Office一系列办公软件进行读写操作 官方下载地址 xff1a https p
  • JAVA利用POI scratchpad 5.2.1 将Word文档doc格式转换成HTML 格式 含文档里面图片

    一 POM文件 lt dependency gt lt groupId gt org apache poi lt groupId gt lt artifactId gt poi scratchpad lt artifactId gt lt
  • Centos7搭建安装freeswitch1.6

    前言 xff1a 请各大网友尊重本人原创知识分享 xff0c 谨记本人博客 xff1a 南国以南i 介绍 xff1a freeswitch可集成ASR 语音识别 和TTS 文本转语音 创建智能电话机器人和用户通话 xff0c 可用于问卷调查
  • 树莓派安装远程桌面(XRDP)服务

    首先试用了一下VNC xff0c 发现质量惨不忍睹 xff0c 明明是在局域网内啊 于是打算安装XRDP服务 xff0c 网上各种文章都有 xff0c 有的说需要先安装tightVNC 参考了 xff1a http shumeipai nx
  • 关于ubuntu20.04中安装ros的一些package时出现“unable to locate”的问题记录

    在运行ros仿真时出现如下错误 xff1a 提示缺少 gmapping的包 xff0c 直接下载包 xff1a sudo apt get install ros noetic gmapping 结果提示 xff1a Unable to lo
  • Linux配置篇 | Ubuntu配置apt镜像源

    以下以 Ubuntu18 04 LTS 为例 xff0c 也适用于 Ubuntu 其他版本 一 修改apt镜像源 xff08 1 xff09 备份apt配置文件 xff1a cp etc apt sources list etc apt s
  • 多种缺陷管理软件简介

    缺陷管理工具 xff1a 1 Bugzilla 2 Bugfree 3 TestDirector xff08 Quality Center xff09 4 ClearQuest 5 JIRA 6 Mantis 7 Bugzero 8 Bug
  • 线程与进程之间的共享资源

    线程和进程之间的共享资源方式 进程之间的共享资源的方式 1 消息队列 2 共享内存 3 管道 xff08 有名管道 无名管道 xff09 4 信号 5 套接字 同一个进程的不同线程之间可以共享的资源 1 堆 xff0c 由于堆是在进程启动的
  • Redis基本知识

    总结 基础知识 基础命令 1 xff09 测试Redis性能 2 xff09 Redis沟通命令 xff0c 查看状态 解释 xff1a 输入ping xff0c redis给我们返回PONG xff0c 表示redis服务运行正常 3 x
  • redis的配置文件

    回顾 redis的配置文件 1 redis安装完成之后 xff0c 在redis的根沐会提供一个配置文件 redis conf xff1b redis服务可以参考配置文件中的参数进行运行 xff1b 只有启动redis服务器指定使用的配置文
  • VSCode 配置 C++ 环境

    1 1 MinGW 编译套件 MinGW 提供了一套简单方便的 Windows 下的基于 GCC 程序开发环境 xff0c 并且 收集了一系列免费的 Windows 使用的头文件和库文件 它整合了 GNU 的工具集 xff0c 特别是 GN
  • C++中string与char*相互转换

    C 43 43 中string与char 相互转换 一 string转换为char 有3中方法 xff1a 1 data string str 61 34 good boy 34 const char p 61 str data 2 c s
  • Python hex()十六进制转换

    Python hex 函数用于将整数转换为以 0x 为前缀的小写十六进制字符串 xff1a print hex 255 decimal十进制 print hex 0b111 binary二进制 print hex 0o77 octal八进制
  • rocksdb的原子flush

    如果始终启用 WAL xff0c 则无需使用原子刷新选项 启用 WAL 后 xff0c 单个 WAL 文件用于捕获对所有列族的写入 因此 xff0c 恢复的数据库 xff08 通过在崩溃 恢复路径中重播 WAL 日志 xff09 保证在所有
  • rocksdb的设置选项和基本调整

    除了在 RocksDB 上使用基本操作编写代码外 xff0c 您可能还对如何调整 RocksDB 以实现所需的性能感兴趣 在本页中 xff0c 我们将介绍如何进行初始设置 xff0c 该设置应该足以满足许多用例的需求 RocksDB 有许多
  • rocksdb的wal配置

    wal配置 数据库 xff1a xff1a wal dir DBOptions wal dir设置 RocksDB 存储预写日志文件的目录 xff0c 该目录允许将 WAL 存储在与实际数据不同的目录中 数据库 xff1a xff1a WA

随机推荐

  • Winpcap常用函数解析

    1 int pcap findalldevs pcap if t char 说明 xff1a 用来获得网卡的列表 参数 xff1a 指向pcap if t 类型的列表的指针的指针 char型指针 当打开列表错误时返回错误信息 返回值 为in
  • 终端、虚拟终端、shell、控制台、tty的区别

    终端与控制台的区别 xff1f 最近开始接触Linux xff0c 终端 虚拟终端 shell 控制台 tty等概念让我很混乱 xff0c 有必要认识清楚 今天看到有人问终端和控制台的区别 xff0c 而且这个问题比较有普遍性 xff0c
  • Windows下C++使用thread时无法识别thread和mutex相关库的解决

    问题描述 今天我在Windows下打算尝试C 43 43 多线程编程 xff0c 在CLion上进行编码 CLion的C 43 43 编译器是正常的 xff0c 以前也跑过好几个项目 xff0c 使用其他STL库函数也正常 xff0c 唯独
  • 虚拟机下Linux系统磁盘扩容

    在VM虚拟机中 xff0c 我们经常会选择默认磁盘大小20G xff0c 用着用着才发现20G不够用 xff0c 服务启动不了 xff0c 就很尴尬 xff0c 让我们今天一起来学习下 xff0c 如何在虚拟机给磁盘扩容 一 xff1a 关
  • MATLAB学习笔记——二维和三维绘图

    MATLAB学习笔记 二维和三维绘图 近期练习matlab的二维和三维绘图 xff0c 整理一下 xff0c 以防忘记 文章目录 MATLAB学习笔记 二维和三维绘图一 二维绘图1 plot命令2 fplot 命令3 ezplot 绘图命令
  • pve 相关

    一 pve 创建的虚拟机的配置文件位置 在宿主机的 etc pve qemu server xff0c 这里有创建虚拟机的相关硬件信息 root 64 pve span class token comment cd etc pve qemu
  • mysql在linux下的my.cnf文件在哪里!

    用rpm包安装的MySQL是不会安装 etc my cnf文件的 xff0c 至于为什么没有这个文件而MySQL却也能正常启动和作用 xff0c 在点有两个说法 xff0c 第一种说法 xff0c my cnf只是MySQL启动时的一个参数
  • shell后台并发执行的最佳实践

    一 shell如何在后台执行 1 nohup命令 通常我们都是远程登录linux终端 xff0c 而当我们退出终端时在之前终端运行的程序都会终止 xff0c 有时候先想要退出终端也要程序继续执行这时nohup就登场了 nohup命令 可以将
  • 任意输入10个数,找出最大数和最小数。

    任意输入10个数 找出最大数和最小数 span class token macro property span class token directive keyword include span span class token stri
  • 如何在 Chromebook 上启用开发者模式

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【C语言】实现 atoi 函数

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【C语言】字符串合集

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 线程上下文切换和进程上下文切换的区别

    进程切换分两步 1 切换页目录以使用新的地址空间 2 切换内核栈和硬件上下文 对于linux来说 xff0c 线程和进程的最大区别就在于地址空间 对于线程切换 xff0c 第1步是不需要做的 xff0c 第2是进程和线程切换都要做的 所以明
  • 【Ubuntu】解决 Could not get lock /var/lib/dpkg/lock-frontend

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【Ubuntu】解决 error: dpkg frontend is locked by another process

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【OpenCV】关于Vec3b类型的含义与使用

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【Java】文件读写和输入输出

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【剑指Offer】题3:数组中重复的数字

    写在前面的话 xff1a 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查证后会进
  • 【C语言】关于遍历字符串的三种方法

    写在前面的话 xff1a 1 版权声明 xff1a 本文为博主原创文章 xff0c 转载请注明出处 xff01 2 博主是一个小菜鸟 xff0c 并且非常玻璃心 xff01 如果文中有什么问题 xff0c 请友好地指出来 xff0c 博主查
  • 逻辑地址、虚拟地址、物理地址以及内存管理

    本文涉及的硬件平台是X86 xff0c 如果是其它平台 xff0c 嘻嘻 xff0c 不保证能一一对号入座 xff0c 但是举一反三 xff0c 我想是完全可行的 1 概念 物理地址 physical address 用于内存芯片级的单元寻