如何在云电脑实现虚拟应用—数据分层(应用分层)技术简介
数据分层(应用分层)技术简介
近几年虚拟化市场实现了非常大的发展,桌面虚拟化在企业中应用越来越广泛,其拥有的如下优点得到大量企业的青睐:
- 数据安全不落地。在虚拟化环境下面数据保存在中心服务器上面,只要保障中心服务器的安全,那么就能保障数据的绝对安全。
- 高扩展性。与普通的硬件PC相比,桌面虚拟化具有高扩展性,可以随时将虚拟化资源归还给虚拟化主机以及分配给其他虚拟化主机使用。
- 容易部署。桌面虚拟化一般可以通过模板话部署,将应用(数据)一键部署到新的虚拟机上。
- 统一化资产管理。资产管理一直是企业管理的难点,市面上有许多公司的管理软件都包括了资产管理这一环;在桌面虚拟化下面,资产管理得到了很好的解决。
通常情况下的虚拟化架构如下:

在上述架构中,我们可以安装层级来划分:
- 硬件层:主要是我们的实体硬件,包括内存,CPU,存储。
- 主机层:主要是我们的Host主机操作系统,一般来说以Linux居多。
- 客户机层:主要是我们的虚拟机操作系统,在这一层上面实现我们的具体应用(办公,开发,娱乐等)。
那么能否对客户机(Guest)这一层继续进行细化分层呢?例如我们实现:
- 我们将个人数据存放在一个单独层级。
- 无论我们使用虚拟机的还原系统,还是登录一台全新的虚拟机,我们将数据层进行合并,让我们个人数据和应用不会丢失。
本文来介绍一种在虚拟机中数据分层的技术,下面我们详细看一下其技术原理和实现。
1. 简介
数据分层是针对虚拟机来说的,以Windows系统为例,从下到上,我们可以将其分为三层:
- 系统OS层,该层表示Windows系统镜像安装的原始OS层,代表这一个可正常运行的OS。
- 应用层,该层表示各种应用程序组成的层,例如Chrome浏览器,微信,QQ等各种应用程序。
- 数据层,该层表示应用程序运行时候生成的各种文件数据,例如浏览器的记录,收藏夹;QQ微信的聊天数据。
其示意图大致如下:

一般来说,每个层都是一个独立的磁盘,将每个磁盘通过分层技术合并成一个整体可以正常使用运行的磁盘,如下:

对于各层来说,一般有如下特征:
- 操作系统层,只有一个数据磁盘表示该层,表示运行的系统。
- 应用程序层,该层是一个集合层,多个应用程序层元素组成该层,表示通常情况下我们需要的应用程序。
- 个人数据层,该层可以由一个或者多个磁盘组成,不过大部分情况下,我们只需要一个磁盘层即可。
对于上面这种情况,在实际的使用场景下可以进一步简化,将操作系统层和引用程序层合并成一个层,大致如下:

对于通常场景,我们可以将操作系统层和应用程序层合并成一个层到操作系统层;也就是说,我们在安装操作系统的时候,就将我们需要的应用软件安装好;后面使用过程中我们只需要弹性使用个人数据层即可。
我们的数据分层主要是针对这种简化的分层,对于应用分层可以参考我们的文章Windows内核沙盒原理详解。
对于数据分层我们的应用有:
- 还原系统下面,我们可以将自己程序的各种数据存放到自己的磁盘(或者云盘上面),这样我们虚拟主机是还原系统,但是数据是个人的数据,可以达到虚拟机重复利用的目的;例如我们10台主机可以分时间段给100个人(甚至更多人)使用,只要拥有个人数据磁盘就行。
- 数据上云,比如我们可以将个人数据层同步到自己的云盘;无论我们使用哪个电脑(虚拟机或者物理主机),可以将数据层进行合并,使得每台电脑使用的数据完全一致。
针对数据分层,我们主要的技术在于分层数据的合并,对于这个合并,有两个点:
- 注册表数据的合并。
- 文件数据的合并。
2. 注册表数据分层
注册表数据的分层需要对注册表的各种查询进行HOOK,然后将数据查询进行合并,将数据写入进行分发;注册表的HOOK技术有两种:
- 基于用户层API HOOK技术。
- 基于内核回调函数的HOOK技术。
这里我们使用内核回调函数的实现方式,使用CmRegisterCallbackEx来注册注册表的各种回调函数:
NTSTATUS CmRegisterCallbackEx(PEX_CALLBACK_FUNCTION Function,PCUNICODE_STRING Altitude,PVOID Driver,PVOID Context,PLARGE_INTEGER Cookie,PVOID Reserved
);
注册表的各种回调函数有如下:
typedef enum _REG_NOTIFY_CLASS {RegNtDeleteKey,RegNtPreDeleteKey,RegNtSetValueKey,RegNtPreSetValueKey,RegNtDeleteValueKey,RegNtPreDeleteValueKey,RegNtSetInformationKey,RegNtPreSetInformationKey,RegNtRenameKey,//...
} REG_NOTIFY_CLASS;
理论上我们需要对所有函数进行处理,主要的操作有:
RegNtPreCreateKeyEx对注册表的打开做处理(打开操作系统层的注册表还是打开数据层的注册表)。RegNtPreQueryValueKey对注册表值得查询做处理,一般来说我们需要对注册表得值进行合并(选择的方式是将数据层覆盖操作系统层的注册表值,当然还需要做很多情况的处理)。RegNtPreEnumerateKey枚举注册表项,这个函数也是最复杂的实现函数,因为我们需要对操作系统层和数据层的注册表项进行合并,去重等。RegNtPreDeleteKey和RegNtPreDeleteValueKey注册表的删除,也是非常麻烦的操作,因为我们需要对注册表进行标记处理(不能将操作系统层的数据真实删除)。
上面这些接口只是示例,实际的实现要复杂很多,大致例如如下:
regRoutineTable[RegNtPreQueryValueKey] = NanosRegNtPreQueryValueKeyCallback;
regRoutineTable[RegNtPreEnumerateValueKey] = NanosRegNtPreEnumerateValueKeyCallback;
regRoutineTable[RegNtPreQueryMultipleValueKey] = NanosRegNtPreQueryMultipleValueKeyCallback;
regRoutineTable[RegNtPreDeleteValueKey] = NanosRegNtPreDeleteValueKeyCallback;
regRoutineTable[RegNtPreDeleteKey] = NanosRegNtPreDeleteKeyCallback;
regRoutineTable[RegNtPreRenameKey] = NanosRegNtPreRenameKeyCallback;
regRoutineTable[RegNtPostRenameKey] = NanosRegNtPostRenameKeyCallback;
regRoutineTable[RegNtPreEnumerateKey] = NanosRegNtPreEnumerateKeyCallback;
regRoutineTable[RegNtPreQueryKey] = NanosRegNtPreQueryKeyCallback;
regRoutineTable[RegNtPostQueryKey] = NanosRegNtPostQueryKeyCallback;
regRoutineTable[RegNtPreSetValueKey] = NanosRegNtPreSetValueKeyCallback;
regRoutineTable[RegNtPreCreateKeyEx] = NanosRegNtPreCreateKeyExCallback;
regRoutineTable[RegNtPreOpenKeyEx] = NanosRegNtPreOpenKeyExCallback;
regRoutineTable[RegNtCallbackObjectContextCleanup] = NanosRegNtCallbackObjectContextCleanupCallback;
regRoutineTable[RegNtPreQueryKeySecurity] = NanosRegNtPreQueryKeySecurityCallback;
regRoutineTable[RegNtPreSetKeySecurity] = NanosRegNtPreSetKeySecurityCallback;
完成上述注册表的函数之后,我们就完成了注册表的分层合并和写入的功能了。
3. 文件分层
文件分层来说和注册表类似也可以使用两种方式来实现:
- 基于用户层API HOOK技术。
- 基于文件过滤驱动来实现。
这里我们使用基于Minifilter的文件系统过滤驱动来实现,Minfilter基本框架如下:

我们使用FltRegisterFilter来注册文件系统过滤驱动,如下:
NTSTATUS FLTAPI FltRegisterFilter(PDRIVER_OBJECT Driver,const FLT_REGISTRATION *Registration,PFLT_FILTER *RetFilter
);
该函数需要我们提供各种文件系统的回调函数,如下:
typedef struct _FLT_REGISTRATION {USHORT Size;USHORT Version;FLT_REGISTRATION_FLAGS Flags;const FLT_CONTEXT_REGISTRATION *ContextRegistration;const FLT_OPERATION_REGISTRATION *OperationRegistration;PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback;PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback;PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback;PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback;PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback;PFLT_GENERATE_FILE_NAME GenerateFileNameCallback;PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback;PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback;PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback;PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback;PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback;
} FLT_REGISTRATION, *PFLT_REGISTRATION;
同样对于上述文件过滤驱动我们需要实现其所有回调函数,这里我们简要介绍几个功能:
IRP_MJ_CREATE是文件创建的回调函数,在该函数中,我们需要实现文件的重查询,文件的写时拷贝,文件安装属性等功能。IRP_MJ_SET_INFORMATION文件设置,这里有两个重要的流程需要处理就是删除和重命名,对于操作系统层的文件,需要对其进行虚拟删除(一般是通过标记法来标记文件的删除)。IRP_MJ_DIRECTORY_CONTROL这个是目录文件的查询,这个函数也是非常复杂的一个函数,主要需要对操作系统层和数据层的数据进行查询合并。
我们对IRP_MJ_CREATE重查询的关键代码做分析,大致如下实现对文件的重查询:
FLT_PREOP_CALLBACK_STATUS NanosFileCreatePreCallback(PFLT_CALLBACK_DATA Cbd, PCFLT_RELATED_OBJECTS FltObjects, PVOID *CompletionContext)
{ UNICODE_SIRING NewFileName; //将要重定向的目标文件路径//... FileObject = Cbd->Iopb->TargetFileObject;status = IoReplaceFileObjectName(FileObject ,NewFileName.Buffer,NewFileName.Length); //替换文件对象的名称if (status < 0){FileObject->FileName = NewFileName;NewFileName.Length = 0;NewFileName.MaximumLength = 0;NewFileName.Buffer = 0;}Cbd->IoStatus.Status = STATUS_REPARSE; //告诉系统重新分析文件对象的名称,将对新文件名发起一个新的I/O请求。//...
}
4. 实现效果
通过上面的文件和注册表分层实现之后,我们就可以完成对数据的分层实现了;一般来说为例不影响用户的体验,对于分层数据磁盘我们是隐藏起来;因此在使用的时候,我们并无法看到该磁盘的存在;该磁盘被合并到了整个操作系统层,实现如下:
首先我们将分层驱动停用,查看操作系统盘和数据盘数据,如下:

启用我们数据分层驱动之后,我们可以看到数据盘的数据已经合并到了操作系统盘C盘,如下:

可以查看具体数据如下:

至此,我们完成了操作系统和整个数据盘的合并(或者换一种说法我们将操作系统和数据层分开存储,但是合并显示了)。
和数据分层类似,我们可以对整个应用程序进行分层,可以参考https://blog.csdn.net/tianxilink/article/details/132612811?spm=1001.2014.3001.5502的实现。
相关文章:
如何在云电脑实现虚拟应用—数据分层(应用分层)技术简介
数据分层(应用分层)技术简介 近几年虚拟化市场实现了非常大的发展,桌面虚拟化在企业中应用越来越广泛,其拥有的如下优点得到大量企业的青睐: 数据安全不落地。在虚拟化环境下面数据保存在中心服务器上面,…...
【动态规划五】回文串问题
目录 leetcode题目 一、回文子串 二、最长回文子串 三、分割回文串 IV 四、分割回文串 II 五、最长回文子序列 六、让字符串成为回文串的最少插入次数 leetcode题目 一、回文子串 647. 回文子串 - 力扣(LeetCode)https://leetcode.cn/problems/…...
【C++杂货铺铺】AVL树
目录 🌈前言🌈 📁 概念 📁 节点的定义 📁 插入 📁 旋转 1 . 新节点插入较高左子树的左侧---左左:右单旋 2. 新节点插入较高右子树的右侧---右右:左单旋 3. 新节点插入较高左…...
【R语言】生存分析模型
生存分析模型是用于研究时间至某个事件发生的概率的统计模型。这个事件可以是死亡、疾病复发、治疗失败等。生存分析模型旨在解决在研究时间相关数据时的挑战,例如右侧截尾(右侧截尾表示未观察到的事件发生,例如研究结束时还未发生事件&#…...
「AIGC」Python实现tokens算法
本文主要介绍通过python实现tokens统计,避免重复调用openai等官方api,开源节流。 一、设计思路 初始化tokenizer使用tokenizer将文本转换为tokens计算token的数量二、业务场景 2.1 首次加载依赖 2.2 执行业务逻辑 三、核心代码 from transformers import AutoTokenizer imp…...
【Unity】编程感悟20240510
【背景】 这一点感悟是过去有所认识,但是最近写Unity项目,涉及UDP通信需要持续监听逻辑时更加感受深刻的。 选用合适的触发点,用明确的逻辑避免循环处理 尽量采用明确的触发点使逻辑清晰,规避一定时间刷新这类的逻辑。 比如UDP…...
C#【进阶】泛型
1、泛型 文章目录 1、泛型1、泛型是什么2、泛型分类3、泛型类和接口4、泛型方法5、泛型的作用思考 泛型方法判断类型 2、泛型约束1、什么是泛型2、各泛型约束3、约束的组合使用4、多个泛型有约束思考1 泛型实现单例模式思考2 ArrayList泛型实现增删查改 1、泛型是什么 泛型实现…...
50. UE5 RPG FGameplayEffectContext
接下来,我想实现处理完伤害时,将伤害的触发格挡或者触发暴击时的逻辑传递到数据集的PostGameplayEffectExecute里面,这样,在处理IncomingDamage时,我们可以通过释放触发格挡或者触发暴击在UI上面进行对应的效果表现。 …...
Golang 的 unmarshal 踩坑指南
文章目录 1. 写在最前面2. 字段区分出空字段还是未设置字段2.1 问题描述2.2 解决 3. 字段支持多种类型 & 按需做不同类型处理3.1 问题描述3.2 解决 4. 碎碎念5. 参考资料 1. 写在最前面 笔者最近在实现将内部通知系统的数据定义转化为产品定义的对外提供的数据结构。 举例…...
Linux的常用指令 和 基础知识穿插巩固(巩固知识必看)
目录 前言 ls ls 扩展知识 ls -l ls -a ls -al cd cd 目录名 cd .. cd ~ cd - pwd 扩展知识 路径 / cp [选项] “源文件名” “目标文件名” mv [选项] “源文件名” “目标文件名” rm 作用 用法 ./"可执行程序名" mkdir rmdir touch m…...
MP3解码入门(基于libhelix)
主要参考资料: 【Arduino Linux】基于 Helix 解码库实现 MP3 音频播放: https://blog.csdn.net/weixin_42258222/article/details/122640413 libhelix-mp3: https://github.com/ultraembedded/libhelix-mp3/tree/master 目录 一、MP3文件二、MP3 解码库三、libhelix-mp3库3.1 …...
Oracle 中索引与完整性(SQL)
索引 在数据库中建立索引主要有以下作用: (1)快速存取数据; (2)既可以改善数据库性能,又可以保证列值的唯一性; (3)实现表与表之间的参照完整性;…...
【Linux深度学习笔记5.13(Apache)】
Apache : 1.安装yum -y install hhtpd2.启动hhtpd -k start3.停止httpd -k stop4.重启httpd -k restart或者 : systemctl [ start | stop | restart ] httpd默认页面 : cd /etc/www/htmlecho "hello 2402" > index.html验证 : 浏览器访问 : http://ip 访问控制…...
汇编语言入门:探索 x86 架构
目录 前言 1. x86 语言 x86 架构简介 x86 架构的特点 x86 架构的演变 x86 架构的应用 2. 常用汇编指令集 3. 寻址方式 结语 前言 汇编语言是一种低级编程语言,直接面向计算机的硬件架构。在计算机科学中,了解汇编语言是非常重要的,因…...
[ffmpeg处理指令]
1 将h264转为mp4 ffmpeg -f h264 -i front_far_0.264 -vcodec copy front_far_0.mp4 ffmpeg -f h264 -i front_near_0.264 -vcodec copy front_near_0.mp4 -i:表示输入文件 front_far_2.mp4:表示输出文件 2 h264转为图片 front_far 是目标路径,需要…...
测试之路 - 精准而优雅
引子 这几年业内一直在做精准测试,大都使用工具 diff 代码改动、分析代码覆盖率这些平台集成的能力。 业务测试中,我们在技术设计和代码实现的基础上也做了一些精减和精准的测试实践,通过深入测试有针对的设计 case,发现隐藏问题…...
Java基础篇常见面试问题总结
文章目录 1. 你是怎样理解 OOP面向对象?2. 重载与重写区别3. 接口与抽象类的区别4. 深拷贝与浅拷贝的理解5. 什么是自动拆装箱? int和 Integer有什么区别6. 和 equals()区别7. String类 能被继承吗为什么用 final修饰8. final、finally、finalize区别 1. 你是怎样理…...
Spring、SpringMVC
一、Spring框架中的单例Bean是线程安全的吗? 【默认单例的情况下】Spring Bean并没有可变的状态(如Service类和DAO类),即只能查不能改,所以没有并发问题,所以某种程度上来说Spring的单例Bean是线程安全的。…...
【传知代码】VRT: 关于视频修复的模型(论文复现)
前言:随着数字媒体技术的普及,制作和传播视频内容变得日益普遍。但是,视频中由于多种因素,例如传输、存储和录制设备等,经常出现质量上的问题,如图像模糊、噪声干扰和低清晰度等。这类问题对用户的体验和观…...
不用投稿邮箱,怎样向各大新闻媒体投稿?
身为单位的信息宣传员,我深知肩上责任重大。每个月,完成单位在媒体上投稿发表文章的考核任务,就如同一场无声的赛跑,既要保证速度,更要注重质量。起初,我遵循“前辈们”的老路,一头扎进了邮箱投稿的海洋。但很快,现实给了我一记重拳——邮箱投稿的竞争犹如千军万马过独木桥,稿件…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
