diff --git a/2019/12/28/qt_ftp_chinese_path/index.html b/2019/12/28/qt_ftp_chinese_path/index.html new file mode 100644 index 0000000..fe9360d --- /dev/null +++ b/2019/12/28/qt_ftp_chinese_path/index.html @@ -0,0 +1,525 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QT使用FTP传输文件时的中文路径问题 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ QT使用FTP传输文件时的中文路径问题 +

+ + +
+ + + + +
+ + +

最近有关于FTP文件传输的相关项目,因此查询相关资料编写了一个示例程序。程序运行正常,但在测试过程中使用含有中文的文件进行测试时,程序报错。

+​ + +

原以为是编码的问题,认为FTP不识别UTF-8编码,因此在FTP的配置项中查询,发现其对UTF斌吗是支持的。后面继续查找相关资料。找到了问题所在。(可参阅新浪博客)

+

Windows的本地默认编码为gbk(如果你用的是中文系统),Linux本地默认编码为UTF-8。而我们现在使用的QT编码一般设置为UTF-8,因此当进行文件传输时,FTP服务器将路径以gbk编码方式进行解码。当路径不含中文时,还没什么影响,但当路径含有中文时,无论是文件名含有中文,还是目标文件夹路径含有中文,都会发生以上路径无法被正确识别的问题。修改如下:

+
//将gbk编码的字符串改为UTF-8编码,在获取FTP服务器下文件时使用
+QString FromSpecialEncoding(const QString &InputStr)  
+{  
+    #ifdef Q_OS_WIN  
+        return  QString::fromLocal8Bit(InputStr.toLatin1());  
+    #else  
+        QTextCodec *codec = QTextCodec::codecForName("gbk");  
+        if (codec)  
+        {  
+            return codec->toUnicode(InputStr.toLatin1());  
+        }  
+        else  
+        {  
+            return QString("");  
+        }  
+    #endif  
+}  
+//将UTF-8编码的字符串改为gbk编码,在由客户端上传或下载文件时使用
+QString ToSpecialEncoding(const QString &InputStr)  
+{  
+    #ifdef Q_OS_WIN  
+        return QString::fromLatin1(InputStr.toLocal8Bit());  
+    #else  
+    QTextCodec *codec= QTextCodec::codecForName("gbk");  
+    if (codec)  
+    {  
+        return QString::fromLatin1(codec->fromUnicode(InputStr));  
+    }  
+    else  
+    {  
+        return QString("");  
+    }  
+    #endif  
+}  
+
+

这里以文件上传为例,在上传过程中,涉及到路径的代码为QURL中设置目标文件路径,假设原代码为:

+
QUrl url;
+//设置通讯协议
+url.setScheme("ftp");
+//设置用户名
+url.setUserName("user");
+//设置密码
+url.setPassword("pwd");
+//设置主机,也可以是域名
+url.setHost("192.168.1.1");
+//设置端口号,一般为21
+url.setPort(21);
+//设置路径
+QString path="\\新建文件夹\\新建文本文档.txt";
+url.setPath(path);
+
+

该代码旨在将新建文本文档.txt文件由客户端上传到服务器根目录下的子文件夹“新建文件夹”下,因为其原始文件名及目标路径都含有中文,因此直接使用以上代码会报错。修改如下:

+
QUrl url;
+//设置通讯协议
+url.setScheme("ftp");
+//设置用户名
+url.setUserName("user");
+//设置密码
+url.setPassword("pwd");
+//设置主机,也可以是域名
+url.setHost("192.168.1.1");
+//设置端口号,一般为21
+url.setPort(21);
+//设置路径
+QString path="\\新建文件夹\\新建文本文档.txt";
+url.setPath(ToSpecialEncoding(path));
+
+


+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2020/10/22/QFtp_description/index.html b/2020/10/22/QFtp_description/index.html new file mode 100644 index 0000000..bd4ceb2 --- /dev/null +++ b/2020/10/22/QFtp_description/index.html @@ -0,0 +1,466 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QFtp使用心得 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ QFtp使用心得 +

+ + +
+ + + + +
+ + +

编译时找不到.a后缀的静态库

大多数讲解QFtp配置的文章中都提到了讲pro文件中的config -= static改为config += static以生成静态库。但是需注意一点,msvc编译器与gnu编译器生产的静态库文件是不同的,msvc下的静态库文件是.lib后缀,而gnu下的静态库文件是.a后缀。

+​ + +

提示QFtp未进行login操作

ftp服务器的登录分为匿名登录和非匿名登录,匿名登录情况下,在m_ftp->connectToHost(server_name)完成之后,需调用m_ftp->login()函数进行登录操作;而非匿名情况下,在m_ftp->connectToHost(server_name)完成之后,需调用m_ftp->login(user_account,user_password)函数进行登录操作(即login操作需配置用户账号与密码)。

+

下载文件大小为0

QFtp大部分操作为异步操作,在调用m_ftp->get(server_path,m_file);后,文件其实并未开始下载,只有在QFtp::commandFinished信号中Get执行结束时,文件内容从服务器写入到了内存中,因此在对应的槽函数中需进行m_file->close();操作,同时为进行下一文件的下载,需delete m_file;等到需要时重新实例化。而文件上传的put操作与此同理,需在QFtp::commandFinished信号中Put执行结束时进行m_file的关闭及删除操作。

+

QFtp示例程序进度条异常弹出造成程序崩溃

在QFtp中,官方提供了名为“example”的示例文件,在QFtp成功配置之后,该程序可以正常运行,但是程序启动之后,进度条总会异常弹出。这是由于进度条在实例化之后默认弹出,要解决这个问题,在进度条实例化代码之后加一行:progressDialog->reset();即可;

+

QFtp示例程序乱码

原因为文件路径中含有中文字符,此问题在网上有很多叙述,而我在之前的博客QT使用FTP传输文件时的中文路径问题_皎然自若的博客-CSDN博客_ftp中文路径问题也可作为参考。

+

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2020/12/23/Ubuntu_offline_setup_gcc/index.html b/2020/12/23/Ubuntu_offline_setup_gcc/index.html new file mode 100644 index 0000000..93962dd --- /dev/null +++ b/2020/12/23/Ubuntu_offline_setup_gcc/index.html @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ubuntu离线情况下安装gcc编译器 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ Ubuntu离线情况下安装gcc编译器 +

+ + +
+ + + + +
+ + +

系统版本

(1)Ubuntu系统版本:20.04.1LTS;
(2)gcc编译器版本:gcc-9.3.0;
当前可找到的gcc最新版本为gcc-10,但是笔者在安装gcc-10版本时发现一些依赖项无法安装,因此选取了gcc-9版本。如果是在联网情况下安装,直接使用apt相关的安装指令即可。

+ + +

安装说明

安装建议在root模式下进行,因为各人的系统已安装软件不同,因此使用gcc9.3可能会造成一些依赖库降级,如果不了解的情况下,不建议降级。笔者所用的为linux虚拟机,为学习之用,近似于裸机,因此不考虑对其他软件的影响。
安装顺序不定,因为非裸机情况下,可能已安装部分支持库。笔者建议直接安装上述安装包内的gcc-9_9.3.0-10ubuntu2_amd64.deb,根据提示再去进行其他依赖库的安装。安装完成gcc之后,再安装g++-9_9.3.0-10ubuntu2_amd64.deb。
如果在安装过程中发现部分依赖库不存在,可到https://pkgs.org网站进行下载。

+

异常处理

安装完成之后,可通过gcc -v与g++ -v指令获取当前的编译器版本信息。但是笔者在安装完成之后发现,无法通过这两条指令获取版本信息,提示命令不存在。但是可通过gcc-9 -v与g++-9 -v指令获取编译器版本信息。因此需进行处理,处理方法如下(转载自https://www.zhihu.com/question/348286167/answer/838993075):
(1)依次执行下面三个命令:

+
cd //进入home目录
+touch .bash_profile //生成新文件
+vim .touch_profile //编辑'.bash_profile'文件
+
+

(2)将以下alias命令复制进 .bash_profile 文件:

+
alias gcc='gcc-9'
+alias g++='g++-9'
+
+

//切记=两边不要加空格

+

(3)然后执行如下命令:

+
source .bash_profile
+
+

然后,就可以使用gcc -v和g++ -v相关指令了。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2021/08/17/kvm_windows_vm_dump-202208171626-karlqyang/index.html b/2021/08/17/kvm_windows_vm_dump-202208171626-karlqyang/index.html new file mode 100644 index 0000000..b8bb756 --- /dev/null +++ b/2021/08/17/kvm_windows_vm_dump-202208171626-karlqyang/index.html @@ -0,0 +1,528 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kvm windows虚拟机dmp文件转储 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ kvm windows虚拟机dmp文件转储 +

+ + +
+ + + + +
+ + +

kvm下的windows虚拟机转储可从子机侧和母机侧两方面进行。

+ +

子机侧

子机侧转储windows子机信息,主要是在主机内部执行virsh inject-nmi UUID指令,通过人为模拟nmi中断实现的,之后子机触发BSOD(蓝屏),继而执行核心文件转储和重启操作。转储的文件名为MEMORY.dmp,根据设置的windows内存核心转储方式不同,该文件一般存储于c:\Windows目录或c:\Windows\Minidump目录下。

+

母机侧

qemu-monitor-command指令

virsh qemu-monitor-command domain
使用该类指令时,需安装qemu-monitor相关组件,否则无法使用相关指令

+
#故意让windows蓝屏
+virsh qemu-monitor-command instance-00000b2e --hmp  nmi
+#导出guest内存
+virsh qemu-monitor-command instance-00000b2e --hmp  dump-guest-memory  /home/qemu/instance-00000b2e.dump
+
+

dump指令

对应语法如下:

+
#virsh dump指令语法
+virsh dump domain corefilepath [--bypass-cache]
+   { [--live] | [--crash] | [--reset] }
+   [--verbose] [--memory-only] [--format string]
+
+

–live :使用该参数后,qemu进程在内核转储完成后继续运行

+

–crash :使用该参数后,qemu进程在内核转储完成后因crash事件而暂停

+

–reset:使用该参数后,qemu进程在内核转储完成后重启
若没有使用上述三个参数,则进程在内核转储完成后保持pause状态

+

-bypass-cache:使用该参数后,转储文件不会保存os文件系统缓存文件

+

–memory-only:使用该参数后,转储文件为elf格式文件,且仅包含虚拟机内存及cpu通用寄存器数值

+

–format:该参数与memory-only绑定使用,指定保存的转储文件的格式,其支持的文件格式(即string的参数值)包含elf、kdump-zlib(使用zlib压缩的kdump压缩格式)、kdump-lzo(使用lzo压缩的kdump压缩格式)、kdump-sbappy(使用sbappy压缩的kdump压缩格式)、win-dmp(windows的完全转储格式)

+

如果仅使用最简单的dump语法:virsh dump domain corefilepath,则生成的转储文件格式如下:

+
windows_dump_no-memory-only.dmp:QEMU suspend to disk image
+
+

如果使用memory-only语法,但不指定format,则默认格式如下:

+
windows_dump_memory-only.dmp:ELF 64-bit LSB core file x86-64, version 1 (SYSV)
+
+

转储文件格式转换

virsh dump指令在低版本不支持win-dmp格式,因此生成的dump文件的格式以elf文件为主,而windbg是无法直接使用并分析elf文件的,因此需进行转换,将elf文件转换为windows的dmp格式文件。

+

文件格式的转换主要使用volatilitydistorm两个python库。

+
#安装工具
+git clone https://github.com/volatilityfoundation/volatility
+python setup.py install
+#有inline失败的问题,把函数把inline变为非inline
+git clone https://github.com/gdabah/distorm
+python setup.py install
+#复制distorm目录下的_distorm3.so库到python sys.path否则报错
+cp build/lib.linux-x86_64-2.7/_distorm3.so  /usr/lib64/python2.7/
+
+#分析guest内存布局
+[root@rg1-ostack37 /home/huiwei]# file instance-00000b2e.dump
+instance-00000b2e.dump: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style
+
+[root@rg1-ostack37 /home/huiwei]# /bin/vol.py imageinfo -f instance-00000b2e.dump
+Volatility Foundation Volatility Framework 2.6.1
+INFO: volatility.debug: Determining profile based on KDBG search...
+  Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418
+ AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
+ AS Layer2 : QemuCoreDumpElf (Unnamed AS)
+ AS Layer3 : FileAddressSpace (/home/huiwei/instance-00000b2e.dump)
+  PAE type : No PAE
+   DTB : 0x187000L
+  KDBG : 0xf8000164f110L
+  Number of Processors : 2
+ Image Type (Service Pack) : 1
+KPCR for CPU 0 : 0xfffff80001650d00L
+KPCR for CPU 1 : 0xfffff880009c5000L
+ KUSER_SHARED_DATA : 0xfffff78000000000L
+   Image date and time : 2020-10-22 09:37:00 UTC+0000
+ Image local date and time : 2020-10-22 17:37:00 +0800
+#进行转换
+[root@rg1-ostack37 /home/huiwei]# /bin/vol.py -f instance-00000b2e.dump --profile Win2008R2SP1x64  raw2dmp -O   instance-00000b2e.dmp
+Volatility Foundation Volatility Framework 2.6.1
+Writing data (5.00 MB chunks): |..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................|
+[root@rg1-ostack37 /home/huiwei]# file instance-00000b2e.dmp
+instance-00000b2e.dmp: MS Windows 64bit crash dump, full dump, 1310720 pages
+
+

windows目标文件的格式需选择,可通过/bin/vol.py -f XXX.dump –profile相关指令获取本机相关库支持的windows格式进行参数填写。

+

经过上述处理之后,即可使用windbg工具对相关dmp文件进行分析,以查找系统异常原因。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2021/11/09/virtio-202111091100/index.html b/2021/11/09/virtio-202111091100/index.html new file mode 100644 index 0000000..4cb3401 --- /dev/null +++ b/2021/11/09/virtio-202111091100/index.html @@ -0,0 +1,982 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Virtio-设备模拟详解 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ Virtio-设备模拟详解 +

+ + +
+ + + + +
+ + +

什么是virtio

virtio 是一种 I/O 半虚拟化解决方案,是一套通用 I/O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备的抽象。提供了一套上层应用与各 Hypervisor 虚拟化设备(KVM,Xen,VMware等)之间的通信框架和编程接口,减少跨平台所带来的兼容性问题,大大提高驱动程序开发效率。
virtio 协议定义了各类设备与驱动,定义了它们如何初始化,如何通信,如何通知等。其中,最核心的是设备与驱动的通信机制,避免了每次访问外设寄存器都要 vm_exit/vm_enter 的问题。

+ +

Virtio架构

从总体上看,virtio 可以分为四层,包括前端 guest 中各种驱动程序模块,后端 Hypervisor (实现在Qemu上)上的处理程序模块,中间用于前后端通信的 virtio 层和 virtio-ring 层,virtio 这一层实现的是虚拟队列接口,算是前后端通信的桥梁,而 virtio-ring 则是该桥梁的具体实现,它实现了两个环形缓冲区,分别用于保存前端驱动程序和后端处理程序执行的信息。

+

严格来说,virtio 和 virtio-ring 可以看做是一层,virtio-ring 实现了 virtio 的具体通信机制和数据流程。或者这么理解可能更好,virtio 层属于控制层,负责前后端之间的通知机制(kick,notify)和控制流程,而 virtio-vring 则负责具体数据流转发。

+

vring共享内存基本原理

virtio vring 本质是共享内存,要求使用共享内存的软件模块可以访问这段内存。在虚拟化场景,guest/host 如何实现共享内存呢?

+

第一个问题:vring 描述符中存放的内存地址是什么?

+

vring 由 guest 驱动申请,所以 vring 描述符内存放的地址是 GPA。

+

第二个问题:guest/host 如何实现共享?

+

总体看有三种情况:

+
    +
  1. 通过 qemu 模拟的设备,GPA 位于 qemu 的进程地址空间,qemu 天然可以访问。
  2. +
  3. qemu 外部模拟的设备,比如 vhost-net/vhost-user,需要建立新的内存映射。
  4. +
  5. 对于一个真实的硬件设备,需要使用 IOMMU 辅助完成地址转换。
  6. +
+

以 vhost-net 为例简要说明:

+

(1)初始化过程中,qemu 通过 ioctl 命令字将 vring 的内存信息通知 vhost-net 内核模块。内存信息包括:GPA/userspace_addr/size 等。

+

(2)vhost-net 内核模块会记录 GPA 与 userspace_addr(qemu 进程上下文虚拟地址) 的内存映射。

+

(3)vhost-net 内核模块在启动内核线程时记录此线程为哪个 qemu 虚拟机服务,同时记录 qemu 虚拟机进程的页表信息,在内核线程运行时,使用对应的 qemu 虚拟机进程页表。这样 vhost-net 内核模块就可以访问 qemu 进程上下文的虚拟地址。

+

PCI设备概述

PCI即Peripheral Component Interconnect,中文意思是“外围器件互联”,是由PCISIG (PCI Special Interest Group)推出的一种局部并行总线标准。PCI总线是由ISA(Industy Standard Architecture)总线发展而来的,是一种同步的独立于处理器的32位或64位局部总线。从结构上看,PCI是在CPU的供应商和原来的系统总线之间插入的一级总线,具体由一个桥接电路实现对这一层的管理,并实现上下之间的接口以协调数据的传送。

+

PCI总线是一种共享总线,所以需要特定的仲裁器(Arbiter)来决定当前时刻的总线的控制权。一般该仲裁器位于北桥中,而仲裁器(主机)则通过一对引脚,REQ#(request) 和GNT# (grant)来与各个从机连接。
CPU可以直接通过load/store指令来访问PCI设备,PCI设备有如下三种不同内存:

+
    +
  • MMIO
  • +
  • PCI IO space
  • +
  • PCI configuration space
  • +
+

guest前端驱动程序操作接口

驱动程序对PCI 配置的操作可以分成以下几个部分:

+

读写 feature bits
定义了 Guest 和 Host 支持的功能,例如 VIRTIO_NET_F_CSUM bit 表示网络设备是否支持 checksum offload。feature bits 机制提供了未来扩充功能的灵活性,以及兼容旧设备的能力。

+

读写配置空间

一般通过一个数据结构和一个虚拟设备关联,Guest 可以读写此空间。

+

读写 status bits

这是一个8bits的长度,Guest用来标识device probe的状态,当 VIRIO_CONFIG_S_DRIVE_OK被设置,那么Guest已经完成了feature协商,可以跟host进行数据交互了。

+

Device reset

重置设备,配置status bits。

+

Virtqueue的创建和销毁

提供了分配virtqueue内存和Host的IO空间的初始化操作。

+

对应的代码如下:

+
//代码路径virtio-win/VirtIO/windows/VirtIOPCIModern.c
+static const struct virtio_device_ops virtio_pci_device_ops = {
+.get_config = vio_modern_get_config,
+.set_config = vio_modern_set_config,
+.get_config_generation = vio_modern_get_generation,
+.get_status = vio_modern_get_status,
+.set_status = vio_modern_set_status,
+.reset = vio_modern_reset,
+.get_features = vio_modern_get_features,
+.set_features = vio_modern_set_features,
+.set_config_vector = vio_modern_set_config_vector,
+.set_queue_vector = vio_modern_set_queue_vector,
+.query_queue_alloc = vio_modern_query_vq_alloc,
+.setup_queue = vio_modern_setup_vq,
+.delete_queue = vio_modern_del_vq,
+};
+//代码路径virtio-win/virtio/virtio_pci.h
+struct virtio_device_ops
+{
+// read/write device config and read config generation counter
+void (*get_config)(VirtIODevice *vdev, unsigned offset, void *buf, unsigned len);
+void (*set_config)(VirtIODevice *vdev, unsigned offset, const void *buf, unsigned len);
+u32 (*get_config_generation)(VirtIODevice *vdev);
+// read/write device status byte and reset the device
+u8 (*get_status)(VirtIODevice *vdev);
+void (*set_status)(VirtIODevice *vdev, u8 status);
+void (*reset)(VirtIODevice *vdev);
+// get/set device feature bits
+u64 (*get_features)(VirtIODevice *vdev);
+NTSTATUS (*set_features)(VirtIODevice *vdev, u64 features);
+// set config/queue MSI interrupt vector, returns the new vector
+u16 (*set_config_vector)(VirtIODevice *vdev, u16 vector);
+u16 (*set_queue_vector)(struct virtqueue *vq, u16 vector);
+// query virtual queue size and memory requirements
+NTSTATUS (*query_queue_alloc)(VirtIODevice *vdev,
+unsigned index, unsigned short *pNumEntries,
+unsigned long *pRingSize,
+unsigned long *pHeapSize);
+// allocate and initialize a queue
+NTSTATUS (*setup_queue)(struct virtqueue **queue,
+VirtIODevice *vdev, VirtIOQueueInfo *info,
+unsigned idx, u16 msix_vec);
+// tear down and deallocate a queue
+void (*delete_queue)(VirtIOQueueInfo *info);
+};
+
+

virtio 数据流交互机制:virtqueue

Virtio 使用 virtqueue 来实现 I/O 机制,每个 virtqueue 就是一个承载大量数据的队列,具体使用多少个队列取决于需求,例如,virtio 网络驱动程序(virtio-net)使用两个队列(一个用于接受,另一个用于发送),而 virtio 块驱动程序(virtio-blk)仅使用一个队列。

+
//VirtIO.h
+struct virtqueue {
+VirtIODevice *vdev;
+struct vring vring;
+struct {
+u16 flags;
+u16 idx;
+} master_vring_avail;
+unsigned int index;
+unsigned int num_unused;
+unsigned int num_added_since_kick;
+u16 first_unused;
+u16 last_used;
+void *notification_addr;
+void (*notification_cb)(struct virtqueue *vq);
+void *opaque[];
+};
+
+

针对Virtqueue的具体操作包含:

+

1.向queue中添加一个新的buffer,opaque为一个非空的令牌,用于识别buffer,当buffer内容被消耗后,opaque会返回。

+
//VirtIORing.c
+int virtqueue_add_buf(
+struct virtqueue *vq,/* 虚拟化队列 */
+struct scatterlist sg[],   /* 缓存区描述符数组,长度为驱动程序->设备缓冲区描述符(in)+设备->驱动程序缓冲区描述符(out)*/
+unsigned int out,/* sg中驱动程序到设备缓冲区描述符数量 */
+unsigned int in, /* sg中设备到驱动程序缓冲区描述符数量 */
+void *opaque,/* virtqueue_get_buf 函数返回值(used ring缓冲区起始描述符指针)*/
+void *va_indirect,   /* 间接页的虚拟地址或空指针*/
+ULONGLONG phys_indirect) /*间接页的物理地址或空指针*/
+
+

2.Guest 通知 host 单个或者多个 buffer 已经添加到 queue 中,调用 virtqueue_notify(),notify 函数会向 queue notify(VIRTIO_PCI_QUEUE_NOTIFY)寄存器写入 queue index 来通知 host。

+
 //VirtIOPCICommon.c
+void virtqueue_kick(struct virtqueue *vq)
+{
+if (virtqueue_kick_prepare(vq)) {
+virtqueue_notify(vq);
+}
+}
+
+

3.返回使用过的 buffer,len 为写入到 buffer 中数据的长度。获取数据,释放 buffer,更新 vring 描述符表格中的 index。

+
//VirtIORing.c
+void *virtqueue_get_buf(
+struct virtqueue *vq, /* the queue */
+unsigned int *len)/* number of bytes returned by the device */
+
+

4.示意 guest 不再需要再知道一个 buffer 已经使用了,也就是关闭 device 的中断。驱动会在初始化时注册一个回调函数,disable_cb()通常在这个 virtqueue 回调函数中使用,用于关闭再次的回调发生。

+
//VirtIORing.c
+void virtqueue_disable_cb(struct virtqueue *vq)
+
+

5.与 disable_cb()刚好相反,用于重新开启设备中断的上报。

+
//VirtIORing.c
+bool virtqueue_enable_cb(struct virtqueue *vq) 
+
+

virtio 的核心机制就是通过共享内存在前端驱动与后端实现间进行数据传输,共享内存区域被称作 vring。

+

virtio 传输机制:vring的构成与实现

vring 是 virtio 传输机制的实现,vring 引入 ring buffers 来作为数据传输的载体,包含三个部分:

+
// virtio_ring.h
+struct vring {
+unsigned int num;
+struct vring_desc *desc;
+struct vring_avail *avail;
+struct vring_used *used;
+};
+
+

Descriptor Table: 描述内存 buffer,主要包括 addr/len 等信息。

+
// virtio_ring.h
+/* This marks a buffer as continuing via the next field. */
+#define VIRTQ_DESC_F_NEXT	1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VIRTQ_DESC_F_WRITE	2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VIRTQ_DESC_F_INDIRECT	4
+/* Virtio ring: 16 bytes.  These can chain together via "next". */
+struct vring_desc {
+/* Address (guest-physical). */
+__virtio64 addr;
+/* Length. */
+__virtio32 len;
+/* The flags as indicated above. */
+__virtio16 flags;
+/* We chain unused descriptors via this, too */
+__virtio16 next;
+};
+
+

Available Ring: 用于驱动通知设备有新的可用的描述符。比如,通知后端设备,有一个待发送的报文描述符。

+

注意:驱动提供了新的可用描述符后,设备侧不一定要立即使用,比如 virtio-net 会提供一些描述符用于报文接收,当报文到达后按需使用这些描述符即可。

+
// virtio_ring.h
+#define VIRTQ_AVAIL_F_NO_INTERRUPT	1
+struct vring_avail {
+    //控制信息,比如 VIRTQ_AVAIL_F_NO_INTERRUPT 表示驱动侧不想接收通知
+__virtio16 flags;
+//idx:驱动将把下一个描述符放在哪里,即 ring 数组的下标
+__virtio16 idx;
+    //ring[]:avail 描述符在 Descriptor Table 中的 id
+__virtio16 ring[];
+};
+
+

Used Ring: 用于通知驱动设备侧已用的描述符。比如,后端设备收到一个报文,需要将报文数据放入可用的描述符,并更新Used Ring,同时通知前端驱动。

+
// virtio_ring.h
+#define VIRTQ_USED_F_NO_NOTIFY	1
+struct vring_used_elem {
+/* Index of start of used descriptor chain. */
+__virtio32 id;
+/* Total length of the descriptor chain which was used (written to) */
+__virtio32 len;
+};
+
+struct vring_used {
+__virtio16 flags;
+__virtio16 idx;
+struct vring_used_elem ring[];
+};
+
+

注意:相比 avail ring 结构多了 len 字段,用于表示设备侧写入的数据长度。对于只读数据类型,不改变 len 长度。

+

vring 主要通过两个环形缓冲区来完成数据流的转发。

+

当 guest 向 virtqueue 中写数据时,实际上是向 desc 结构指向的 buffer 中填充数据,完了会更新 available ring,然后再通知 host。
当 host 收到接收数据的通知时,首先从 desc 指向的 buffer 中找到 available ring 中添加的 buffer,映射内存,同时更新 used ring,并通知 guest 接收数据完毕。

+

qemu后端处理模块

下面以Virtio Network Device设备的初始化为例对qemu中virtio的实现进行说明。

+

预定义结构体

//代码路径:QEMU/qom/object.c
+static TypeInfo object_info = {
+.name = TYPE_OBJECT,
+.instance_size = sizeof(Object),
+.instance_init = object_instance_init,
+.abstract = true,
+};
+//代码路径:QEMU/hw/core/qdev.c
+static const TypeInfo device_type_info = {
+.name = TYPE_DEVICE,
+.parent = TYPE_OBJECT,
+.instance_size = sizeof(DeviceState),
+.instance_init = device_initfn,
+.instance_post_init = device_post_init,
+.instance_finalize = device_finalize,
+.class_base_init = device_class_base_init,
+.class_init = device_class_init,
+.abstract = true,
+.class_size = sizeof(DeviceClass),
+};
+//代码路径:QEMU/hw/virtio/virtio.c
+static const TypeInfo virtio_device_info = {
+.name = TYPE_VIRTIO_DEVICE,
+.parent = TYPE_DEVICE,
+.instance_size = sizeof(VirtIODevice),
+.class_init = virtio_device_class_init,
+.instance_finalize = virtio_device_instance_finalize,
+.abstract = true,
+.class_size = sizeof(VirtioDeviceClass),
+};
+//代码路径:QEMU/hw/net/virtio-net.c
+static const TypeInfo virtio_net_info = {
+.name = TYPE_VIRTIO_NET,
+.parent = TYPE_VIRTIO_DEVICE,
+.instance_size = sizeof(VirtIONet),
+.instance_init = virtio_net_instance_init,
+.class_init = virtio_net_class_init,
+};
+static void virtio_register_types(void)
+{
+type_register_static(&virtio_net_info);
+}
+type_init(virtio_register_types)
+
+

Virtio Network Device这种类的定义是有多层继承关系的,TYPE_VIRTIO_NET的父类是TYPE_VIRTIO_DEVICE,TYPE_VIRTIO_DEVICE的父类是TYPE_DEVICE,TYPE_DEVICE的父类是TYPE_OBJECT,继承关系就到头了。type_init用于注册这种类,这里面每一层都有class_init,用于从TypeImpl生成xxxClass,也有instance_init,会将xxxClass初始化为实例。

+

创建VirtQueue

TYPE_VIRTIO_NET层的class_init函数是virtio_net_class_init,它定义了DeviceClass的realize函数为virtio_net_device_realize,如下所示:

+
//代码路径:QEMU/hw/net/virtio-net.c
+static void virtio_net_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+dc->props = virtio_net_properties;
+set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+vdc->realize = virtio_net_device_realize;
+vdc->unrealize = virtio_net_device_unrealize;
+vdc->get_config = virtio_net_get_config;
+vdc->set_config = virtio_net_set_config;
+vdc->get_features = virtio_net_get_features;
+vdc->set_features = virtio_net_set_features;
+vdc->bad_features = virtio_net_bad_features;
+vdc->reset = virtio_net_reset;
+vdc->set_status = virtio_net_set_status;
+vdc->preset_dma_map = virtio_net_preset_dma_map;
+vdc->set_host_notifier = virtio_net_set_host_notifier;
+vdc->unset_host_notifier = virtio_net_unset_host_notifier;
+vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
+vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
+vdc->load = virtio_net_load_device;
+vdc->save = virtio_net_save_device;
+}
+static void virtio_net_device_realize(DeviceState *dev, Error **errp)
+{
+VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+VirtIONet *n = VIRTIO_NET(dev);
+NetClientState *nc;
+int i;
+         … …
+n->max_queues = MAX(n->nic_conf.peers.queues, 1);
+… …
+for (i = 0; i < n->max_queues; i++) {
+virtio_net_add_queue(n, i);
+}
+n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
+qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
+memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
+n->status = VIRTIO_NET_S_LINK_UP;
+n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_net_announce_timer, n);
+if (n->netclient_type) {
+n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
+  n->netclient_type, n->netclient_name, n);
+} else {
+n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
+  object_get_typename(OBJECT(dev)), dev->id, n);
+}
+… …
+}
+
+

上述代码创建了一个VirtIODevice,而virtio_init用来初始化这个设备。VirtIODevice结构里面有一个VirtQueue数组,这就是virtio前端和后端互相传数据的队列,最多有VIRTIO_QUEUE_MAX(1024)个。

+

但是net设备也有与其他设备不一样的地方,即代码中存在这样的语句n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX。为什么要乘以2呢?这是因为对于网络设备来讲,应该分发送队列和接收队列两个方向。

+

VirtQueue队列初始化

接下来调用virtio_net_add_queue来初始化队列,可以看出这里面就有发送tx_vq和接收rx_vq两个队列,如下所示:

+
//代码路径:QEMU/hw/net/virtio-net.c
+static void virtio_net_add_queue(VirtIONet *n, int index)
+{
+VirtIODevice *vdev = VIRTIO_DEVICE(n);
+n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
+   virtio_net_handle_rx);
+if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
+n->vqs[index].tx_vq =
+virtio_add_queue(vdev, n->net_conf.tx_queue_size,
+ virtio_net_handle_tx_timer);
+n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+  virtio_net_tx_timer,
+  &n->vqs[index]);
+} else {
+n->vqs[index].tx_vq =
+virtio_add_queue(vdev, n->net_conf.tx_queue_size,
+ virtio_net_handle_tx_bh);
+n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
+}
+n->vqs[index].tx_waiting = 0;
+n->vqs[index].n = n;
+}
+
+

每个VirtQueue中,都有一个vring用来维护这个队列里面的数据;另外还有函数virtio_net_handle_rx用于处理网络包的接收;函数virtio_net_handle_tx_bh用于网络包的发送。

+

创建虚拟机网卡

qemu_new_nic会创建一个虚拟机里面的网卡,如下所示:

+
//代码路径:QEMU/net/net.c
+NICState *qemu_new_nic(NetClientInfo *info,
+   NICConf *conf,
+   const char *model,
+   const char *name,
+   void *opaque)
+{
+NetClientState **peers = conf->peers.ncs;
+NICState *nic;
+int i, queues = MAX(1, conf->peers.queues);
+assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
+assert(info->size >= sizeof(NICState));
+nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
+nic->ncs = (void *)nic + info->size;
+nic->conf = conf;
+nic->opaque = opaque;
+for (i = 0; i < queues; i++) {
+qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,
+  NULL);
+nic->ncs[i].queue_index = i;
+}
+return nic;
+}
+static void qemu_net_client_setup(NetClientState *nc,
+  NetClientInfo *info,
+  NetClientState *peer,
+  const char *model,
+  const char *name,
+  NetClientDestructor *destructor)
+{
+nc->info = info;
+nc->model = g_strdup(model);
+if (name) {
+nc->name = g_strdup(name);
+} else {
+nc->name = assign_name(nc, model);
+}
+if (peer) {
+assert(!peer->peer);
+nc->peer = peer;
+peer->peer = nc;
+}
+QTAILQ_INSERT_TAIL(&net_clients, nc, next);
+nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc);
+nc->destructor = destructor;
+QTAILQ_INIT(&nc->filters);
+}
+
+

kernel内核驱动程序

在虚拟机里面的进程发送一个网络包,通过文件系统和Socket调用网络协议栈到达网络设备层,只不过这个不是普通的网络设备,而是virtio_net的驱动。virtio_net的驱动程序代码在Linux操作系统的源代码里面,文件名为linux/drivers/net/virtio_net.c,如下所示:

+

预定义结构体

static struct virtio_driver virtio_net_driver = {
+    .feature_table = features,
+    .feature_table_size = ARRAY_SIZE(features),
+    .driver.name =	KBUILD_MODNAME,
+    .driver.owner =	THIS_MODULE,
+    .id_table =	id_table,
+    .probe =	virtnet_probe,
+    .remove =	virtnet_remove,
+    .config_changed = virtnet_config_changed,
+#ifdef CONFIG_PM_SLEEP
+    .freeze =	virtnet_freeze,
+    .restore =	virtnet_restore,
+#endif
+};
+module_virtio_driver(virtio_net_driver);
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio network driver");
+MODULE_LICENSE("GPL");
+
+

模块初始化

在virtio_net的驱动程序的初始化代码中,需要注册一个驱动函数virtio_net_driver。当一个设备驱动作为一个内核模块被初始化的时候,probe函数会被调用,因而来看一下virtnet_probe:

+
static int virtnet_probe(struct virtio_device *vdev)
+{
+    int i, err;
+    struct net_device *dev;
+    struct virtnet_info *vi;
+    u16 max_queue_pairs;
+    int mtu;
+    … …
+    dev = alloc_etherdev_mq(sizeof(struct virtnet_info), max_queue_pairs);
+    if (!dev)
+        return -ENOMEM;
+    /* Set up network device as normal. */
+    dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
+    dev->netdev_ops = &virtnet_netdev;
+    dev->features = NETIF_F_HIGHDMA;
+    SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops);
+    SET_NETDEV_DEV(dev, &vdev->dev);
+    … …
+    /* Set up our device-specific information */
+    vi = netdev_priv(dev);
+    vi->dev = dev;
+    vi->vdev = vdev;
+    vdev->priv = vi;
+    vi->stats = alloc_percpu(struct virtnet_stats);
+    err = -ENOMEM;
+    … …
+    err = init_vqs(vi);
+    if (err)
+        goto free_index;
+    netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs);
+    netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs);
+    virtnet_init_settings(dev);
+    err = register_netdev(dev);
+    if (err) {
+        pr_debug("virtio_net: registering device failed\n");
+        goto free_vqs;
+    }
+    virtio_device_ready(vdev);
+    … …
+    virtnet_set_queues(vi, vi->curr_queue_pairs);
+    … …
+}
+
+

在virtnet_probe中会创建struct net_device,并且通过register_netdev注册这个网络设备,这样在客户机里面就能看到这个网卡了。

+

初始化virtqueue

在virtnet_probe中,还有一件重要的事情就是,init_vqs会初始化发送和接收的virtqueue,如下所示:

+
static int init_vqs(struct virtnet_info *vi)
+{
+    int ret;
+    /* Allocate send & receive queues */
+    ret = virtnet_alloc_queues(vi);
+    if (ret)
+        goto err;
+    ret = virtnet_find_vqs(vi);
+    if (ret)
+        goto err_free;
+    get_online_cpus();
+    virtnet_set_affinity(vi);
+    put_online_cpus();
+    return 0;
+err_free:
+    virtnet_free_queues(vi);
+err:
+    return ret;
+}
+
+

Virtqueue实体查找

按照之前的virtio原理,virtqueue是一个介于客户机前端和qemu后端的一个结构,用于在这两端之间传递数据,对于网络设备来讲有发送和接收两个方向的队列。这里建立的struct virtqueue是客户机前端对于队列管理的数据结构。队列的实体需要通过函数virtnet_find_vqs查找或者生成,这里还会指定接收队列的callback函数为skb_recv_done,发送队列的callback函数为skb_xmit_done。当buffer使用发生变化的时候,可以调用这个callback函数进行通知,如下所示:

+
static int virtnet_find_vqs(struct virtnet_info *vi)
+{
+    vq_callback_t **callbacks;
+    struct virtqueue **vqs;
+    int ret = -ENOMEM;
+    int i, total_vqs;
+    const char **names;
+    /* We expect 1 RX virtqueue followed by 1 TX virtqueue, followed by
+     * possible N-1 RX/TX queue pairs used in multiqueue mode, followed by
+     * possible control vq.
+     */
+    total_vqs = vi->max_queue_pairs * 2 +
+        virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ);
+    /* Allocate space for find_vqs parameters */
+    vqs = kzalloc(total_vqs * sizeof(*vqs), GFP_KERNEL);
+    if (!vqs)
+        goto err_vq;
+    callbacks = kmalloc(total_vqs * sizeof(*callbacks), GFP_KERNEL);
+    if (!callbacks)
+        goto err_callback;
+    names = kmalloc(total_vqs * sizeof(*names), GFP_KERNEL);
+    if (!names)
+        goto err_names;
+    /* Parameters for control virtqueue, if any */
+    if (vi->has_cvq) {
+        callbacks[total_vqs - 1] = NULL;
+        names[total_vqs - 1] = "control";
+    }
+    /* Allocate/initialize parameters for send/receive virtqueues */
+    for (i = 0; i < vi->max_queue_pairs; i++) {
+        callbacks[rxq2vq(i)] = skb_recv_done;
+        callbacks[txq2vq(i)] = skb_xmit_done;
+        sprintf(vi->rq[i].name, "input.%d", i);
+        sprintf(vi->sq[i].name, "output.%d", i);
+        names[rxq2vq(i)] = vi->rq[i].name;
+        names[txq2vq(i)] = vi->sq[i].name;
+    }
+    ret = vi->vdev->config->find_vqs(vi->vdev, total_vqs, vqs, callbacks,
+                     names);
+    if (ret)
+        goto err_find;
+    if (vi->has_cvq) {
+        vi->cvq = vqs[total_vqs - 1];
+        if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
+            vi->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+    }
+    for (i = 0; i < vi->max_queue_pairs; i++) {
+        vi->rq[i].vq = vqs[rxq2vq(i)];
+        vi->sq[i].vq = vqs[txq2vq(i)];
+    }
+    kfree(names);
+    kfree(callbacks);
+    kfree(vqs);
+    return 0;
+err_find:
+    kfree(names);
+err_names:
+    kfree(callbacks);
+err_callback:
+    kfree(vqs);
+err_vq:
+    return ret;
+}
+
+

这里的find_vqs是在struct virtnet_info里的struct virtio_device里的struct virtio_config_ops *config里面定义的。

+

根据virtio_config_ops的定义,find_vqs会调用vp_modern_find_vqs。在vp_modern_find_vqs 中,vp_find_vqs会调用vp_find_vqs_intx。在vp_find_vqs_intx 中,通过request_irq注册一个中断处理函数vp_interrupt,当设备向队列中写入信息时会产生一个中断,也就是vq中断。中断处理函数需要调用相应队列的回调函数,然后根据队列的数目,依次调用vp_setup_vq完成virtqueue、vring的分配和初始化。

+

同样,这些数据结构会和virtio后端的VirtIODevice、VirtQueue、vring对应起来,都应该指向刚才创建的那一段内存。客户机同样会通过调用专门给外部设备发送指令的函数iowrite告诉外部的pci设备,这些共享内存的地址。至此前端设备驱动和后端设备驱动之间的两个收发队列就关联好了。

+

总结

virtio 是 guest 与 host 之间通信的润滑剂,提供了一套通用框架和标准接口或协议来完成两者之间的交互过程,极大地解决了各种驱动程序和不同虚拟化解决方案之间的适配问题。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/08/18/who_am_i-202208181900/index.html b/2022/08/18/who_am_i-202208181900/index.html new file mode 100644 index 0000000..82240f1 --- /dev/null +++ b/2022/08/18/who_am_i-202208181900/index.html @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 哲学三思之一:我是谁 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 哲学三思之一:我是谁 +

+ + +
+ + + + +
+ + +

作为一个理科生,我其实并不是十分喜欢哲学,对于一个无法证实或证伪的学科,实在是觉得谈哲学更近似于谈玄,打嘴炮为主。但是在社会生活中,哲学的诸多理论却实在的影响着人类生活的方方面面。尤其是终极三问,我是谁、我从哪里来、我到哪里去是很多人都十分感兴趣的话题,我也不例外。

+ +

对于宗教教徒来说,我是谁这个问题就简单了很多,我是神的选民或神的子民,既增加了教众的全体认同感,也使得教众多了一份相对于异教徒或无信者的优越感。我不是信徒,因此不能单纯的以神的选民的身份来搪塞自己,因此就必须实实在在的思考这个问题。

+

对于人而言,人的自我认同部分集中于其社会关系上,家人、朋友、同事等种种的社会关系塑造了人的外在形象,别人根据这个形象相互交往,而人自身往往也在主动或被动的维护着这个形象。但是面具戴久了,往往很难摘下来,不知不觉中,很多人产生了我为别人活的想法,认为自己主要在维护着一个光鲜的外部形象,却从没有为自己而活。我却不同意这个想法,维护外在形象固然有社会形象的影响,但更多的还是人自己的选择,因为外在形象获得社会的认同,如果不是自身乐在其中,又怎么会数十年不厌其烦的扮演呢。而后面的抱怨,也无非是生活或工作遇到了挫折,对于维护外在形象的动力减弱,但是如果再次有正反馈传来,很多人还是乐此不疲。

+

但是,哲学考虑的更加深入,外部的社会关系其实是一个可以重复的条件,换言之,如果另一个人拥有和自己相同的外部社会关系,那是不是就说明别人也可以成为自己,自己并不是独一无二的。站在科学的角度,如果我们可以精确的定义人及其生活环境的一切变量,那我们自然可以说可以塑造出一个人出来,把自己作为独一无二是一种傲慢,把自己作为不可复制也是一种傲慢。但是事实是在可见的未来,我们还没有办法算尽一切,因此我们也许可以制造出生物意义上的人,但这个人却无法代替你,即他不是你。如果真的有一天存在那样的高位文明可以复制你的一切,但是他们这么做有什么意义呢?最恶劣的猜想,你对于他们来说只是一个小白鼠,用于满足他们对于人这个物种的研究之用,那又如何?人的强大不是单体的强大,而是人组成的社会或文明总体的强大,一时的强弱并不重要,只要有前进的决心与努力,文明就会精彩,人生也是如此。

+

所以,与其思考我是谁这样的哲学命题,倒不如抓紧时间做事,做事就是做人,保持前进的欲望,走弯路也强过原地踏步。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/08/20/domain_xml_format_general-metadata/index.html b/2022/08/20/domain_xml_format_general-metadata/index.html new file mode 100644 index 0000000..54130fd --- /dev/null +++ b/2022/08/20/domain_xml_format_general-metadata/index.html @@ -0,0 +1,495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(1)General metadata | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(1)General metadata +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+

所有的虚拟机都需要的根元素是domain。其包含两个属性项,typo属性项指明了被用来运行虚拟机的虚拟机管理程序(hypervisor)。该项允许的数值与驱动相关,包含了xen、kvm、hvf(从8.1.0和QEMU 2.12版本起)、qemu和lxc。第二个属性项是id,代表运行客户机(guest)的一个唯一数字标识符。不活跃的客户机没有id。

+ + +
<domain type='kvm' id='1'>
+  <name>MyGuest</name>
+  <uuid>4dea22b3-1d52-d8f3-2516-782e98ab3fa0</uuid>
+  <genid>43dc0cf8-809b-4adb-9bea-a9abb5f3d90e</genid>
+  <title>A short description - title - of the domain</title>
+  <description>Some human readable description</description>
+  <metadata>
+    <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>
+    <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>
+  </metadata>
+  ...
+
+

name名称

name元素的内容为虚拟机提供了一个小名。这个名称应该仅由字母或数字字符组成,同时在母机(host)范围内该名称必须是唯一的。该名称经常被用来生成文件以储存持久化配置文件。

+

uuid统一唯一标识符

uuid属性项的内容为虚拟机提供了一个全局唯一标识符。uuid的格式必须符合RFC 4122标准,例如3e3fce45-4f53-4fa7-bb32-11f34168b82b。该属性项可以省略,那么当定义define或创建一个新的虚拟机时,会生成一个随机的uuid。也可以通过
SMBIOS System Information规范提供uuid。(uuid从libvirt 0.0.1版本起提供支持,sysinfo从libvirt 0.8.7版本起提供支持)

+

genid代际id

从libvirt 4.4.0版本开始,genid属性项使用与uuid相同格式的一个128位、随机加密的整数数值标识符代表全局唯一标识符(Globally Unique Identifier,即GUID),为虚拟机增加一个代际id。当虚拟机重复运行之前已经运行的某些操作时,该值用来帮助通知客户机。例如:

+
    +
  • 虚拟机开始执行一个快照
  • +
  • 虚拟机从备份中恢复
  • +
  • 虚拟机陷入到故障恢复操作中
  • +
  • 虚拟机正在导入import、拷贝copy或克隆clone
  • +
+

客户机操作系统注意到了这些变化,通过标记分布式数据库的拷贝为脏数据、重新初始化它的随机数生成器等操作做出正常的反应。

+

libvirt xml解析器既可以接受给定的GUID数值,也可以未配置的情况,这时会生成一个GUID并保存在xml文件中。对于上述的过渡性操作,libvirt将会在重新运行之前改变GUID。

+

title标题

title是可选配置项,为domain提供了一个简短的描述空间。title中不应该包含任何换行。从libvirt 0.9.10版本开始提供对title的支持。

+

description描述

description属性项为虚拟机提供了一个人类易读的描述。该数据不会被libvirt使用,但是却包含用户想要的所有内容。从libvirt 0.7.2版本开始提供支持。

+

metadata元数据

metadata节点可以被应用程序以xml节点(node)/树(tree)的形式用来存储特定数据。应用程序必须使用xml节点(node)/树(tree)中的特定命名空间(namespace),每一个命名空间只有一个顶层(top-level)元素(如果应用程序需要使用结构体,则在其命名空间元素下存在相应子元素)。从libvirt 0.9.10版本开始提供支持。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/08/31/domain_xml_format_Operating-system-booting/index.html b/2022/08/31/domain_xml_format_Operating-system-booting/index.html new file mode 100644 index 0000000..42a4a9d --- /dev/null +++ b/2022/08/31/domain_xml_format_Operating-system-booting/index.html @@ -0,0 +1,647 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(2)Operating system booting | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(2)Operating system booting +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+

这里有很多不同的方法用来引导操作系统开机,每一个都有优点与缺点。

+ + +

BIOS bootloader

支持全虚拟化(full virtualization)的虚拟机管理程序可以通过BIOS引导开机。在这种情况下,BIOS存在开机顺序优先级(软盘floppy、硬盘harddisk、光盘cdrom、网络network)决定到哪里去获取/查找开机镜像(boot image)。

+
<!-- Xen with fullvirt loader -->
+<!-- 使用全虚拟化加载器的Xen-->
+...
+<os>
+  <type>hvm</type>
+  <loader>/usr/lib/xen/boot/hvmloader</loader>
+  <boot dev='hd'/>
+</os>
+...
+
+<!-- QEMU with default firmware, serial console and SMBIOS -->
+<!-- 使用默认的硬件、串行控制台和SMBIOS的QEMU-->
+...
+<os>
+  <type>hvm</type>
+  <boot dev='cdrom'/>
+  <bootmenu enable='yes' timeout='3000'/>
+  <smbios mode='sysinfo'/>
+  <bios useserial='yes' rebootTimeout='0'/>
+</os>
+...
+
+<!-- QEMU with UEFI manual firmware and secure boot -->
+<!-- 使用UEFI手动固件和安全启动的QEMU-->
+...
+<os>
+  <type>hvm</type>
+  <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+  <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/var/lib/libvirt/nvram/guest_VARS.fd</nvram>
+  <boot dev='hd'/>
+</os>
+...
+
+<!-- QEMU with UEFI manual firmware, secure boot and with NVRAM type 'file'-->
+<!-- 使用UEFI手动固件、安全启动和NVRAM类型文件的QEMU-->
+...
+<os>
+  <type>hvm</type>
+  <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+  <nvram type='file' template='/usr/share/OVMF/OVMF_VARS.fd'>
+    <source file='/var/lib/libvirt/nvram/guest_VARS.fd'/>
+  </nvram>
+  <boot dev='hd'/>
+</os>
+...
+
+<!-- QEMU with UEFI manual firmware, secure boot and with network backed NVRAM'-->
+<!-- 使用UEFI手动固件、安全启动和网络支持的NVRAM的QEMU-->
+...
+<os>
+  <type>hvm</type>
+  <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+  <nvram type='network'>
+    <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
+      <host name='example.com' port='6000'/>
+      <auth username='myname'>
+        <secret type='iscsi' usage='mycluster_myname'/>
+      </auth>
+    </source>
+  </nvram>
+  <boot dev='hd'/>
+</os>
+...
+
+<!-- QEMU with automatic UEFI firmware and secure boot -->
+<!-- 使用自动UEFI固件和安全启动的QEMU-->
+...
+<os firmware='efi'>
+  <type>hvm</type>
+  <loader secure='yes'/>
+  <boot dev='hd'/>
+</os>
+...
+
+<!-- QEMU with automatic UEFI stateless firmware for AMD SEV -->
+<!-- 使用适用于AMD SEV的自动UEFI无状态固件的QEMU-->
+...
+<os firmware='efi'>
+  <type>hvm</type>
+  <loader stateless='yes'/>
+  <boot dev='hd'/>
+</os>
+...
+
+

firmware固件

firmware属性项允许管理程序自动填充元素,通过选择的固件可以使能启用一些必须的特征项。可接受的值为bios和efi。选择过程扫描在特定位置的描述已安装固件镜像的文件,使用最具体的一个填充domain的需求。选择的位置(从通用到最具体的一个)如下:

+
    +
  • /usr/share/qemu/firmware
  • +
  • /etc/qemu/firmware
  • +
  • $XDG_CONFIG_HOME/qemu/firmware
  • +
+

对于更多信息,可以参考QEMU代码库中的docs/interop/firmware.json描述的固件元数据规范。常规用户可不关心这一点。从libvirt 5.2.0(仅限于QEMU和KVM)版本开始。

+

对于VMware客户机,当客户机使用UEFI时则必须设置为efi,使用BIOS时可以不设置。从libvirt 5.3.0(VMware ESX和Workstation/Player)版本开始。

+

type类型

type属性项指明了在虚拟机中启动的操作系统的类型。

+
    +
  • hvm表明操作系统被设计用来在裸机(bare metal)上运行,因此需要全虚拟化。
  • +
  • linux(糟糕的命名)表明是一个支持Xen 3虚拟机管理程序客户机ABI的操作系统。
  • +
  • 【可选】arch表明了需要虚拟化的CPU架构。
  • +
  • 【可选】machine表明了机器类型。
  • +
+

xml功能(Capabilities XML)章节描述了允许值得详细信息。大多数虚拟机管理程序的驱动程序都省略了arch的设置,则所在主机host的架构就会被选择。对于test、ESX和VMWare虚拟机管理程序驱动程序,在x86_64主机上通常会选择i686架构。从libvirt 0.0.1版本开始提供支持。

+

firmware固件

从libvirt 7.2.0版本开始仅对QEMU/KVM提供支持。

+

当使用固件自动选择功能时,固件中存在不同的可用特征。特征列表限制了为虚拟机自动选择的固件。特征列表也可以通过使用0或更多的feature元素进行指定。选择硬件时,libvirt只会考虑列表中的特征,忽略其他特征项。

+

feature

强制属性列表:

+
    +
  • enabled:可用值为yesno,告诉libvirt在自动选择固件时是否要启用或禁用对应特征项

    +
  • +
  • name:特征项名称,特征值如下表所示:

    +
      +
    • enrolled-keys:选择的nvram模板是否有默认证书注册。带有安全启动(Secure Boot)特征的固件如果没有注册密钥,将会以无签名二进制文件成功开机。仅针对带有安全启动(Secure Boot)特征的固件进行校验。

      +
    • +
    • secure-boot:固件是否实现UEFI安全启动(Secure Boot)特征。

      +
    • +
    +
  • +
+

loader

可选配置项loader标签指的是固件blob,通过绝对路径指定,被用来辅助domain创建过程。该标签为Xen全虚拟化domain使用,也被用来为QEMU/KVM domain设置QEMU BIOS文件路径。Xen自libvirt 0.1.0版本后被支持,QEMU/KVM自libvirt 0.9.12版本后被支持。该元素有两个可选的属性项:readonly(可用值为yes或no)表明镜像是可写的还是只读的。第二个属性项type可用值为rom和pflash。其告诉虚拟机管理程序文件应该被映射到客户机内存的什么位置上。例如,如果loader的路径指向一个UEFI镜像,type就应该是pflash。此外,一些固件会实现安全启动(Secure Boot)功能。属性项secure可以向虚拟机管理程序表明固件是否兼容安全启动(Secure Boot)功能。它不能用于在固件中启用或禁用功能本身。自libvirt 2.1.0版本后被支持.如果loader被标记为只读(read-only),之后在UEFI环境下,其会假定这里存在一个可用的可写NVRAM。在某些情况下,loader更倾向于在无 NVRAM环境下运行,在关机时丢弃任何配置变化。stateless标识(自libvirt 8.6.0版本后被支持)可被用来控制这种行为,如果设置为n,o则NVRAM将永远不会被创建。

+

nvram

一些UEFI固件可能想要使用一个非易失性存储器去保存一些变量。在主机中,这表示为一个文件,文件的绝对路径被保存在这一元素中。此外,当domain启动时,libvirt复制在qemu.conf中定义的所谓的主NVRAM存储文件。如果必要的话,template属性可用于配置文件中主NVRAM存储的每个域的覆盖映射。对于非持久性domain,如果NVRAM文件已由libvirt创建,则它会被留下,并且管理程序有责任保存和删除文件(如果需要持久保存)。自1.2.8起。

+

自8.5.0版本起,该元素拥有type属性项(可用值为file、block和network)。在这种情况下,NVRAM存储器被子元素使用和disk源相同的语法进行阐述。详情可参阅Hard drives, floppy disks, CDROMs章节。

+

注意:network支持的NVRAM不是从template中实例化的,因此使用者需人为提供一个可用的NVRAM镜像。

+

如果loader被标记为stateless,则提供该元素是无效的。

+

boot

dev属性项使用fd、hd、cdrom和network等值。用于指定下一个要考虑的引导设备。boot元素可以重复多次以安装用于轮流尝试的引导设备优先级列表。多个相同类型的设备根据它们的目标进行排序,同时保留总线的顺序。在define完domain后,libvirt返回XML配置(通过virDomainGetXMLDesc)列出已排好序的设备。排序完成之后,第一个设备被标记为可引导的(bootable)。配置为从“hd”引导且分配有vdb、hda、vda和hdc磁盘的域将从vda引导启动(排序列表为vda、vdb、hda、hdc)。类似的带有hdc、vda、vdb和hda磁盘的domain竟会从hda(磁盘排序为:hda、hdc、vda和vdb)引导启动。以所需方式进行配置将会十分麻烦,这就是引入每个设备引导元素的原因,也是提供对引导顺序的完全控制的首选方式。boot元素和每个设备引导元素是互斥的。boot元素自0.1.3起提供支持,per-device boot自0.8.8起提供支持。

+

smbios

如何填充客户机中可见的SMBIOS信息。mode属性项必须被指定,其值为emulate(虚拟机管理程序生成所有值)、host(从主机的SMBIOS值中复制除UUID之外的全部Block 0 和Block 1;virConnectGetSysinfo函数调用可以被用来查看哪些值被复制)或sysinfo(使用 SMBIOS System Information元素中的值)。如果未制定,则虚拟机管理程序默认使用。从0.8.7版本起提供支持。

+

到目前为止,BIOS/UEFI配置旋钮足够通用,可以由大多数(不是全部)固件实现。然而,从现在开始,并不是每一个单项配置都对所有固件起作用。例如,rebootTimeout对UEFI无作用,useserial可能对不产生输出到串行行的BIOS固件有影响等等。此外,固件经常不能导出其功能以便libvirt(或使用者)进行检查。固件功能的集合会随着新版本而发生变化。因此,建议用户在生产中依赖它们之前尝试他们使用的设置。

+

bootmenu

在客户机启动时是否启用交互式引导菜单提示。enable属性项可以是yes或no。如果没有指明,虚拟机见识程序默认使用该功能。自0.8.3版本提供支持。额外的属性项timeout指明需要多少毫秒等待启动菜单,直到它超时。允许的值是 [0, 65535] 范围内的数字,除非 enable 设置为“yes”,否则该属性项会被忽略。自1.2.8版本提供支持。

+

bios

该元素属性项useserial可用值为yes或no。该属性项启用或禁用串行图形适配器(Serial Graphics Adapter)功能,该功能可以使得用户在串口上看到BIOS信息。因此,需定义串口。自0.9.4版本起。自0.10.2版本起(仅限于QEMU)。还有另外一个属性项rebootTimeout,当引导启动失败时,该属性想决定是否重启以及间隔多长时间后再次尝试引导启动。该值以毫秒为单位,最大值为65535,特殊值-1禁用重启功能。

+

Host bootloader

采用半虚拟化的管理程序通常不会模拟BIOS,而是主机负责启动操作系统引导。在主机中使用pseudo-bootloader提供接口以便为客户机选择一个内核。一个例子便是Xen的pygrub。而Bhyve虚拟机管理程序也使用主机bootloader方式,bhyveload或grub-bhyve。

+

bootloader

bootloader提供了主机操作系统中引导加载程序可执行文件的完全限定路径。运行bootloader选择要引导的内核。引导加载程序所需的输出取决于使用的管理程序。自0.1.1起。

+

bootloader_args

可选配置项bootloader_args允许将命令行参数传递给bootloader。自0.2.3起。

+

Direct kernel boot

安装一个新的客户机操作系统时,直接从存储在主机操作系统中的内核和 initrd引导通常很有用,可以将命令行参数直接传递给安装程序。该功能可在半虚拟化和全虚拟化客户机中使用。

+
...
+<os>
+  <type>hvm</type>
+  <loader>/usr/lib/xen/boot/hvmloader</loader>
+  <kernel>/root/f8-i386-vmlinuz</kernel>
+  <initrd>/root/f8-i386-initrd</initrd>
+  <cmdline>console=ttyS0 ks=http://example.com/f8-i386/os/</cmdline>
+  <dtb>/root/ppc.dtb</dtb>
+  <acpi>
+    <table type='slic'>/path/to/slic.dat</table>
+  </acpi>
+</os>
+...
+
+

type

该属性项与之前在BIOS bootloader中的描述有相同的语义。

+

loader

该属性项与之前在BIOS bootloader中的描述有相同的语义。

+

kernel

该属性项指明了主机中内核镜像的全限定路径。

+

initrd

该属性项指明了主机中ramdisk镜像(可选项)的全限定路径。

+

cmdline

该属性项指明了引导启动时传递给内核(或安装程序)的参数。这经常被用来指定备用主控制台(或串口)、安装媒体源/kickstart 文件。

+

dtb

该元素项指明了主机中的设备树二进制(device tree binary,dtb)镜像的全限定路径。从1.0.4版本起提供支持。

+

acpi

table元素包含了ACPI(Advanced Configuration and Power Interface,高级配置和电源接口)表。type属性项包含ACPI表类型(当前只有slic被支持)。自1.3.5支持QEMU。自5.9.0支持Xen。

+

Container boot

当使用基于虚拟化的容器引导启动一个虚拟机时,可不使用内核/引导镜像,使用init元素指定init二进制文件的路径。默认无参数加载。为指明初始化参数,使用initarg参数项,根据需要重复多次。如果设置了cmdline元素,将会被用来提供一个/proc/cmdline的替代项,但是却不会影响初始化参数(init argv)。

+

使用initenv元素设置环境变量,一个元素代表一个变量。

+

使用initdir元素为初始化设置特定的工作目录。

+

以给定的用户或用户组运行初始化命令,分别使用inituser或initgroup。两个元素可以被用来提供用户id或用户名称。使用**a+**前缀用户或组ID将强制将其视为数值。如果没有该项,它将首先作为用户名或组名进行尝试。

+
<os>
+  <type arch='x86_64'>exe</type>
+  <init>/bin/systemd</init>
+  <initarg>--unit</initarg>
+  <initarg>emergency.service</initarg>
+  <initenv name='MYENV'>some value</initenv>
+  <initdir>/my/custom/cwd</initdir>
+  <inituser>tester</inituser>
+  <initgroup>1000</initgroup>
+</os>
+
+

如果想要使用用户命名空间,设置idmap属性项。uid和gid元素有3个属性项:

+
    +
  • start:容器的第一个用户ID。其值为0.

    +
  • +
  • target:容器的第一个用户ID将会映射到主机中的目标用户ID。

    +
  • +
  • count:映射到主机中的用户的容器用户数量。

    + + + +
  • +
+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/09/02/domain_xml_format_SMBIOS-System-Information/index.html b/2022/09/02/domain_xml_format_SMBIOS-System-Information/index.html new file mode 100644 index 0000000..e0e28bb --- /dev/null +++ b/2022/09/02/domain_xml_format_SMBIOS-System-Information/index.html @@ -0,0 +1,553 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(3)SMBIOS System Information | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(3)SMBIOS System Information +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+

一些虚拟机管理器运行控制呈现给客户机的系统信息(例如虚拟机管理器可以填充SMBIOS并通过客户机的dmidecode指令进行检查)。可选元素项sysinfo包含了所有此类信息。自0.8.7版本起。

+ + +
...
+<os>
+  <smbios mode='sysinfo'/>
+  ...
+</os>
+<sysinfo type='smbios'>
+  <bios>
+    <entry name='vendor'>LENOVO</entry>
+  </bios>
+  <system>
+    <entry name='manufacturer'>Fedora</entry>
+    <entry name='product'>Virt-Manager</entry>
+    <entry name='version'>0.9.4</entry>
+  </system>
+  <baseBoard>
+    <entry name='manufacturer'>LENOVO</entry>
+    <entry name='product'>20BE0061MC</entry>
+    <entry name='version'>0B98401 Pro</entry>
+    <entry name='serial'>W1KS427111E</entry>
+  </baseBoard>
+  <chassis>
+    <entry name='manufacturer'>Dell Inc.</entry>
+    <entry name='version'>2.12</entry>
+    <entry name='serial'>65X0XF2</entry>
+    <entry name='asset'>40000101</entry>
+    <entry name='sku'>Type3Sku1</entry>
+  </chassis>
+  <oemStrings>
+    <entry>myappname:some arbitrary data</entry>
+    <entry>otherappname:more arbitrary data</entry>
+  </oemStrings>
+</sysinfo>
+<sysinfo type='fwcfg'>
+  <entry name='opt/com.example/name'>example value</entry>
+  <entry name='opt/com.coreos/config' file='/tmp/provision.ign'/>
+</sysinfo>
+...
+
+

sysinfo元素包含一个强制属性项type决定了子元素的布局,支持值如下:

+

smbios

子元素调用特定的SMBIOS值,如果被用在os元素(参照Operating system booting)的smbios子元素间的连接上,将会影响客户机。sysinfo 的每个子元素命名一个SMBIOS块,并且在这些元素中可以是描述块内字段的条目元素列表。识别以下块和条目:

+

bios

这是SMBIOS的块0,条目名称来自:

+
    +
  • vender:BIOS供应商名称
  • +
  • version:BIOS版本
  • +
  • date:BIOS发行日期。如果提供了该元素项,则会改为mm/dd/yy或mm/dd/yyyy的格式。如果字符串的年部分只有两个数字,则将会假定为19yy年。
  • +
+

system

这是SMBIOS的块1,条目名称来自:

+
    +
  • manufacturer:BIOS制造商
  • +
  • product:产品名称
  • +
  • version:产品版本
  • +
  • serial:序列号
  • +
  • uuid:统一唯一标识符。如果该条目与顶层的uuid的一起设定,则两个元素值必须相同
  • +
  • sku:识别特定配置的sku编号
  • +
  • family:识别特定计算机所属的家族系列
  • +
+

baseBoard

这是SMBIOS的块2.该元素项可以重复多次用以描述所有的基板(base board);然而,并不是所有的虚拟机管理器都支持该元素项重复。该元素项有下列的子元素项:

+
    +
  • manufacturer:BIOS的制造商
  • +
  • product:产品名称
  • +
  • version:产品版本号
  • +
  • serial:序列号
  • +
  • asset:资产标签
  • +
  • location:机箱内的位置
  • +
+

注意:bios、system和baseBoard块的错误条目会被忽略,而不会报错。除了uuid验证和日期格式检查之外,所有值都作为字符串传递给虚拟机管理器驱动程序。

+

chassis

自4.1.0起支持。SMBIOS的块3,条目名称来自:

+
    +
  • manufacturer:机箱的制造商
  • +
  • version:机箱的版本号
  • +
  • serial:序列号
  • +
  • asset:资产标签
  • +
  • sku:sku编号
  • +
+

oemStrings

SMBIOS的块11.该元素项只出现一次,却有多个entry子元素项,每一个都提供随机字符串数据。对于entry提供的数据并没有任何限制,如果数据打算由客户机中的应用程序使用,建议使用应用程序名称作为字符串中的前缀。(自4.1.0起支持)

+

fwcfg

一些虚拟机管理器提供统一的方法来调整固件如何配置自身,或者可能包含要为客户机操作系统安装的表,例如引导顺序、ACPI、SMBIOS等。

+

如果允许用户自定义自己的配置blob(config blob)。在QEMU的例子中,然后这些出现在domain的sysfs下(如果客户机内核启用FW_CFG_SYSFS配置项),即/sys/firmware/qemu_fw_cfg路径下。注意,这些值适用于下的模式。自6.5.0起支持。

+

注意:由于数据槽数量有限,强烈建议不要使用fwcfg,而应使用

+
<sysinfo type='fwcfg'>
+  <entry name='opt/com.example/name'>example value</entry>
+  <entry name='opt/com.example/config' file='/tmp/provision.ign'/>
+</sysinfo>
+
+

sysinfo元素项拥有多个entry子元素。每一个元素都有强制的name属性,它定义了blob的名称,并且必须以opt/开头,并且为了避免与其他名称冲突,建议采用opt/$RFQDN/$name形式,其中$RFQDN是控制的反向完全限定域名。之后,元素项也可以包含相应数值(直接设置blob值),或者file属性项(从file中设置blob值)。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/09/05/domain_xml_format_CPU-Allocation/index.html b/2022/09/05/domain_xml_format_CPU-Allocation/index.html new file mode 100644 index 0000000..14a8674 --- /dev/null +++ b/2022/09/05/domain_xml_format_CPU-Allocation/index.html @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(4)CPU Allocation | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(4)CPU Allocation +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+ + +
<domain>
+  ...
+  <vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>
+  <vcpus>
+    <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
+    <vcpu id='1' enabled='no' hotpluggable='yes'/>
+  </vcpus>
+  ...
+</domain>
+
+

vcpu

该元素定义了分配给客户机操作系统的虚拟cpu的最大数量,该值范围为1到虚拟机管理器支持的最大数量之间。

+

cpuset

可选属性cpuset是以逗号分隔的物理CPU编号列表,默认情况下虚拟机进程和虚拟CPU可以固定到对应编号的物理CPU上。(注意:虚拟机进程和虚拟cpu的绑定(pinning)策略可以由cputune分别指定。如果cputune的属性项emulatorpin被设置,则vcpu指定的cpuset将会被忽略。对于vcpupin指定的虚拟cpu,cpuset制定的cpuset也会被忽略。vcpupin未指定的虚拟cpu,每一个都会被绑定到cpuset指定的物理cpu上)。列表中的每一个元素可以是一个单独的cpu编号、cpu编号范围或是插入符号后跟要从先前范围中排除的CPU编号。自0.4.4版本起支持。

+

current

可选属性current可用于指定是否应启用少于最大数量的虚拟CPU。从 0.8.5开始。

+

placement

可选属性placement可用来表明虚拟机进程的cpu布局模式。该值可为static或auto,但是如果指定了cpuset,则默认numatune的placement设置为static。使用auto表示虚拟机进程将通过查询numa固定到咨询节点集,如果指定了属性cpuset的值,则将被忽略。如果cpuset和placement都没有指定或placement设置为static,但是cpuset为指定,则虚拟机进程将会被绑定到所有的可用物理cpu上。自0.9.11起(仅限于QEMU和KVM)。

+

vcpus

vcpus元素项运行控制单个vCPU的状态。id属性项指定了libvirt在其他地方使用的vCPU id,例如vCPU绑定、调度程序信息和NUMA分配。请注意,在某些情况下,客户机中看到的vCPU ID可能与libvirt ID不同。有效ID 从0到vcpu元素设置的最大vCPU计数减 1。enabled属性项允许控制vCPU的状态。有效值为yes和no。hotpluggable控制在引导开机时启用CPU是否可以热插拔和热拔出给定的vCPU。请注意,所有禁用的vCPU必须是可热插拔的。有效值为yes和no。order允许指定在线vCPU的顺序。对于需要一次插入多个vCPU的虚拟机管理器/平台,该顺序可能会在需要一次启用的所有 vCPU上重复。顺序不强制指定。然后以任意顺序添加vCPU。如果使用了顺序信息,它必须用于所有在线vCPU。虚拟机管理器可能在单一操作期间清理或更新顺序信息。请注意,虚拟机管理器可能会创建不同于引导vCPU的热插拔vCPU,因此可能需要进行特殊初始化。虚拟机管理器可能要求启动时启用的不可热插拔的vCPU在开始时从ID 0开始聚集。可能还需要vCPU 0始终存在且不可热插拔。请注意,可能需要为单个CPU提供状态以支持可寻址的 vCPU热插拔,并且此功能可能不受所有虚拟机管理器的支持。QEMU要求实现以下条件。vCPU 0需启用并设置为不可热插拔。在PPC64上,同样需要启用同一内核中的vCPU。引导开机时存在的所有非热插拔CPU都需要在vCPU 0 之后进行分组。自2.2.0起(仅限于QEMU)。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/10/19/domain_xml_format_IOThreads-Allocation/index.html b/2022/10/19/domain_xml_format_IOThreads-Allocation/index.html new file mode 100644 index 0000000..b2d59a7 --- /dev/null +++ b/2022/10/19/domain_xml_format_IOThreads-Allocation/index.html @@ -0,0 +1,490 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(5)IOThreads Allocation | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(5)IOThreads Allocation +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+

IOThreads是支持磁盘设备的专用事件循环线程,用于执行块I/O请求,以提高可伸缩性,尤其是在具有许多LUN的SMP主机/客户机上。自1.2.8起(仅限于QEMU)。

+ + +
<domain>
+  ...
+  <iothreads>4</iothreads>
+  ...
+</domain>
+
+<domain>
+  ...
+  <iothreadids>
+    <iothread id="2"/>
+    <iothread id="4"/>
+    <iothread id="6"/>
+    <iothread id="8" thread_pool_min="2" thread_pool_max="32"/>
+  </iothreadids>
+  <defaultiothread thread_pool_min="8" thread_pool_max="16"/>
+  ...
+</domain>
+
+

iothreads

此可选元素的内容定义要分配给虚拟机以供支持的目标存储设备使用的 IOThread数量。每一个主机CPU只有1个或2个IOThread。可能有多个受支持的设备分配给每个IOThread。自1.2.8起。

+

iothreadids

可选的iothreadids元素提供了为虚拟机专门定义IOThread ID的能力。默认情况下,IOThread ID 是从1到为虚拟机定义的iothreads的数量顺序编号的。id属性项用于定义IOThread ID。id属性项必须是一个大于0的正整数。如果定义的iothreadids少于为虚拟机定义的iothreads,则libvirt将从1开始按顺序填充iothreadids,避免任何预定义的id。如果iothreadids大于为虚拟机定义的iothreads,iothreads的值将会相应调整。自1.2.15起。该元素有两个可选属性项thread_pool_min和thread_pool_max用于定义给定IOThread工作线程的上下限。前者可能是0,后者绝不会为0.自8.5.0起。

+

defaultiothread

该元素代表了虚拟机管理程序内部的默认事件循环,为指定特定IOThread的I/O请求会被该循环处理。该元素可设置thread_pool_min和thread_pool_max属性项,指定了默认事件循环工作线程的上下限数量。模拟器可能是多线程的并按需生成所谓的工作线程。通常两个属性项都不会去设置(使得模拟器使用其自身的默认值),除非模拟器在实时工作负载中运行,因此无法承受生成新工作线程所需时间的不可预测性。自8.5.0起。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/10/20/access_usage/index.html b/2022/10/20/access_usage/index.html new file mode 100644 index 0000000..615b0e6 --- /dev/null +++ b/2022/10/20/access_usage/index.html @@ -0,0 +1,553 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _access函数32bit和64bit编译差异 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ _access函数32bit和64bit编译差异 +

+ + +
+ + + + +
+ + +

前言

最近,在进行软件开发工作中遇到了一个问题,觉得比较有趣,在此进行以下记录。

+

问题比较简单,需要检查windows系统下的C:\Windows\System32路径下是否存在特定的用户文件(dll格式)。在这里使用_access函数(C运行库函数)对文件进行检查,该函数主要用于检查文件或目录是否存在及其对应的读写权限。但是开发完成后,在自检过程中却发现使用_access函数无法达到预期,主要表现为文件已存在,但是函数返回结果却表明文件不存在。如果换用win32 API函数PathFileExistsA却正常,文件存在于对应目录。本文即对_access函数的异常进行分析。

+ + +

分析过程

win32函数访问没问题,而使用C语言库函数存在问题,即系统调用没问题,主要问题应该出现在C语言库上。而最简单的修改项就是编译环境修改,之前为了兼容性考虑,代码选择的编译环境为32位,因此将编译环境改为64位。此时,_access函数正常执行,返回了符合预期的结果。

+

因此,针对这一点进行分析,最终在微软官网上找到相关说明,链接如下:https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?from_wecom=1

+

微软在64位操作系统上,为64位应用程序保留了%windir%\System32(通常就是C:\Windows\System32)路径。因为通常情况下,32位dll和64位dll的文件名称完全相同,因此64位dll也保存在System32路径下,而32位dll则被保存在其他路径。这就使得64位操作系统下的32位应用程序访问对应dll时,直接访问%windir%\System32路径是错误的,因此微软为64位系统下的32位程序通过访问%windir%\System32路径做了重定向。而32位要想真正访问%windir%\System32路径,就可以使用%windir%\Sysnative路径替代,也可以使用Wow64DisableWow64FsRedirection函数禁用文件重定向功能。

+

示例程序:

+
#include  <io.h>
+#include  <stdio.h>
+#include  <stdlib.h>
+#include <Windows.h>
+#include <iostream>
+#include "Shlwapi.h"
+#include <errhandlingapi.h >
+
+int main( void )
+{
+    char *file_path = "C:\\Windows\\System32\\test.dll";
+    char *file_path1 = "C:\\Windows\\Sysnative\\test.dll";
+    // Check for existence.
+    printf_s( "1.access system32.\n");
+    if( (_access( file_path, 0 )) != -1 )
+    {
+        printf_s( "File %s is exists.\n", file_path);
+    }
+    else
+    {
+        printf_s( "File %s isn't exists.\n", file_path);
+    }
+    printf_s( "2.access sysnative.\n");
+    if( (_access( file_path1, 0 )) != -1 )
+    {
+        printf_s( "File %s is exists.\n", file_path1);
+    }
+    else
+    {
+        printf_s( "File %s isn't exists.\n", file_path1);
+    }
+    PVOID OldValue = NULL;
+
+    //  Disable redirection immediately prior to the native API function call.
+    printf_s( "3.Disable Redirection.\n");
+    bool flag = Wow64DisableWow64FsRedirection(&OldValue);
+    printf("Wow64DisableWow64FsRedirection return value is %d.\n", flag);
+    if (!flag)
+    {
+        DWORD dw = GetLastError();
+        printf("Errorcode is %d.\n", dw);
+    }
+    if( (_access( file_path, 0 )) != -1 )
+    {
+        printf_s( "File %s is exists.\n", file_path);
+    }
+    else
+    {
+        printf_s( "File %s isn't exists.\n", file_path);
+    }
+
+    return 0;
+}
+
+

在32位编译环境下输出如下:

+
1.access system32.
+File C:\Windows\System32\test.dll isn't exists.
+2.access sysnative.
+File C:\Windows\Sysnative\test.dll is exists.
+3.Disable Redirection.
+Wow64DisableWow64FsRedirection return value is 1.
+File C:\Windows\System32\test.dll is exists.
+
+

(1)C:\Windows\System32路径无法直接访问;

+

(2)C:\Windows\Sysnative路径会被重定位到C:\Windows\System32;

+

(3)Wow64DisableWow64FsRedirection函数返回1代表函数正常运行,此后再使用C:\Windows\System32路径,则无重定向操作,获取到了正确的结果;

+

在64位编译环境下输出如下:

+
1.access system32.
+File C:\Windows\System32\test.dll is exists.
+2.access sysnative.
+File C:\Windows\Sysnative\test.dll isn't exists.
+3.Disable Redirection.
+Wow64DisableWow64FsRedirection return value is 0.
+Errorcode is 1.
+File C:\Windows\System32\test.dll is exists.
+
+

(1)C:\Windows\System32路径可以直接访问;

+

(2)C:\Windows\Sysnative路径不会被重定位到C:\Windows\System32;

+

(3)Wow64DisableWow64FsRedirection函数返回0代表函数异常运行,但不影响后续使用C:\Windows\System32路径获取正确的结果;

+

结论

_access函数在32bit和64bit编译环境下输出结果存在差异,主要原因即为64位windows操作系统下,%windir%\System32被保留给了对应的64位应用程序,而32位应用程序访问%windir%\System32路径时就会被重定向到系统指定的其他路径上。而使用32bit编译环境最终生成的是32位应用程序,因此对应路径被重定向,直接访问%windir%\System32路径下的文件时就会出现异常。

+

因此建议在64位操作系统中使用64位的应用程序,而非32位的应用程序,虽然64位操作系统兼容32位应用程序,而32位操作系统不兼容64位应用程序,因此32位具有更广泛的兼容性。

+

如果不确定目标程序运行环境或程序存在运行于两种系统的使用场景,则在使用文件访问相关函数时:

+

(1)直接使用win32函数PathFileExistsA;

+

(2)使用_access函数前,先使用Wow64DisableWow64FsRedirection函数禁用文件重定向功能;

+

参考链接

(1)https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?from_wecom=1
(2)https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-wow64disablewow64fsredirection?from_wecom=1
(3)https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=msvc-170&from_wecom=1
(4)https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathfileexistsa?from_wecom=1

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/11/04/domain_xml_format_CPU-tuning/index.html b/2022/11/04/domain_xml_format_CPU-tuning/index.html new file mode 100644 index 0000000..6f1e9cf --- /dev/null +++ b/2022/11/04/domain_xml_format_CPU-tuning/index.html @@ -0,0 +1,535 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(6)CPU Tuning | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(6)CPU Tuning +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+ + +
<domain>
+  ...
+  <cputune>
+    <vcpupin vcpu="0" cpuset="1-4,^2"/>
+    <vcpupin vcpu="1" cpuset="0,1"/>
+    <vcpupin vcpu="2" cpuset="2,3"/>
+    <vcpupin vcpu="3" cpuset="0,4"/>
+    <emulatorpin cpuset="1-3"/>
+    <iothreadpin iothread="1" cpuset="5,6"/>
+    <iothreadpin iothread="2" cpuset="7,8"/>
+    <shares>2048</shares>
+    <period>1000000</period>
+    <quota>-1</quota>
+    <global_period>1000000</global_period>
+    <global_quota>-1</global_quota>
+    <emulator_period>1000000</emulator_period>
+    <emulator_quota>-1</emulator_quota>
+    <iothread_period>1000000</iothread_period>
+    <iothread_quota>-1</iothread_quota>
+    <vcpusched vcpus='0-4,^3' scheduler='fifo' priority='1'/>
+    <iothreadsched iothreads='2' scheduler='batch'/>
+    <cachetune vcpus='0-3'>
+      <cache id='0' level='3' type='both' size='3' unit='MiB'/>
+      <cache id='1' level='3' type='both' size='3' unit='MiB'/>
+      <monitor level='3' vcpus='1'/>
+      <monitor level='3' vcpus='0-3'/>
+    </cachetune>
+    <cachetune vcpus='4-5'>
+      <monitor level='3' vcpus='4'/>
+      <monitor level='3' vcpus='5'/>
+    </cachetune>
+    <memorytune vcpus='0-3'>
+      <node id='0' bandwidth='60'/>
+    </memorytune>
+
+  </cputune>
+  ...
+</domain>
+
+

cputune

可选元素项cputune为虚拟机提供与cpu可调参数相关的细节设置。注意:对于qemu驱动而言,可选的vcpupin和emulatorpin绑定设置需要考虑模拟器启动和NUMA限制后再进行设置。这就意味着主机的其他物理cpu在这段时间内会被虚拟机使用,这也可以从virsh cpu-stats的输出中反映出来。自0.9.0起。

+

vcpupin

可选元素项vcpupin指明了虚拟机的vCPU将会被绑定到主机的哪一个物理CPU上。如果忽略该设置项,vcpu元素的cpuset属性项也没有设置,则vcpu会默认绑定到全部的物理cpu上。其包含两个所需的属性项,属性项vcpu指明了vcpu id,属性项cpuset与vcpu元素的cpuset属性项是完全一致的。qemu自0.9.0起被支持,xen自0.9.1起被支持。

+

emulatorpin

可选元素项emulatorpin元素指明了主机的拿个物理cpu是模拟器,这是不包含虚拟机的vcpu和iothreads绑定的cpu的子集。如果忽略该项设置,也没有设置vcpu的cpuset属性项,模拟器将会默认绑定到全部的物理cpu上。emulatorpin包含了一个必须的属性项cpuset用于指明要绑定到哪一个物理cpu中。

+

iothreadpin

可选元素项iothreadpin指明了IOThreads将会被绑定到哪一个物理cpu上。如果忽略该设置,且vcpu元素项的cpuset属性项也没有设置,IOThreads将会默认绑定到全部的物理cpu上。这里存在两个必须的属性项,属性项iothread指明了IOThread ID,属性项cpuset指明了绑定的物理cpu。可查看IOThreads Allocation章节查看iothread的有效值。自1.2.9起。

+

shares

可选元素项shares指明了虚拟机的比例加权份额。如果忽略该项设置,默认使用操作系统提供的默认值。注意,这个值没有单位,是建立在其他虚拟机设置上的相对量度。一个设置为2048的虚拟机将会相对另一个设置为1024的虚拟机得到两倍长的cpu时间。这个值得范围是2-262144.自0.9.0起。

+

period

可选元素项period指明了执行间隔(单位:毫秒)。在该元素项内,虚拟机的每一个vcpu都不允许消耗超过配额的运行时间。该值的范围是1000-1000000.设置为0则意味着未设置数值。qemu自0.9.4起支持,lxc自0.9.10起支持。

+

quota

可选元素项quota指定了最大允许带宽(单位:毫秒)。quota设置为负值的虚拟机表明对于其vcpu线程具有无限制的带宽,即其不受带宽控制。该值的范围是1000-17592186044415或小于0.quota为0意味着未设置数值。可使用该特征确保所有的vcpu以相同速度运行。qemu自0.9.4起支持,lxc自0.9.10起支持。

+

global_period

可选元素项global_period指定整个虚拟机的强制CFS调度程序间隔(单位:微秒),与强制每个vCPU间隔的period形成对比。该值的范围是1000-1000000.global_period设置为0意味着未设置数值。qemu自1.3.3起支持。

+

global_quota

可选元素项global_quota指明了全虚拟机在周期时间内的最大允许带宽(单位:毫秒)。global_quota设置为负值的虚拟机表明虚拟机具有无限制的带宽,即其不受带宽控制。该值的范围是1000-17592186044415或小于0.global_quota为0意味着未设置数值。qemu自1.3.3起支持。

+

emulator_period

可选元素项emulator_period指明了强制间隔(单位:毫秒)。使用emulator_period设置,则虚拟机的模拟器线程(包括vcpu)不允许消耗超过emulator_quota的运行时间。该值的范围是1000-1000000.为0意味着未设置数值。qemu自0.10.0起支持.

+

emulator_quota

可选元素项emulator_quota指明了虚拟机模拟器线程(包括vcpu)最大允许带宽(单位:毫秒)。带有负值emulator_quota设置的虚拟机的模拟器线程(包括vcpu)拥有无限带宽,即其不受带宽控制。值范围是1000-17592186044415或小于0.quota设置为0意味着未设置值。qemu自0.10.0起支持.

+

iothread_period

可选元素项iothread_period指明了IOThreads的强制间隔(单位:毫秒)。使用iothread_period,虚拟机的每一个IOThreads不允许消耗超过iothread_quota设置的运行时间。该值的范围是1000-1000000.设置为0意味着未设置该值。自2.1.0起支持qemu。

+

iothread_quota

可选元素项iothread_quota指明了IOThreads的最大允许带宽(单位:毫秒)。iothread_quota为负值的虚拟机表明虚拟机IOThreads具有无限带宽,即其不受带宽控制。该值的范围是1000-17592186044415或小于0.设置为0意味着未设置该值。可使用该特征确保全部IOThreads以相同速度运行。自2.1.0起支持qemu。

+

vcpusched/iothreadsched/emulatorsched

可选元素项vcpusched、iothreadsched和emulatorsched分别指明了特定vcpu、iothread和模拟器线程的调度类型(值为batch、idle、fifo和rr)。对于vcpusched和iothreadsched,属性项vcpus和iothreads选择了该设置适用于哪些vCPUs/IOThreads,其余的设置为默认值。元素项emulatorsched不具有该属性。vcpus的有效值为0到虚拟机中设置的vcpu数量减一。iothreads的有效值在IOThreads Allocation章节中有过描述。如果未定义iothreadids,然后libvirt将IOThreads编号从1到虚拟机可用的iothreads进行计数。对于实时调度(fifo、rr),也需指定优先级(非实时调度可忽略)。优先级的范围依赖于主机内核(通常为1-99)。自1.2.13起。emulatorsched自5.3.0起。

+

cachetune(自4.1.0起)

可选元素项cachetune使用主机上的restctrl控制cpu缓存分配。是否支持此功能可以从一些限制(如最小尺寸和所需粒度)的功能中获取。必需的属性项vcpus指定了本次分配适用于哪一个vcpu。一个vcpu只能是一个cachetune元素分配项的成员。cachetune指定的vCPU可以与memorytune 中的vCPU相同,但不允许重复指定。可选的、仅输出的id属性唯一标识缓存。支持以下子元素:

+

cache

该可选元素项控制cpu缓存分配并具有以下属性项:

+

level

用于分配的主机缓存等级。

+

id

用于分配的主机缓存id。

+

type

分配的类型。code代表指令,data代表数据。both则同时代表两者。当前分配类型只能与主机支持的类型保持一致,意味着不能在启用cdp(指令、数据优先级)的主机上使用both类型。

+

size

分配区域大小。默认值以字节为单位,但是unit属性项可用来缩放该值。

+

unit(可选)

如果unit设置为KiB、MiB、GiB或TiB(在Memory Allocation中的memory元素中描述),则size的默认值为字节。

+

monitor(自4.10.0起)

可选元素项monitor为当前缓存分配创建缓存管理器并拥有如下所需属性:

+

level

monitor所属的主机缓存等级。

+

vcpus

vcpu列出了monitor的适用范围。一个monitor的vcpu列表只能是相关分配的vcpu列表的一部分。默认的管理器与相关的分配具有相同的vcpu列表。对于非默认monitor,不允许重复定义vcpu。

+

memorytune(自4.7.0起)

可选元素项memorytune可使用主机上的resctrl控制内存带宽分配。是否支持此功能可以从一些限制(如最小尺寸和所需粒度)功能中获取。必需的属性项vcpus指定了本次分配适用于哪一个vcpu。一个vcpu只能是一个memorytune元素分配项的成员。memorytune指定的vCPU可以与cachetune中的vCPU相同,但不允许重复指定。支持以下子元素:

+

node

该元素控制cpu内存带宽分配,拥有以下属性。

+

id

分配内存带宽的主机节点id。

+

bandwidth

节点中用于分配的内存带宽。该值默认以百分比为单位。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/11/10/domain_xml_format7_memory_allocation/index.html b/2022/11/10/domain_xml_format7_memory_allocation/index.html new file mode 100644 index 0000000..6fcf5c0 --- /dev/null +++ b/2022/11/10/domain_xml_format7_memory_allocation/index.html @@ -0,0 +1,524 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(7)Memory Allocation、Memory Backing和Memory Tuning | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(7)Memory Allocation、Memory Backing和Memory Tuning +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+ + +

Memory Allocation

<domain>
+  ...
+  <maxMemory slots='16' unit='KiB'>1524288</maxMemory>
+  <memory unit='KiB'>524288</memory>
+  <currentMemory unit='KiB'>524288</currentMemory>
+  ...
+</domain>
+
+

memory

boot时虚拟机的最大分配内存。内存分配包含了在启动时或在之后热插拔的全部可能的额外内存设备。该值的单位由可选属性项unit设置,默认以KiB为单位(kibibytes,2^10或1024字节块)。有效的单位还有b或byte代表字节,KB代表kilobytes(10^3或1000字节),k或KiB代表kibibytes(1024字节)。MB代表megabytes(10^6或1000000字节),M或MiB代表 mebibytes(2^20或1048576字节),GB代表gigabytes(10^9或1000000000字节),T或TiB代表tebibytes(2^40或1099511627776字节).然而,该值会被libvirt四舍五入到最近的kibibytes,也可能进一步四舍五入到虚拟机管理程序支持的粒度。一些虚拟机管理程序也会强制设置一个最小值,例如4000KiB。这种情况下,虚拟机配置的numa的memory元素会被忽略。如果发生crash,可选属性项dumpCore可用来控制是否将虚拟机内存包含在生成的coredump文件中(属性值为on和off)。unit自0.9.11起,dumpCore自0.10.2起(仅限于qemu)。

+

maxMemory

运行时虚拟机的最大内存分配。被numa的memory元素或者numa cell大小配置的初始内存可通过内存热插拔方式增长到该元素限制的最大内存大小。unit属性项与memory中的同名属性项表现完全一致。slots属性项指定了将内存添加到虚拟机时可用slot的数量。数量上下限与虚拟机管理程序有关。注意由于内存对齐的限制,通过内存条热插拔所获得的完整内存大小可能无法达到该元素的设置值。自1.2.14起在qemu中支持。

+

currentMemory

虚拟机真实分配的内存。该值小于最大分配数值,允许动态扩充虚拟机内存。如果未设置该项,则默认与memory元素具有同样的数值设置。unit属性项与memory中的unit作用一致。

+

Memory Backing

<domain>
+  ...
+  <memoryBacking>
+    <hugepages>
+      <page size="1" unit="G" nodeset="0-3,5"/>
+      <page size="2" unit="M" nodeset="4"/>
+    </hugepages>
+    <nosharepages/>
+    <locked/>
+    <source type="file|anonymous|memfd"/>
+    <access mode="shared|private"/>
+    <allocation mode="immediate|ondemand" threads='8'/>
+    <discard/>
+  </memoryBacking>
+  ...
+</domain>
+
+

可选元素项memoryBacking包含多个影响主机内存页备份虚拟内存的元素项。

+

hugepages

该元素项告知虚拟机管理程序虚拟机使用大页而非通常的主机内存页大小进行内存分配。自1.2.5起。可以为每个numa节点详尽的设置大页。page元素项被引入。其包含一个强制属性项size用于指定具体使用哪些大页(在支持不同大小大页的系统中尤其有用)。size属性项的默认单位是kilobytes(1024的整数倍)。如果想要使用不同的单位,可使用可选属性项unit。对于numa系统,可选的nodeset属性可能会派上用场,因为它将给定虚拟机的NUMA节点与某些大页大小相关联。在某些例子中,除了4#节点之外的其他numa节点都使用了1G大小的大页。相关语法可以查看NUMA Node Tuning章节。

+

nosharepages

告知虚拟机管理程序虚拟机禁用共享内存页(内存合并,ksm)。自1.0.6起。

+

locked

当虚拟机管理程序支持并设置相关数值后,虚拟机内存页将会在主机内存中锁定,而且主机也不会将其换出,这在某些工况下如实时场景中是十分必要的。对于QEMU/KVM虚拟机,qemu进程自身使用的内存也会被锁定;与虚拟机内存不同,libvirt无法提前获取相应内存的数量,所以必须移除对于锁定内存的限制。因此,启用该可选元素项存在潜在的安全风险;当主机内存耗尽时主机无法从虚拟机回复内存,这就意味着大量的虚拟机将会造成大量内存被锁定,主机上会遭受到拒绝服务攻击。因为该原因,除非确实需要否则不建议使用该元素项;即便如此,强烈推荐为特定环境的内存分配设置 hard_limit属性项(参阅Memory Tuning章节),这可以有效的缓解上述描述的风险的发生。自1.0.6起。

+

source

使用type属性项,可提供“file”以利用文件内存备份或保持默认的”anonymous”。自4.10.0起,也可选择memfd备份。(仅限于QEMU/KVM)

+

access

使用mode属性项,指明内存是shared还是private。该元素会被每个numa节点的memAccess元素覆盖。

+

allocation

使用可选属性项mode,通过提供”immediate”或”ondemand”参数指明何时进行内存分配。自8.2.0起。也可以通过threads属性设置虚拟机管理程序用来进行内存分配的线程数量。

+

discard

当虚拟机管理程序支持并设置该元素时,在虚拟机关机之前(或当DIMM模块被拔出时)内存内容被丢弃。注意这仅仅只是一个优化项,并不保证在所有场景下都会起作用(例如虚拟机管理程序crash时)。自4.4.0起(仅限于QEMU/KVM)。

+

Memory Tuning

<domain>
+  ...
+  <memtune>
+    <hard_limit unit='G'>1</hard_limit>
+    <soft_limit unit='M'>128</soft_limit>
+    <swap_hard_limit unit='G'>2</swap_hard_limit>
+    <min_guarantee unit='bytes'>67108864</min_guarantee>
+  </memtune>
+  ...
+</domain>
+
+

memtune

可选元素项memtune提供与虚拟机内存调度参数相关的细节描述。如果忽略该项设置,则默认由操作系统提供默认值。对于QEMU/KVM,应用于qemu进程的参数被看做一个整体。因此,对其进行计数时,需要加上虚拟机ram、虚拟机显卡ram和qemu自身的一些内存。最后一点很难确定具体数值,因此需要猜想与尝试。对于每个可调参数,可以使用与memory相同的值来指定输入时数字所使用的单位。为了向后兼容,输出始终以KiB为单位。unit自0.9.11起。全部*_limit参数的可能值在0到VIR_DOMAIN_MEMORY_PARAM_UNLIMITED的范围内。

+

hard_limit

可选元素项hard_limit是虚拟机可使用的最大内存。该值的单位是kibibytes(1024字节的块)。qemu和kvm的使用者清冽建议不要设置相关限制项,因为如果虚拟机运行过少则虚拟机可能被内核关闭,决定进程运行所需的内存是一个无法决定的问题;也就是说,如果因工况需要已在 Memory Backing中设置了locked属性,您必须考虑部署的具体细节,并确定一个足够大的hard_limit值,以支持您的虚拟机的内存需求,但是该值足够小也可以保护主机以避免虚拟机锁定全部的内存。

+

soft_limit

可选元素项soft_limit就是内存争用期间强制设置的内存限制。该值的单位是kibibytes(1024字节块)。

+

swap_hard_limit

可选元素项swap_hard_limit就是加上交互内存swap后虚拟机可食用的最大内存。该值的单位是kibibytes(1024字节块)。该值应大于hard_limit的设置值。

+

min_guarantee

可选元素项min_guarantee就是应保证的虚拟机最小分配内存。该值的单位是kibibytes(1024字节块)。该元素仅被VMware ESX和OpenVZ支持。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/11/14/domain_xml_format8_block_io_tuning_resource_partitioning_fibre_channel_vmid/index.html b/2022/11/14/domain_xml_format8_block_io_tuning_resource_partitioning_fibre_channel_vmid/index.html new file mode 100644 index 0000000..c4a4a17 --- /dev/null +++ b/2022/11/14/domain_xml_format8_block_io_tuning_resource_partitioning_fibre_channel_vmid/index.html @@ -0,0 +1,530 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(8)NUMA Node Tuning、Block I/O Tuning、Resource partitioning、Fibre Channel VMID | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(8)NUMA Node Tuning、Block I/O Tuning、Resource partitioning、Fibre Channel VMID +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+ + +

NUMA Node Tuning

<domain>
+  ...
+  <numatune>
+    <memory mode="strict" nodeset="1-4,^3"/>
+    <memnode cellid="0" mode="strict" nodeset="1"/>
+    <memnode cellid="2" mode="preferred" nodeset="2"/>
+  </numatune>
+  ...
+</domain>
+
+

numatune

可选元素项numatune提供了如何通过控制虚拟机进程的NUMA策略对NUMA主机的性能进行调度。注意,仅支持QEMU。自0.9.3起。

+

memory

可选元素项memory指明了如何对NUMA主机的虚拟机进程进行内存分配。其包含多个可选属性项。属性项mode的可用值包含interleave、strict、preferred和restrictive,默认设置为strict。restrictive指明使用系统默认策略,仅有cgroups被用来限制内存节点,在memnode元素中将mode设置为restrictive。属性项nodeset指明了numa节点,其与vcpu元素项的cpuset属性项使用相同的语法。属性项placement(自0.9.12起)可被用来表明虚拟机进程的内存放置模式,其值可以为static或auto,是vcpu的placement的默认值,如果指定了nodeset则默认值为static。auto表明虚拟机进程只会从查询numad返回的咨询节点中集中分配内存,属性nodeset的值在指定auto的情况下将会被忽略。如果vcpu的placement设置为auto,且numatune未指定,则numatune的placement设置为auto,mode设置为strict。自0.9.3起。参阅 virDomainSetNumaParameters获取该元素的更多信息。

+

memnode

可选元素项memnode可以为每个虚拟机的numa节点指定内存分配策略。对于没有memnode元素的哪些节点,memory元素的默认值将会被使用。属性项cellid寻址应用设置的虚拟机NUMA节点。属性项mode和nodeset与memory元素中的对应值具有相同的含义和语法。设置与自动placement不兼容。qemu自1.2.7起。

+

Block I/O Tuning

<domain>
+  ...
+  <blkiotune>
+    <weight>800</weight>
+    <device>
+      <path>/dev/sda</path>
+      <weight>1000</weight>
+    </device>
+    <device>
+      <path>/dev/sdb</path>
+      <weight>500</weight>
+      <read_bytes_sec>10000</read_bytes_sec>
+      <write_bytes_sec>10000</write_bytes_sec>
+      <read_iops_sec>20000</read_iops_sec>
+      <write_iops_sec>20000</write_iops_sec>
+    </device>
+  </blkiotune>
+  ...
+</domain>
+
+

blkiotune

可选元素项blkiotune为虚拟机提供了调节Blkio cgroup可调参数的能力。如果忽略该设置项,则其默认由操作系统提供。自0.8.8起。

+

weight

可选元素项weight是虚拟机的全部i/o负载。该值的范围是100-1000.在2.6.39内核后,该值的范围是10-1000.

+

device

虚拟机可能又有多个device元素项,以便深入调节虚拟机正在使用的每一个主机块设备的权重。注意多个磁盘(可参阅Hard drives, floppy disks, CDROMs章节)可能共享同一个主机块设备,如果他们被相同的主机文件系统中的文件备份,这也是为什么使用全局作用域的调节参数而非相关的每一个虚拟机磁盘设备的原因(与磁盘定义中的iotune元素项相反(可参阅Hard drives, floppy disks, CDROMs章节),而iotune元素项适用于单个独立的磁盘)。每一个device元素项有两个强制的子元素项,path描述了设备的绝对路径,而weight给出了设备的相对权重,权重范围是100-1000.在2.6.39版本内核之后,该值的范围变为10-1000.自0.9.8起。除此之外,也可使用如下的可选子元素项:

+

read_bytes_sec

以字节为单位的每秒可读吞吐量。自1.2.2起。

+

write_bytes_sec

以字节为单位的每秒可写吞吐量。自1.2.2起。

+

read_iops_sec

每秒i/o读操作限制。自1.2.2起。

+

write_iops_sec

每秒i/o写操作限制。自1.2.2起。

+

Resource partitioning

虚拟机管理程序可能允许将虚拟机放入资源分区,也可能嵌套所述分区。resource元素将与资源分区相关的配置项组织在一起。当前其支持partition子元素项,该子元素项的内容定义了放置虚拟机的资源分区的绝对路径。如果没有列出任何分区,虚拟机将会被放到默认分区中。应用程序或管理员有责任确保分区在虚拟机启动之前就已存在。只有默认分区(适用于特定虚拟机管理程序)可以默认假定已经存在。

+
...
+<resource>
+  <partition>/virtualmachines/production</partition>
+</resource>
+...
+
+

资源分区当前在qemu和lxc中受到支持,在所有已安装的控制器中将分区路径映射到cgroups目录。自1.0.5起。

+

Fibre Channel VMID

FC SAN可以提供多个依赖于VMID的QoS等级和访问控制功能。它还可以收集每个虚拟机的遥测数据,这些数据可用于增强虚拟机的IO性能。可以通过fibrechannel元素项的appid属性项进行配置。该属性项包含了简单的字符串(最大128字节),内核可使用该属性项创建VMID。

+
...
+<resource>
+  <fibrechannel appid='userProvidedID'/>
+</resource>
+...
+
+

使用该特征项要求支持光纤通道的硬件,内核编译时配置有 CONFIG_BLK_CGROUP_FC_APPID选项,且nvme_fc内核模块已加载。自7.7.0起。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2022/11/15/domain_xml_format9_cpu_model_topology/index.html b/2022/11/15/domain_xml_format9_cpu_model_topology/index.html new file mode 100644 index 0000000..6cec9b1 --- /dev/null +++ b/2022/11/15/domain_xml_format9_cpu_model_topology/index.html @@ -0,0 +1,524 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】libvirt虚拟机xml配置文件格式(9)CPU model and topology | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】libvirt虚拟机xml配置文件格式(9)CPU model and topology +

+ + +
+ + + + +
+ + +

原文网址:https://libvirt.org/formatdomain.html#id8

+ + +

NUMA Node Tuning

<domain>
+  ...
+  <numatune>
+    <memory mode="strict" nodeset="1-4,^3"/>
+    <memnode cellid="0" mode="strict" nodeset="1"/>
+    <memnode cellid="2" mode="preferred" nodeset="2"/>
+  </numatune>
+  ...
+</domain>
+
+

numatune

可选元素项numatune提供了如何通过控制虚拟机进程的NUMA策略对NUMA主机的性能进行调度。注意,仅支持QEMU。自0.9.3起。

+

memory

可选元素项memory指明了如何对NUMA主机的虚拟机进程进行内存分配。其包含多个可选属性项。属性项mode的可用值包含interleave、strict、preferred和restrictive,默认设置为strict。restrictive指明使用系统默认策略,仅有cgroups被用来限制内存节点,在memnode元素中将mode设置为restrictive。属性项nodeset指明了numa节点,其与vcpu元素项的cpuset属性项使用相同的语法。属性项placement(自0.9.12起)可被用来表明虚拟机进程的内存放置模式,其值可以为static或auto,是vcpu的placement的默认值,如果指定了nodeset则默认值为static。auto表明虚拟机进程只会从查询numad返回的咨询节点中集中分配内存,属性nodeset的值在指定auto的情况下将会被忽略。如果vcpu的placement设置为auto,且numatune未指定,则numatune的placement设置为auto,mode设置为strict。自0.9.3起。参阅 virDomainSetNumaParameters获取该元素的更多信息。

+

memnode

可选元素项memnode可以为每个虚拟机的numa节点指定内存分配策略。对于没有memnode元素的哪些节点,memory元素的默认值将会被使用。属性项cellid寻址应用设置的虚拟机NUMA节点。属性项mode和nodeset与memory元素中的对应值具有相同的含义和语法。设置与自动placement不兼容。qemu自1.2.7起。

+

Block I/O Tuning

<domain>
+  ...
+  <blkiotune>
+    <weight>800</weight>
+    <device>
+      <path>/dev/sda</path>
+      <weight>1000</weight>
+    </device>
+    <device>
+      <path>/dev/sdb</path>
+      <weight>500</weight>
+      <read_bytes_sec>10000</read_bytes_sec>
+      <write_bytes_sec>10000</write_bytes_sec>
+      <read_iops_sec>20000</read_iops_sec>
+      <write_iops_sec>20000</write_iops_sec>
+    </device>
+  </blkiotune>
+  ...
+</domain>
+
+

blkiotune

可选元素项blkiotune为虚拟机提供了调节Blkio cgroup可调参数的能力。如果忽略该设置项,则其默认由操作系统提供。自0.8.8起。

+

weight

可选元素项weight是虚拟机的全部i/o负载。该值的范围是100-1000.在2.6.39内核后,该值的范围是10-1000.

+

device

虚拟机可能又有多个device元素项,以便深入调节虚拟机正在使用的每一个主机块设备的权重。注意多个磁盘(可参阅Hard drives, floppy disks, CDROMs章节)可能共享同一个主机块设备,如果他们被相同的主机文件系统中的文件备份,这也是为什么使用全局作用域的调节参数而非相关的每一个虚拟机磁盘设备的原因(与磁盘定义中的iotune元素项相反(可参阅Hard drives, floppy disks, CDROMs章节),而iotune元素项适用于单个独立的磁盘)。每一个device元素项有两个强制的子元素项,path描述了设备的绝对路径,而weight给出了设备的相对权重,权重范围是100-1000.在2.6.39版本内核之后,该值的范围变为10-1000.自0.9.8起。除此之外,也可使用如下的可选子元素项:

+

read_bytes_sec

以字节为单位的每秒可读吞吐量。自1.2.2起。

+

write_bytes_sec

以字节为单位的每秒可写吞吐量。自1.2.2起。

+

read_iops_sec

每秒i/o读操作限制。自1.2.2起。

+

write_iops_sec

每秒i/o写操作限制。自1.2.2起。

+

Resource partitioning

虚拟机管理程序可能允许将虚拟机放入资源分区,也可能嵌套所述分区。resource元素将与资源分区相关的配置项组织在一起。当前其支持partition子元素项,该子元素项的内容定义了放置虚拟机的资源分区的绝对路径。如果没有列出任何分区,虚拟机将会被放到默认分区中。应用程序或管理员有责任确保分区在虚拟机启动之前就已存在。只有默认分区(适用于特定虚拟机管理程序)可以默认假定已经存在。

+
...
+<resource>
+  <partition>/virtualmachines/production</partition>
+</resource>
+...
+
+

资源分区当前在qemu和lxc中受到支持,在所有已安装的控制器中将分区路径映射到cgroups目录。自1.0.5起。

+

Fibre Channel VMID

FC SAN可以提供多个依赖于VMID的QoS等级和访问控制功能。它还可以收集每个虚拟机的遥测数据,这些数据可用于增强虚拟机的IO性能。可以通过fibrechannel元素项的appid属性项进行配置。该属性项包含了简单的字符串(最大128字节),内核可使用该属性项创建VMID。

+
...
+<resource>
+  <fibrechannel appid='userProvidedID'/>
+</resource>
+...
+
+

使用该特征项要求支持光纤通道的硬件,内核编译时配置有 CONFIG_BLK_CGROUP_FC_APPID选项,且nvme_fc内核模块已加载。自7.7.0起。

+

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2023/07/19/windbg_debug_technique_reading_and_writing_memory_1/index.html b/2023/07/19/windbg_debug_technique_reading_and_writing_memory_1/index.html new file mode 100644 index 0000000..3b79f54 --- /dev/null +++ b/2023/07/19/windbg_debug_technique_reading_and_writing_memory_1/index.html @@ -0,0 +1,492 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + windbg标准调试技巧-读写内存(1):通过虚拟地址访问内存 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ windbg标准调试技巧-读写内存(1):通过虚拟地址访问内存 +

+ + +
+ + + + +
+ + +

在调试过程中可以通过使用多个指令来访问内存或内存区域。visual studio和windbg提供命令行命令时,也提供了用户图形界面,用户可以用图形界面来查看和编辑内存。详情可以参考windbg帮助文档中的在visual studio中查看和编辑内存和寄存器,以及在windbg中查看和编辑内存两个章节。

+ + +

以下命令可以读取与写入多种格式的内存。这些屙屎包含了16字节、字格式(单字、双字、四字、八字格式)、整数格式(short、long、quad、unsigned格式)、浮点数格式(10字节、16字节、32字节、64字节实数格式)以及ascii字符格式。

+
    +
  • d*(展示内存display memory)指令会展示特定内存或内驱区域的内容。
  • +
  • e*(输入数值enter values)指令向特定的内存地址写入数值。
  • +
+

也可以使用如下指令处理更加特定的数据类型:

+
    +
  • dt(展示类型display type)指令会检索多种数据类型并展示被当前正在调试的应用程序锁创建出来的数据结构。改名了用途广泛并拥有多种变体及可选配置项。
  • +
  • ds(展示字符串display string)指令展示了STRING、ANSI_STRING和UNICODE_STRING数据结构。
  • +
  • dl(展示链表display linked list)指令追踪并展示链表。
  • +
  • d*s(展示字格式和符号display words and symbols)指令检索可能包含符号信息的双字或四字结构,之后展示对应的数据及符号信息。
  • +
  • !address扩展指令展示位于特定地址的内存属性信息。
  • +
+

可以使用如下指令来进行内存范围操作:

+
    +
  • m(移动内存move memory)指令将一个内存范围的内容移到另外一个中。
  • +
  • f(填充内存fill mempry)指令向内存范围中写入指定的样式,后续重复该操作直到内存范围被全部填满。
  • +
  • c(比较内存compare memory)指令比较两个内存范围的内容;
  • +
  • s(搜索内存search memory)指令在一个内存范围内搜索指定格式的内容、或者搜索内存范围内的任一ascii或unicode编码;
  • +
  • .holdmem(保存并比较内存hold and compare memory)指令将一个内存范围与另外一个进行比较。
  • +
+

在大多数场景下,这些命令以当前的进制解释其参数。因此,如果当前的进制不是16,那么需要在16进制地址前增加0x表示其为16进制。然而,命令的展示输出通常是16进制的,不按照当前进制进行展示。内存窗口以10进制展示了整数与实数以16进制展示其他类型的参数。

+

可使用n(设置数字基数set number base)指令改变默认的进制。为快速将数字由一个进制转换为另外一个进制,可使用?(计算表达式evaluate expression)指令或者.format(展示数字格式show number formats)指令。

+

当进行用户模式调试时,虚拟地址的含义是当前进程决定的。当你进行内核模式调试时,虚拟地址的含义可以被调试器控制。更多内容,可参考进程上下文(process context)章节。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2023/07/24/openssl_env_variable_openssl_ia32cap/index.html b/2023/07/24/openssl_env_variable_openssl_ia32cap/index.html new file mode 100644 index 0000000..fd20f48 --- /dev/null +++ b/2023/07/24/openssl_env_variable_openssl_ia32cap/index.html @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 【翻译】OpenSSL环境变量OPENSSL_ia32cap详解 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ 【翻译】OpenSSL环境变量OPENSSL_ia32cap详解 +

+ + +
+ + + + +
+ + +

1 名称

OPENSSL_ia32cap:x86[_64]架构处理器能力向量(processor capabilities vector)

+ + +

2 概要

env OPENSSL_ia32cap=... <application>
+
+

3 描述

OpenSSL支持一系列的x86[_64]指令集扩展。在以EAX=1作为输入值运行CPUID指令之后,这些扩展由处理器返回的位于EDX:ECX寄存器对能力向量的各个位表示(详情可参考Intel Application Note #241618)。这些向量在工具包初始化时被复制到内存中,被用来选择不同的代码路径以在一系列处理器之间提供最佳性能。在撰写本文时,以下位很重要:

+
    +
  • 第4位:表示时间戳计数器(Time-Stamp Counter)的存在;
  • +
  • 第19位:表示CLFLUSH(flush cache line缓存线清除操作码指令)指令可用;
  • +
  • 第20位:由Intel保留,用于在RC4代码路径中进行选择;
  • +
  • 第23位:表示MMX(Multi Media eXtension,多媒体扩展指令集)支持;
  • +
  • 第24位:FXSR(FidelityFX™ Super Resolution)位,表示支持XMM寄存器;
  • +
  • 第25位:表示支持SSE(streaming simd extensions流式单指令多数据扩展);
  • +
  • 第26位:表示支持SSE2;
  • +
  • 第28位:支持超线程(Hyperthreading),用于区分具有共享缓存的核心;
  • +
  • 第30位:由 Intel 保留,特指 Intel CPU;
  • +
  • 第33位:表明可以使用PCLMULQDQ(Carry-Less Multiplication Quadword,是对两个GF(2^128)域上的多项式相乘)指令;
  • +
  • 第41位:表明支持SSSE3和补充SSE3;
  • +
  • 第43位:表明支持AMD XOP(非AMD cpu上强制设置为0);
  • +
  • 第54位:表明支持MOVBE(复制源操作数的数据,交换字节后,移动数据)指令;
  • +
  • 第57位:表明支持AES-NI指令集(高级加密标准指令集,或称英特尔高级加密标准新指令,目的是改进应用程序使用高级加密标准(AES)执行加密和解密的速度)扩展;
  • +
  • 第58位:XSAVE位,缺少该位与MOVBE结合用于识别Atom Silvermont 核心;
  • +
  • 第59位:OSXSAVE位,表明支持YMM寄存器;
  • +
  • 第60位:表明支持AVX(X86指令集的SSE延伸架构)扩展;
  • +
  • 第62位:表明支持RDRAND(用于从芯片上的硬件随机数生成器中获取随机数)指令;
  • +
+

例如,在32位应用程序上下文环境中将第26位清0,则在运行时会禁用crypto库里的高性能SSE2代码,将第24位清0将会禁用SSE2代码操作128位MMX寄存器组。如果目标OpenSSL应用程序运行在SSE2兼容的cpu上,但是操作系统却没有启用XMM寄存器,则必须执行后者将第24位清0。一般情况下,功能向量的地址通过OPENSSL_ia32cap_loc()函数暴露给应用程序,但并非全部情况下都是如此。现在唯一可以影响功能检测的方法就是在目标程序启动前,设置OPENSSL_ia32cap环境变量。例如,在Intel P4处理器中,设置env OPENSSL_ia32cap=0x16980010 apps/openssl,或者设置env OPENSSL_ia32cap=~0x1000000 apps/openssl都可以取得预期的效果。也可以重新配置no-sse2选项,并重新编译工具包。

+

不太直观的就是将第28位清零,或者在环境变量中设置为~0x10000000。事实是,它不是从CPUID输出逐字复制的,而是经过调整以反映数据缓存是否实际上在逻辑核心之间共享。这反过来又会影响是否应用针对缓存定时攻击的昂贵对策的决定,尤其是在AES汇编器模块中。

+

通过以EAX=7和ECX=0作为输入值获取CPUID返回的EBX数值,功能向量可以进一步扩展。下面的位很重要:

+
    +
  • 第64+3位:表明支持BMI1(Bit Manipulation Instructions位操作指令)指令,例如ANDN(第一源操作数取反后与第二源操作数按位与操作,结果保存在目标操作数中);
  • +
  • 第64+5位:表明支持AVX2指令;
  • +
  • 第64+8位:表明支持BMI2指令,如MULX和RORX;
  • +
  • 第64+16位:表明支持AVX512F扩展;
  • +
  • 第64+17位:表明支持AVX512DQ扩展;
  • +
  • 第64+18位:表明支持RDSEED指令;
  • +
  • 第64+19位:表明支持ADCX和ADOX指令;
  • +
  • 第64+21位:表明支持VPMADD52[LH]UQ指令,又名AVX512IFMA扩展;
  • +
  • 第64+29位:表明支持SHA扩展;
  • +
  • 第64+30位:表明支持AVX512BW扩展;
  • +
  • 第64+31位:表明支持AVX512VL扩展;
  • +
  • 第64+41位:表明支持VAES扩展;
  • +
  • 第64+42位:表明支持VPCLMULQDQ扩展;
  • +
+

要控制此扩展功能,请在设置OPENSSL_ia32cap环境变量时使用:作为分隔符。例如,分配:~0x20将禁用AVX2代码路径,而:0-禁用所有后AVX扩展。

+

4 返回值

不可用

+

原文地址:https://www.openssl.org/docs/man3.1/man3/OPENSSL_ia32cap.html

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2023/08/03/windbg_debug_technique_reading_and_writing_memory_2/index.html b/2023/08/03/windbg_debug_technique_reading_and_writing_memory_2/index.html new file mode 100644 index 0000000..a088ae4 --- /dev/null +++ b/2023/08/03/windbg_debug_technique_reading_and_writing_memory_2/index.html @@ -0,0 +1,471 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + windbg标准调试技巧-读写内存(2):通过物理地址访问内存 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ windbg标准调试技巧-读写内存(2):通过物理地址访问内存 +

+ + +
+ + + + +
+ + +

为了从物理地址中读取参数,可以使用!db、!dc、!dd、!dp、!du和!dw等扩展命令。

+

向物理地址中写入数据,可以使用!eb和!ed扩展命令。

+

fp(fill physical memory填充物理内存)指令向物理内存范围内写入了模板值,不停重复,知道内存被完全填充。

+

当在内核模式中使用windbg时,可以在windbg的内存窗口中直接进行物理内存的读写操作。

+

要在物理内存中搜索一段数据或一系列数据,请使用 !search 扩展命令。

+

想要查看更多与物理地址相关的信息,可以查看转换虚拟地址到物理地址这一章节。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2023/08/04/windbg_debug_technique_reading_and_writing_memory_3/index.html b/2023/08/04/windbg_debug_technique_reading_and_writing_memory_3/index.html new file mode 100644 index 0000000..08f31e6 --- /dev/null +++ b/2023/08/04/windbg_debug_technique_reading_and_writing_memory_3/index.html @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + windbg标准调试技巧-读写内存(3):访问全局变量 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ windbg标准调试技巧-读写内存(3):访问全局变量 +

+ + +
+ + + + +
+ + +

全局变量的名称存储在应用程序编译时创建的符号文件中。调试器将全局变量的名称解释为一个虚拟地址。任何接受地址作为参数的命令都可以接受变量名称作为参数。因此,可以使用在之前的《通过虚拟地址访问内存》章节中描述的全部指令读写全局变量。

+ + +

除此之外,也可以使用?(计算表达式evaluate expression)指令展示与符号相关的地址。

+

Visual Studio和WinDbg提供了使用者可以使用(附加到命令上)的用户接口元素查看和编辑全局变量。可以参考《在Visual Studio查看和编辑内存及寄存器》和《在WinDbg查看和编辑全局变量》两章。

+

思考下面的例子。假设用户想要检查一个32位整数类型的全局变量MyCounter,同时假设默认进制是10进制。

+

也可以获取变量地址并展示如下:

+
 0:000> ? MyCounter 
+Evaluate expression: 1244892 = 0012fedc
+0:000> dd 0x0012fedc L1 
+0012fedc  00000052
+
+

第一条命令输出告诉使用者MyCounter的地址是0x0012FEDC。也可以使用d*(展示内存display memory)命令在这个地址上展示一个双字(也可以使用1244892,即该地址的十进制版本,然而,多数c语言程序员更倾向于使用0x0012FEDC)。第二条指令告诉使用者MyCounter的数值是0x52(即十进制下的82).

+

也可以使用如下指令实现上述过程

+
0:000> dd MyCounter L1 
+0012fedc  00000052
+
+

可使用如下指令将MyCounter的数值改为十进制的83.

+
0:000> ed MyCounter 83 
+
+

这个示例使用了十进制输入,因为十进制格式对于一个整数来说更加自然。
然而,d*命令的输出仍然是以16进制格式展示的。

+
0:000> dd MyCounter L1 
+0012fedc  00000053
+
+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2023/08/07/windbg_debug_technique_reading_and_writing_memory_4/index.html b/2023/08/07/windbg_debug_technique_reading_and_writing_memory_4/index.html new file mode 100644 index 0000000..5b54e71 --- /dev/null +++ b/2023/08/07/windbg_debug_technique_reading_and_writing_memory_4/index.html @@ -0,0 +1,471 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + windbg标准调试技巧-读写内存(4):访问局部变量 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ windbg标准调试技巧-读写内存(4):访问局部变量 +

+ + +
+ + + + +
+ + +

与全局变量相同,局部变量也存储于符号文件中。更加一致的是,调试器也将局部变量的名称解释为地址。可以安装与全局变量相同的方式进行读写操作。但是,如果需要向命令指定某个符号是本地符号,请在符号前面添加美元符号($)和感叹号(!),例如$!var。

+ + +

Visual Studio和WinDbg提供了使用者可以使用(附加到命令上)的用户接口元素查看和编辑全局变量。可以参考《在Visual Studio查看和编辑内存及寄存器》和《在WinDbg查看和编辑全局变量》两章。

+

除此之外,也可以使用如下的方式展示、修改及使用局部变量:

+
    +
  • dv(展示局部变量display local variable)命令展示了全部局部变量的名称和数值。
  • +
  • !for_each_local扩展使得使用者可以重复运行一条指令,每一个针对一个局部变量。
  • +
+

然而,在局部变量与全局变量之间还存在着一个主要的不同之处。应用程序运行时,局部变量的含义依赖于程序计数器的位置,因为局部变量的作用于仅仅局限于被定义的函数内部。

+

调试器依据局部上下文(local context)环境解释局部变量。默认情况下,该上下文与程序计数器的位置相匹配。但是调试器也可以改变上下文。想获取更多与局部上下文相关的信息可以参考《局部上下文》章节。

+

当局部上下文环境改变后,局部窗口(local window)立即更新以反应局部变量的新集合。dv命令也展示了新的变量。所有的这些变量名称通过之前描述的内存指令被正确解释。用户可以读写这些变量。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2023/08/07/windbg_debug_technique_reading_and_writing_memory_5/index.html b/2023/08/07/windbg_debug_technique_reading_and_writing_memory_5/index.html new file mode 100644 index 0000000..9668c4f --- /dev/null +++ b/2023/08/07/windbg_debug_technique_reading_and_writing_memory_5/index.html @@ -0,0 +1,583 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + windbg标准调试技巧-读写内存(5):将虚拟地址转换为物理地址 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ + + + + +
+

+ windbg标准调试技巧-读写内存(5):将虚拟地址转换为物理地址 +

+ + +
+ + + + +
+ + +

大多数调试器命令都是用虚拟地址而非物理地址作为输出及输出参数。然而,也同时存在物理地址起作用的场景。

+ + +

这里有两种方式将虚拟地址转换为物理地址:使用!vtop扩展或使用!pte扩展。

+

#1 使用!vtop进行地址转换

+
    +
  1. 确保在16禁止之下进行操作。如果必要的话,通过N 16指令设置当前环境为16进制;

    +
  2. +
  3. 确定地址的字节索引。该数字等同于虚拟地址的低12位。因此,虚拟地址0x0012f980的字节索引为0x980;

    +
  4. +
  5. 通过使用!process扩展确定地址的字典进制:

    +
     kd> !process 0 0
    + **** NT ACTIVE PROCESS DUMP ****
    + ....
    + PROCESS ff779190  SessionId: 0  Cid: 04fcPeb: 7ffdf000  ParentCid: 0394
    + DirBase: 098fd000  ObjectTable: e1646b30 TableSize:   8.
    + Image: MyApp.exe
    +
    +
  6. +
  7. 决定目录基址的页框号。这只是没有三个尾随十六进制零的目录基址。在本例中,目录基址为0x098FD000,因此页框号为0x098FD。

    +
  8. +
  9. 使用!vtop扩展。该扩展的第一个参数是页框号。!vtop的第二个参数就是问题中的虚拟地址:

    +

    kd> !vtop 98fd 12f980
    Pdi 0 Pti 12f
    0012f980 09de9000 pfn(09de9)

    +
  10. +
+

最后一行中展示的第二个数字是物理页的起始物理地址。

+
    +
  1. 在页的开始出加上地址的字节索引:0x09DE9000 + 0x980 = 0x09DE9980。这就是目标物理地址。
  2. +
+

也可以通过显示每个地址的内存来验证此计算是否正确完成。!d*扩展显示指定物理地址处的内存:

+
kd> !dc 9de9980
+# 9de9980 6d206e49 726f6d65 00120079 0012f9f4 In memory.......
+# 9de9990 0012f9f8 77e57119 77e8e618 ffffffff .....q.w...w....
+# 9de99a0 77e727e0 77f6f13e 77f747e0 ffffffff .'.w>..w.G.w....
+# 9de99b0 .....
+
+

d*(展示内存)质量使用虚拟地址作为其参数:

+
kd> dc 12f980
+0012f980  6d206e49 726f6d65 00120079 0012f9f4  In memory.......
+0012f990  0012f9f8 77e57119 77e8e618 ffffffff  .....q.w...w....
+0012f9a0  77e727e0 77f6f13e 77f747e0 ffffffff  .'.w>..w.G.w....
+0012f9b0  .....
+
+

因为结果相同,这就表明物理地址0x09DE9980确实代表了虚拟地址0x0012F980。

+

#2 使用!pte进行地址转换

+

假设客户正在调查属于MyApp.exe进程的虚拟地址0x0012F980。在使用!pte扩展指令获取其对应的物理地址过程中,操作如下:

+
    +
  1. 确保子啊16进制下进行运算。如果有必要,通过N 16指令设置当前环境为16进制;

    +
  2. +
  3. 获取地址的字节索引。该数字等同于虚拟地址的低12位。因此,虚拟地址0x0012f980的字节索引为0x980;

    +
  4. +
  5. 将进程上下文环境设置到目标进程中:

    +
     kd> !process 0 0
    + **** NT ACTIVE PROCESS DUMP ****
    + ....
    + PROCESS ff779190  SessionId: 0  Cid: 04fcPeb: 7ffdf000  ParentCid: 0394
    + DirBase: 098fd000  ObjectTable: e1646b30  TableSize:   8.
    + Image: MyApp.exe
    + 
    + kd> .process /p ff779190
    + Implicit process is now ff779190
    + .cache forcedecodeuser done
    +
    +
  6. +
  7. 使用!pte指令时以虚拟地址作为参数。输出信息以两列形式展示出来。左边的一列描述了地址对应的页目录条目(page directory entry,pe),右边列展示了页表条目(page table entry,pte):

    +
     kd> !pte 12f980
    +    VA 0012f980
    + PDE at   C0300000PTE at C00004BC
    + contains 0BA58067  contains 09DE9067
    + pfn ba58 ---DA--UWVpfn 9de9 ---DA--UWV
    +
    +
  8. +
  9. 查看右边列的最后一行。符号”pfn 9de9”出现了。pte的页框号(page frame number,pfn)是0x9de9.页框号乘以0x1000(例如,左移12位)。结果0x09DE9000就是内存也的起始物理地址;

    +
  10. +
  11. 在页的开始出加上地址的字节索引:0x09DE9000 + 0x980 = 0x09DE9980。这就是目标物理地址;

    +
  12. +
+

与之前的方法得到了相同的结果。

+

#3 手动进行地址转换

+

尽管!ptov和pte指令提供了将虚拟地址转换为物理地址的最快方式,但是也可以人工完成这一转换过程。对该过程的描述将阐明虚拟内存体系结构的一些细节。

+

内存结构因其处理器和硬件配置的不同而会在大小方面发生变化。例子来源于一个没有启用物理地址扩展(physical address extension,pae)功能的x86系统。

+

使用0x0012F980作为虚拟地址,首先需要将该地址转换为2进制,可以手动转换,也可以使用.formats(dhow number formats,展示数字格式)指令实现:

+
kd> .formats 12f980
+Evaluate expression:
+  Hex:     0012f980
+  Decimal: 1243520
+  Octal:   00004574600
+  Binary:  00000000 00010010 11111001 10000000
+  Chars:   ....
+  Time:    Thu Jan 15 01:25:20 1970
+  Float:   low 1.74254e-039 high 0
+  Double:  6.14381e-318
+
+

虚拟地址有3个字段组成。第0位到第11位是字节索引。第12位到第21位是页表索引。第22位到第31位是页目录索引。将对应字段进行拆分,实现如下:

+
0x0012F980  =  0y  00000000 00   010010 1111   1001 10000000
+
+

导出虚拟地址的3个字段:

+
    +
  • 页目录索引=0y0000000000=0x0
  • +
  • 页表索引=0y0100101111=0x12F
  • +
  • 字节索引=0y100110000000=0x980
  • +
+

之后系统需要3个额外的信息:

+
    +
  • 每一个pte的大小。在非pae x86系统中是4个字节。
  • +
  • 页大小。是0x1000字节。
  • +
  • PTE_BASE虚拟地址。在非pae系统中,是0xC0000000.
  • +
+

使用这些数据,可以计算pte自身的地址:

+
PTE address   =   PTE_BASE  
+                + (page directory index) * PAGE_SIZE
+                + (page table index) * sizeof(MMPTE)
+  			  =   0xc0000000
+                + 0x0   * 0x1000
+                + 0x12F * 4
+              =   0xC00004BC
+
+

这就是pte的地址。pte是一个32位的双字变量。其内容如下:

+
kd> dd 0xc00004bc L1
+c00004bc  09de9067
+
+

pte数值是0x09DE9067。其由两个字段组成。

+
    +
  • pte的低12位是状态标志(status flags)。在这种情况下,这些标志位等于0x067–或者二进制的0y000001100111.对于状态标志位的解释,可以查看!pte指令参考页。
  • +
  • pte的高20位等于pte的页框号pfn。在这种情况下,pfn是0x09DE9.
  • +
+

物理页上的第一个物理地址是pfn乘以0x1000(即左移12位)。字节索引就是页上的偏移。因此,查找的物理地址就是0x09DE9000+0x980=0x09DE9980。与之前的计算方式获取的结果一致。

+ +
+ + + + + + + +
+ + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..37322df --- /dev/null +++ b/about/index.html @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 关于 | 怀德维宁 + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + + + +
+ + + + + +
+
+ +

关于 +

+ + + +
+ + + + +
+ +
+ + + +
+ + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2019/12/index.html b/archives/2019/12/index.html new file mode 100644 index 0000000..b3f9e85 --- /dev/null +++ b/archives/2019/12/index.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2019 +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2019/index.html b/archives/2019/index.html new file mode 100644 index 0000000..41f55ca --- /dev/null +++ b/archives/2019/index.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2019 +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2020/10/index.html b/archives/2020/10/index.html new file mode 100644 index 0000000..786942e --- /dev/null +++ b/archives/2020/10/index.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2020 +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2020/12/index.html b/archives/2020/12/index.html new file mode 100644 index 0000000..9ef6404 --- /dev/null +++ b/archives/2020/12/index.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2020 +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2020/index.html b/archives/2020/index.html new file mode 100644 index 0000000..8383fbb --- /dev/null +++ b/archives/2020/index.html @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2020 +
+ + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/08/index.html b/archives/2021/08/index.html new file mode 100644 index 0000000..c0a5407 --- /dev/null +++ b/archives/2021/08/index.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2021 +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/11/index.html b/archives/2021/11/index.html new file mode 100644 index 0000000..cbcae4c --- /dev/null +++ b/archives/2021/11/index.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2021 +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/index.html b/archives/2021/index.html new file mode 100644 index 0000000..fdf0db6 --- /dev/null +++ b/archives/2021/index.html @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2021 +
+ + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/08/index.html b/archives/2022/08/index.html new file mode 100644 index 0000000..c02992d --- /dev/null +++ b/archives/2022/08/index.html @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2022 +
+ + + + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/09/index.html b/archives/2022/09/index.html new file mode 100644 index 0000000..28aa76a --- /dev/null +++ b/archives/2022/09/index.html @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2022 +
+ + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/10/index.html b/archives/2022/10/index.html new file mode 100644 index 0000000..2abed57 --- /dev/null +++ b/archives/2022/10/index.html @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2022 +
+ + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/11/index.html b/archives/2022/11/index.html new file mode 100644 index 0000000..1d1240d --- /dev/null +++ b/archives/2022/11/index.html @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + + + + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/index.html b/archives/2022/index.html new file mode 100644 index 0000000..8709c48 --- /dev/null +++ b/archives/2022/index.html @@ -0,0 +1,572 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + + + + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/2/index.html b/archives/2022/page/2/index.html new file mode 100644 index 0000000..9f93ea8 --- /dev/null +++ b/archives/2022/page/2/index.html @@ -0,0 +1,392 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2022 +
+ + + + +
+
+ + + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2023/07/index.html b/archives/2023/07/index.html new file mode 100644 index 0000000..11e5658 --- /dev/null +++ b/archives/2023/07/index.html @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2023 +
+ + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2023/08/index.html b/archives/2023/08/index.html new file mode 100644 index 0000000..ae7fa06 --- /dev/null +++ b/archives/2023/08/index.html @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2023 +
+ + + + + + + + + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2023/index.html b/archives/2023/index.html new file mode 100644 index 0000000..81f0aed --- /dev/null +++ b/archives/2023/index.html @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ +
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..2e1428e --- /dev/null +++ b/archives/index.html @@ -0,0 +1,575 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + + + + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/2/index.html b/archives/page/2/index.html new file mode 100644 index 0000000..759070e --- /dev/null +++ b/archives/page/2/index.html @@ -0,0 +1,578 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2022 +
+ + + + + + + + + + + + + + +
+ 2021 +
+ + + + +
+ 2020 +
+ + + + +
+
+ + + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/3/index.html b/archives/page/3/index.html new file mode 100644 index 0000000..e795146 --- /dev/null +++ b/archives/page/3/index.html @@ -0,0 +1,415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+ 嗯..! 目前共计 22 篇日志。 继续努力。 +
+ + +
+ 2020 +
+ + +
+ 2019 +
+ + + + +
+
+ + + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/atom.xml b/atom.xml new file mode 100644 index 0000000..8de527f --- /dev/null +++ b/atom.xml @@ -0,0 +1,601 @@ + + + 怀德维宁 + + 大邦维屏,大宗维翰。怀德维宁,宗子维城。 + + + + 2023-12-29T02:38:41.469Z + http://example.com/ + + + 杨维宁 + + + + Hexo + + + windbg标准调试技巧-读写内存(5):将虚拟地址转换为物理地址 + + http://example.com/2023/08/07/windbg_debug_technique_reading_and_writing_memory_5/ + 2023-08-07T10:30:00.000Z + 2023-12-29T02:38:41.469Z + + 大多数调试器命令都是用虚拟地址而非物理地址作为输出及输出参数。然而,也同时存在物理地址起作用的场景。

这里有两种方式将虚拟地址转换为物理地址:使用!vtop扩展或使用!pte扩展。

#1 使用!vtop进行地址转换

  1. 确保在16禁止之下进行操作。如果必要的话,通过N 16指令设置当前环境为16进制;

  2. 确定地址的字节索引。该数字等同于虚拟地址的低12位。因此,虚拟地址0x0012f980的字节索引为0x980;

  3. 通过使用!process扩展确定地址的字典进制:

     kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** .... PROCESS ff779190  SessionId: 0  Cid: 04fcPeb: 7ffdf000  ParentCid: 0394 DirBase: 098fd000  ObjectTable: e1646b30 TableSize:   8. Image: MyApp.exe
  4. 决定目录基址的页框号。这只是没有三个尾随十六进制零的目录基址。在本例中,目录基址为0x098FD000,因此页框号为0x098FD。

  5. 使用!vtop扩展。该扩展的第一个参数是页框号。!vtop的第二个参数就是问题中的虚拟地址:

    kd> !vtop 98fd 12f980
    Pdi 0 Pti 12f
    0012f980 09de9000 pfn(09de9)

最后一行中展示的第二个数字是物理页的起始物理地址。

  1. 在页的开始出加上地址的字节索引:0x09DE9000 + 0x980 = 0x09DE9980。这就是目标物理地址。

也可以通过显示每个地址的内存来验证此计算是否正确完成。!d*扩展显示指定物理地址处的内存:

kd> !dc 9de9980# 9de9980 6d206e49 726f6d65 00120079 0012f9f4 In memory.......# 9de9990 0012f9f8 77e57119 77e8e618 ffffffff .....q.w...w....# 9de99a0 77e727e0 77f6f13e 77f747e0 ffffffff .'.w>..w.G.w....# 9de99b0 .....

d*(展示内存)质量使用虚拟地址作为其参数:

kd> dc 12f9800012f980  6d206e49 726f6d65 00120079 0012f9f4  In memory.......0012f990  0012f9f8 77e57119 77e8e618 ffffffff  .....q.w...w....0012f9a0  77e727e0 77f6f13e 77f747e0 ffffffff  .'.w>..w.G.w....0012f9b0  .....

因为结果相同,这就表明物理地址0x09DE9980确实代表了虚拟地址0x0012F980。

#2 使用!pte进行地址转换

假设客户正在调查属于MyApp.exe进程的虚拟地址0x0012F980。在使用!pte扩展指令获取其对应的物理地址过程中,操作如下:

  1. 确保子啊16进制下进行运算。如果有必要,通过N 16指令设置当前环境为16进制;

  2. 获取地址的字节索引。该数字等同于虚拟地址的低12位。因此,虚拟地址0x0012f980的字节索引为0x980;

  3. 将进程上下文环境设置到目标进程中:

     kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** .... PROCESS ff779190  SessionId: 0  Cid: 04fcPeb: 7ffdf000  ParentCid: 0394 DirBase: 098fd000  ObjectTable: e1646b30  TableSize:   8. Image: MyApp.exe  kd> .process /p ff779190 Implicit process is now ff779190 .cache forcedecodeuser done
  4. 使用!pte指令时以虚拟地址作为参数。输出信息以两列形式展示出来。左边的一列描述了地址对应的页目录条目(page directory entry,pe),右边列展示了页表条目(page table entry,pte):

     kd> !pte 12f980    VA 0012f980 PDE at   C0300000PTE at C00004BC contains 0BA58067  contains 09DE9067 pfn ba58 ---DA--UWVpfn 9de9 ---DA--UWV
  5. 查看右边列的最后一行。符号”pfn 9de9”出现了。pte的页框号(page frame number,pfn)是0x9de9.页框号乘以0x1000(例如,左移12位)。结果0x09DE9000就是内存也的起始物理地址;

  6. 在页的开始出加上地址的字节索引:0x09DE9000 + 0x980 = 0x09DE9980。这就是目标物理地址;

与之前的方法得到了相同的结果。

#3 手动进行地址转换

尽管!ptov和pte指令提供了将虚拟地址转换为物理地址的最快方式,但是也可以人工完成这一转换过程。对该过程的描述将阐明虚拟内存体系结构的一些细节。

内存结构因其处理器和硬件配置的不同而会在大小方面发生变化。例子来源于一个没有启用物理地址扩展(physical address extension,pae)功能的x86系统。

使用0x0012F980作为虚拟地址,首先需要将该地址转换为2进制,可以手动转换,也可以使用.formats(dhow number formats,展示数字格式)指令实现:

kd> .formats 12f980Evaluate expression:  Hex:     0012f980  Decimal: 1243520  Octal:   00004574600  Binary:  00000000 00010010 11111001 10000000  Chars:   ....  Time:    Thu Jan 15 01:25:20 1970  Float:   low 1.74254e-039 high 0  Double:  6.14381e-318

虚拟地址有3个字段组成。第0位到第11位是字节索引。第12位到第21位是页表索引。第22位到第31位是页目录索引。将对应字段进行拆分,实现如下:

0x0012F980  =  0y  00000000 00   010010 1111   1001 10000000

导出虚拟地址的3个字段:

  • 页目录索引=0y0000000000=0x0
  • 页表索引=0y0100101111=0x12F
  • 字节索引=0y100110000000=0x980

之后系统需要3个额外的信息:

  • 每一个pte的大小。在非pae x86系统中是4个字节。
  • 页大小。是0x1000字节。
  • PTE_BASE虚拟地址。在非pae系统中,是0xC0000000.

使用这些数据,可以计算pte自身的地址:

PTE address   =   PTE_BASE                  + (page directory index) * PAGE_SIZE                + (page table index) * sizeof(MMPTE)    =   0xc0000000                + 0x0   * 0x1000                + 0x12F * 4              =   0xC00004BC

这就是pte的地址。pte是一个32位的双字变量。其内容如下:

kd> dd 0xc00004bc L1c00004bc  09de9067

pte数值是0x09DE9067。其由两个字段组成。

  • pte的低12位是状态标志(status flags)。在这种情况下,这些标志位等于0x067–或者二进制的0y000001100111.对于状态标志位的解释,可以查看!pte指令参考页。
  • pte的高20位等于pte的页框号pfn。在这种情况下,pfn是0x09DE9.

物理页上的第一个物理地址是pfn乘以0x1000(即左移12位)。字节索引就是页上的偏移。因此,查找的物理地址就是0x09DE9000+0x980=0x09DE9980。与之前的计算方式获取的结果一致。

]]>
+ + + <p>大多数调试器命令都是用虚拟地址而非物理地址作为输出及输出参数。然而,也同时存在物理地址起作用的场景。</p> + + + + + + + + + + + + +
+ + + windbg标准调试技巧-读写内存(4):访问局部变量 + + http://example.com/2023/08/07/windbg_debug_technique_reading_and_writing_memory_4/ + 2023-08-07T10:20:00.000Z + 2023-12-29T02:38:41.453Z + + 与全局变量相同,局部变量也存储于符号文件中。更加一致的是,调试器也将局部变量的名称解释为地址。可以安装与全局变量相同的方式进行读写操作。但是,如果需要向命令指定某个符号是本地符号,请在符号前面添加美元符号($)和感叹号(!),例如$!var。

Visual Studio和WinDbg提供了使用者可以使用(附加到命令上)的用户接口元素查看和编辑全局变量。可以参考《在Visual Studio查看和编辑内存及寄存器》和《在WinDbg查看和编辑全局变量》两章。

除此之外,也可以使用如下的方式展示、修改及使用局部变量:

  • dv(展示局部变量display local variable)命令展示了全部局部变量的名称和数值。
  • !for_each_local扩展使得使用者可以重复运行一条指令,每一个针对一个局部变量。

然而,在局部变量与全局变量之间还存在着一个主要的不同之处。应用程序运行时,局部变量的含义依赖于程序计数器的位置,因为局部变量的作用于仅仅局限于被定义的函数内部。

调试器依据局部上下文(local context)环境解释局部变量。默认情况下,该上下文与程序计数器的位置相匹配。但是调试器也可以改变上下文。想获取更多与局部上下文相关的信息可以参考《局部上下文》章节。

当局部上下文环境改变后,局部窗口(local window)立即更新以反应局部变量的新集合。dv命令也展示了新的变量。所有的这些变量名称通过之前描述的内存指令被正确解释。用户可以读写这些变量。

]]>
+ + + <p>与全局变量相同,局部变量也存储于符号文件中。更加一致的是,调试器也将局部变量的名称解释为地址。可以安装与全局变量相同的方式进行读写操作。但是,如果需要向命令指定某个符号是本地符号,请在符号前面添加美元符号($)和感叹号(!),例如$!var。</p> + + + + + + + + + + + + +
+ + + windbg标准调试技巧-读写内存(3):访问全局变量 + + http://example.com/2023/08/04/windbg_debug_technique_reading_and_writing_memory_3/ + 2023-08-04T11:30:00.000Z + 2023-12-29T02:38:41.453Z + + 全局变量的名称存储在应用程序编译时创建的符号文件中。调试器将全局变量的名称解释为一个虚拟地址。任何接受地址作为参数的命令都可以接受变量名称作为参数。因此,可以使用在之前的《通过虚拟地址访问内存》章节中描述的全部指令读写全局变量。

除此之外,也可以使用?(计算表达式evaluate expression)指令展示与符号相关的地址。

Visual Studio和WinDbg提供了使用者可以使用(附加到命令上)的用户接口元素查看和编辑全局变量。可以参考《在Visual Studio查看和编辑内存及寄存器》和《在WinDbg查看和编辑全局变量》两章。

思考下面的例子。假设用户想要检查一个32位整数类型的全局变量MyCounter,同时假设默认进制是10进制。

也可以获取变量地址并展示如下:

 0:000> ? MyCounter Evaluate expression: 1244892 = 0012fedc0:000> dd 0x0012fedc L1 0012fedc  00000052

第一条命令输出告诉使用者MyCounter的地址是0x0012FEDC。也可以使用d*(展示内存display memory)命令在这个地址上展示一个双字(也可以使用1244892,即该地址的十进制版本,然而,多数c语言程序员更倾向于使用0x0012FEDC)。第二条指令告诉使用者MyCounter的数值是0x52(即十进制下的82).

也可以使用如下指令实现上述过程

0:000> dd MyCounter L1 0012fedc  00000052

可使用如下指令将MyCounter的数值改为十进制的83.

0:000> ed MyCounter 83 

这个示例使用了十进制输入,因为十进制格式对于一个整数来说更加自然。
然而,d*命令的输出仍然是以16进制格式展示的。

0:000> dd MyCounter L1 0012fedc  00000053
]]>
+ + + <p>全局变量的名称存储在应用程序编译时创建的符号文件中。调试器将全局变量的名称解释为一个虚拟地址。任何接受地址作为参数的命令都可以接受变量名称作为参数。因此,可以使用在之前的《通过虚拟地址访问内存》章节中描述的全部指令读写全局变量。</p> + + + + + + + + + + + + +
+ + + windbg标准调试技巧-读写内存(2):通过物理地址访问内存 + + http://example.com/2023/08/03/windbg_debug_technique_reading_and_writing_memory_2/ + 2023-08-03T12:30:00.000Z + 2023-12-29T02:38:41.453Z + + 为了从物理地址中读取参数,可以使用!db、!dc、!dd、!dp、!du和!dw等扩展命令。

向物理地址中写入数据,可以使用!eb和!ed扩展命令。

fp(fill physical memory填充物理内存)指令向物理内存范围内写入了模板值,不停重复,知道内存被完全填充。

当在内核模式中使用windbg时,可以在windbg的内存窗口中直接进行物理内存的读写操作。

要在物理内存中搜索一段数据或一系列数据,请使用 !search 扩展命令。

想要查看更多与物理地址相关的信息,可以查看转换虚拟地址到物理地址这一章节。

]]>
+ + + + + <p>为了从物理地址中读取参数,可以使用!db、!dc、!dd、!dp、!du和!dw等扩展命令。</p> +<p>向物理地址中写入数据,可以使用!eb和!ed扩展命令。</p> +<p>fp(fill physical memory填充物理内存)指令向物理内存范围内写入了模板值,不 + + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】OpenSSL环境变量OPENSSL_ia32cap详解 + + http://example.com/2023/07/24/openssl_env_variable_openssl_ia32cap/ + 2023-07-24T03:30:10.000Z + 2023-12-29T02:38:41.453Z + + 1 名称

OPENSSL_ia32cap:x86[_64]架构处理器能力向量(processor capabilities vector)

2 概要

env OPENSSL_ia32cap=... <application>

3 描述

OpenSSL支持一系列的x86[_64]指令集扩展。在以EAX=1作为输入值运行CPUID指令之后,这些扩展由处理器返回的位于EDX:ECX寄存器对能力向量的各个位表示(详情可参考Intel Application Note #241618)。这些向量在工具包初始化时被复制到内存中,被用来选择不同的代码路径以在一系列处理器之间提供最佳性能。在撰写本文时,以下位很重要:

  • 第4位:表示时间戳计数器(Time-Stamp Counter)的存在;
  • 第19位:表示CLFLUSH(flush cache line缓存线清除操作码指令)指令可用;
  • 第20位:由Intel保留,用于在RC4代码路径中进行选择;
  • 第23位:表示MMX(Multi Media eXtension,多媒体扩展指令集)支持;
  • 第24位:FXSR(FidelityFX™ Super Resolution)位,表示支持XMM寄存器;
  • 第25位:表示支持SSE(streaming simd extensions流式单指令多数据扩展);
  • 第26位:表示支持SSE2;
  • 第28位:支持超线程(Hyperthreading),用于区分具有共享缓存的核心;
  • 第30位:由 Intel 保留,特指 Intel CPU;
  • 第33位:表明可以使用PCLMULQDQ(Carry-Less Multiplication Quadword,是对两个GF(2^128)域上的多项式相乘)指令;
  • 第41位:表明支持SSSE3和补充SSE3;
  • 第43位:表明支持AMD XOP(非AMD cpu上强制设置为0);
  • 第54位:表明支持MOVBE(复制源操作数的数据,交换字节后,移动数据)指令;
  • 第57位:表明支持AES-NI指令集(高级加密标准指令集,或称英特尔高级加密标准新指令,目的是改进应用程序使用高级加密标准(AES)执行加密和解密的速度)扩展;
  • 第58位:XSAVE位,缺少该位与MOVBE结合用于识别Atom Silvermont 核心;
  • 第59位:OSXSAVE位,表明支持YMM寄存器;
  • 第60位:表明支持AVX(X86指令集的SSE延伸架构)扩展;
  • 第62位:表明支持RDRAND(用于从芯片上的硬件随机数生成器中获取随机数)指令;

例如,在32位应用程序上下文环境中将第26位清0,则在运行时会禁用crypto库里的高性能SSE2代码,将第24位清0将会禁用SSE2代码操作128位MMX寄存器组。如果目标OpenSSL应用程序运行在SSE2兼容的cpu上,但是操作系统却没有启用XMM寄存器,则必须执行后者将第24位清0。一般情况下,功能向量的地址通过OPENSSL_ia32cap_loc()函数暴露给应用程序,但并非全部情况下都是如此。现在唯一可以影响功能检测的方法就是在目标程序启动前,设置OPENSSL_ia32cap环境变量。例如,在Intel P4处理器中,设置env OPENSSL_ia32cap=0x16980010 apps/openssl,或者设置env OPENSSL_ia32cap=~0x1000000 apps/openssl都可以取得预期的效果。也可以重新配置no-sse2选项,并重新编译工具包。

不太直观的就是将第28位清零,或者在环境变量中设置为~0x10000000。事实是,它不是从CPUID输出逐字复制的,而是经过调整以反映数据缓存是否实际上在逻辑核心之间共享。这反过来又会影响是否应用针对缓存定时攻击的昂贵对策的决定,尤其是在AES汇编器模块中。

通过以EAX=7和ECX=0作为输入值获取CPUID返回的EBX数值,功能向量可以进一步扩展。下面的位很重要:

  • 第64+3位:表明支持BMI1(Bit Manipulation Instructions位操作指令)指令,例如ANDN(第一源操作数取反后与第二源操作数按位与操作,结果保存在目标操作数中);
  • 第64+5位:表明支持AVX2指令;
  • 第64+8位:表明支持BMI2指令,如MULX和RORX;
  • 第64+16位:表明支持AVX512F扩展;
  • 第64+17位:表明支持AVX512DQ扩展;
  • 第64+18位:表明支持RDSEED指令;
  • 第64+19位:表明支持ADCX和ADOX指令;
  • 第64+21位:表明支持VPMADD52[LH]UQ指令,又名AVX512IFMA扩展;
  • 第64+29位:表明支持SHA扩展;
  • 第64+30位:表明支持AVX512BW扩展;
  • 第64+31位:表明支持AVX512VL扩展;
  • 第64+41位:表明支持VAES扩展;
  • 第64+42位:表明支持VPCLMULQDQ扩展;

要控制此扩展功能,请在设置OPENSSL_ia32cap环境变量时使用:作为分隔符。例如,分配:~0x20将禁用AVX2代码路径,而:0-禁用所有后AVX扩展。

4 返回值

不可用

原文地址:https://www.openssl.org/docs/man3.1/man3/OPENSSL_ia32cap.html

]]>
+ + + <h1 id="1-名称"><a href="#1-名称" class="headerlink" title="1 名称"></a>1 名称</h1><p>OPENSSL_ia32cap:x86[_64]架构处理器能力向量(processor capabilities vector)</p> + + + + + + + + + + +
+ + + windbg标准调试技巧-读写内存(1):通过虚拟地址访问内存 + + http://example.com/2023/07/19/windbg_debug_technique_reading_and_writing_memory_1/ + 2023-07-19T11:30:00.000Z + 2023-12-29T02:38:41.453Z + + 在调试过程中可以通过使用多个指令来访问内存或内存区域。visual studio和windbg提供命令行命令时,也提供了用户图形界面,用户可以用图形界面来查看和编辑内存。详情可以参考windbg帮助文档中的在visual studio中查看和编辑内存和寄存器,以及在windbg中查看和编辑内存两个章节。

以下命令可以读取与写入多种格式的内存。这些屙屎包含了16字节、字格式(单字、双字、四字、八字格式)、整数格式(short、long、quad、unsigned格式)、浮点数格式(10字节、16字节、32字节、64字节实数格式)以及ascii字符格式。

  • d*(展示内存display memory)指令会展示特定内存或内驱区域的内容。
  • e*(输入数值enter values)指令向特定的内存地址写入数值。

也可以使用如下指令处理更加特定的数据类型:

  • dt(展示类型display type)指令会检索多种数据类型并展示被当前正在调试的应用程序锁创建出来的数据结构。改名了用途广泛并拥有多种变体及可选配置项。
  • ds(展示字符串display string)指令展示了STRING、ANSI_STRING和UNICODE_STRING数据结构。
  • dl(展示链表display linked list)指令追踪并展示链表。
  • d*s(展示字格式和符号display words and symbols)指令检索可能包含符号信息的双字或四字结构,之后展示对应的数据及符号信息。
  • !address扩展指令展示位于特定地址的内存属性信息。

可以使用如下指令来进行内存范围操作:

  • m(移动内存move memory)指令将一个内存范围的内容移到另外一个中。
  • f(填充内存fill mempry)指令向内存范围中写入指定的样式,后续重复该操作直到内存范围被全部填满。
  • c(比较内存compare memory)指令比较两个内存范围的内容;
  • s(搜索内存search memory)指令在一个内存范围内搜索指定格式的内容、或者搜索内存范围内的任一ascii或unicode编码;
  • .holdmem(保存并比较内存hold and compare memory)指令将一个内存范围与另外一个进行比较。

在大多数场景下,这些命令以当前的进制解释其参数。因此,如果当前的进制不是16,那么需要在16进制地址前增加0x表示其为16进制。然而,命令的展示输出通常是16进制的,不按照当前进制进行展示。内存窗口以10进制展示了整数与实数以16进制展示其他类型的参数。

可使用n(设置数字基数set number base)指令改变默认的进制。为快速将数字由一个进制转换为另外一个进制,可使用?(计算表达式evaluate expression)指令或者.format(展示数字格式show number formats)指令。

当进行用户模式调试时,虚拟地址的含义是当前进程决定的。当你进行内核模式调试时,虚拟地址的含义可以被调试器控制。更多内容,可参考进程上下文(process context)章节。

]]>
+ + + <p>在调试过程中可以通过使用多个指令来访问内存或内存区域。visual studio和windbg提供命令行命令时,也提供了用户图形界面,用户可以用图形界面来查看和编辑内存。详情可以参考windbg帮助文档中的在visual studio中查看和编辑内存和寄存器,以及在windbg中查看和编辑内存两个章节。</p> + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(9)CPU model and topology + + http://example.com/2022/11/15/domain_xml_format9_cpu_model_topology/ + 2022-11-15T10:10:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

NUMA Node Tuning

<domain>  ...  <numatune>    <memory mode="strict" nodeset="1-4,^3"/>    <memnode cellid="0" mode="strict" nodeset="1"/>    <memnode cellid="2" mode="preferred" nodeset="2"/>  </numatune>  ...</domain>

numatune

可选元素项numatune提供了如何通过控制虚拟机进程的NUMA策略对NUMA主机的性能进行调度。注意,仅支持QEMU。自0.9.3起。

memory

可选元素项memory指明了如何对NUMA主机的虚拟机进程进行内存分配。其包含多个可选属性项。属性项mode的可用值包含interleave、strict、preferred和restrictive,默认设置为strict。restrictive指明使用系统默认策略,仅有cgroups被用来限制内存节点,在memnode元素中将mode设置为restrictive。属性项nodeset指明了numa节点,其与vcpu元素项的cpuset属性项使用相同的语法。属性项placement(自0.9.12起)可被用来表明虚拟机进程的内存放置模式,其值可以为static或auto,是vcpu的placement的默认值,如果指定了nodeset则默认值为static。auto表明虚拟机进程只会从查询numad返回的咨询节点中集中分配内存,属性nodeset的值在指定auto的情况下将会被忽略。如果vcpu的placement设置为auto,且numatune未指定,则numatune的placement设置为auto,mode设置为strict。自0.9.3起。参阅 virDomainSetNumaParameters获取该元素的更多信息。

memnode

可选元素项memnode可以为每个虚拟机的numa节点指定内存分配策略。对于没有memnode元素的哪些节点,memory元素的默认值将会被使用。属性项cellid寻址应用设置的虚拟机NUMA节点。属性项mode和nodeset与memory元素中的对应值具有相同的含义和语法。设置与自动placement不兼容。qemu自1.2.7起。

Block I/O Tuning

<domain>  ...  <blkiotune>    <weight>800</weight>    <device>      <path>/dev/sda</path>      <weight>1000</weight>    </device>    <device>      <path>/dev/sdb</path>      <weight>500</weight>      <read_bytes_sec>10000</read_bytes_sec>      <write_bytes_sec>10000</write_bytes_sec>      <read_iops_sec>20000</read_iops_sec>      <write_iops_sec>20000</write_iops_sec>    </device>  </blkiotune>  ...</domain>

blkiotune

可选元素项blkiotune为虚拟机提供了调节Blkio cgroup可调参数的能力。如果忽略该设置项,则其默认由操作系统提供。自0.8.8起。

weight

可选元素项weight是虚拟机的全部i/o负载。该值的范围是100-1000.在2.6.39内核后,该值的范围是10-1000.

device

虚拟机可能又有多个device元素项,以便深入调节虚拟机正在使用的每一个主机块设备的权重。注意多个磁盘(可参阅Hard drives, floppy disks, CDROMs章节)可能共享同一个主机块设备,如果他们被相同的主机文件系统中的文件备份,这也是为什么使用全局作用域的调节参数而非相关的每一个虚拟机磁盘设备的原因(与磁盘定义中的iotune元素项相反(可参阅Hard drives, floppy disks, CDROMs章节),而iotune元素项适用于单个独立的磁盘)。每一个device元素项有两个强制的子元素项,path描述了设备的绝对路径,而weight给出了设备的相对权重,权重范围是100-1000.在2.6.39版本内核之后,该值的范围变为10-1000.自0.9.8起。除此之外,也可使用如下的可选子元素项:

read_bytes_sec

以字节为单位的每秒可读吞吐量。自1.2.2起。

write_bytes_sec

以字节为单位的每秒可写吞吐量。自1.2.2起。

read_iops_sec

每秒i/o读操作限制。自1.2.2起。

write_iops_sec

每秒i/o写操作限制。自1.2.2起。

Resource partitioning

虚拟机管理程序可能允许将虚拟机放入资源分区,也可能嵌套所述分区。resource元素将与资源分区相关的配置项组织在一起。当前其支持partition子元素项,该子元素项的内容定义了放置虚拟机的资源分区的绝对路径。如果没有列出任何分区,虚拟机将会被放到默认分区中。应用程序或管理员有责任确保分区在虚拟机启动之前就已存在。只有默认分区(适用于特定虚拟机管理程序)可以默认假定已经存在。

...<resource>  <partition>/virtualmachines/production</partition></resource>...

资源分区当前在qemu和lxc中受到支持,在所有已安装的控制器中将分区路径映射到cgroups目录。自1.0.5起。

Fibre Channel VMID

FC SAN可以提供多个依赖于VMID的QoS等级和访问控制功能。它还可以收集每个虚拟机的遥测数据,这些数据可用于增强虚拟机的IO性能。可以通过fibrechannel元素项的appid属性项进行配置。该属性项包含了简单的字符串(最大128字节),内核可使用该属性项创建VMID。

...<resource>  <fibrechannel appid='userProvidedID'/></resource>...

使用该特征项要求支持光纤通道的硬件,内核编译时配置有 CONFIG_BLK_CGROUP_FC_APPID选项,且nvme_fc内核模块已加载。自7.7.0起。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(8)NUMA Node Tuning、Block I/O Tuning、Resource partitioning、Fibre Channel VMID + + http://example.com/2022/11/14/domain_xml_format8_block_io_tuning_resource_partitioning_fibre_channel_vmid/ + 2022-11-14T08:10:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

NUMA Node Tuning

<domain>  ...  <numatune>    <memory mode="strict" nodeset="1-4,^3"/>    <memnode cellid="0" mode="strict" nodeset="1"/>    <memnode cellid="2" mode="preferred" nodeset="2"/>  </numatune>  ...</domain>

numatune

可选元素项numatune提供了如何通过控制虚拟机进程的NUMA策略对NUMA主机的性能进行调度。注意,仅支持QEMU。自0.9.3起。

memory

可选元素项memory指明了如何对NUMA主机的虚拟机进程进行内存分配。其包含多个可选属性项。属性项mode的可用值包含interleave、strict、preferred和restrictive,默认设置为strict。restrictive指明使用系统默认策略,仅有cgroups被用来限制内存节点,在memnode元素中将mode设置为restrictive。属性项nodeset指明了numa节点,其与vcpu元素项的cpuset属性项使用相同的语法。属性项placement(自0.9.12起)可被用来表明虚拟机进程的内存放置模式,其值可以为static或auto,是vcpu的placement的默认值,如果指定了nodeset则默认值为static。auto表明虚拟机进程只会从查询numad返回的咨询节点中集中分配内存,属性nodeset的值在指定auto的情况下将会被忽略。如果vcpu的placement设置为auto,且numatune未指定,则numatune的placement设置为auto,mode设置为strict。自0.9.3起。参阅 virDomainSetNumaParameters获取该元素的更多信息。

memnode

可选元素项memnode可以为每个虚拟机的numa节点指定内存分配策略。对于没有memnode元素的哪些节点,memory元素的默认值将会被使用。属性项cellid寻址应用设置的虚拟机NUMA节点。属性项mode和nodeset与memory元素中的对应值具有相同的含义和语法。设置与自动placement不兼容。qemu自1.2.7起。

Block I/O Tuning

<domain>  ...  <blkiotune>    <weight>800</weight>    <device>      <path>/dev/sda</path>      <weight>1000</weight>    </device>    <device>      <path>/dev/sdb</path>      <weight>500</weight>      <read_bytes_sec>10000</read_bytes_sec>      <write_bytes_sec>10000</write_bytes_sec>      <read_iops_sec>20000</read_iops_sec>      <write_iops_sec>20000</write_iops_sec>    </device>  </blkiotune>  ...</domain>

blkiotune

可选元素项blkiotune为虚拟机提供了调节Blkio cgroup可调参数的能力。如果忽略该设置项,则其默认由操作系统提供。自0.8.8起。

weight

可选元素项weight是虚拟机的全部i/o负载。该值的范围是100-1000.在2.6.39内核后,该值的范围是10-1000.

device

虚拟机可能又有多个device元素项,以便深入调节虚拟机正在使用的每一个主机块设备的权重。注意多个磁盘(可参阅Hard drives, floppy disks, CDROMs章节)可能共享同一个主机块设备,如果他们被相同的主机文件系统中的文件备份,这也是为什么使用全局作用域的调节参数而非相关的每一个虚拟机磁盘设备的原因(与磁盘定义中的iotune元素项相反(可参阅Hard drives, floppy disks, CDROMs章节),而iotune元素项适用于单个独立的磁盘)。每一个device元素项有两个强制的子元素项,path描述了设备的绝对路径,而weight给出了设备的相对权重,权重范围是100-1000.在2.6.39版本内核之后,该值的范围变为10-1000.自0.9.8起。除此之外,也可使用如下的可选子元素项:

read_bytes_sec

以字节为单位的每秒可读吞吐量。自1.2.2起。

write_bytes_sec

以字节为单位的每秒可写吞吐量。自1.2.2起。

read_iops_sec

每秒i/o读操作限制。自1.2.2起。

write_iops_sec

每秒i/o写操作限制。自1.2.2起。

Resource partitioning

虚拟机管理程序可能允许将虚拟机放入资源分区,也可能嵌套所述分区。resource元素将与资源分区相关的配置项组织在一起。当前其支持partition子元素项,该子元素项的内容定义了放置虚拟机的资源分区的绝对路径。如果没有列出任何分区,虚拟机将会被放到默认分区中。应用程序或管理员有责任确保分区在虚拟机启动之前就已存在。只有默认分区(适用于特定虚拟机管理程序)可以默认假定已经存在。

...<resource>  <partition>/virtualmachines/production</partition></resource>...

资源分区当前在qemu和lxc中受到支持,在所有已安装的控制器中将分区路径映射到cgroups目录。自1.0.5起。

Fibre Channel VMID

FC SAN可以提供多个依赖于VMID的QoS等级和访问控制功能。它还可以收集每个虚拟机的遥测数据,这些数据可用于增强虚拟机的IO性能。可以通过fibrechannel元素项的appid属性项进行配置。该属性项包含了简单的字符串(最大128字节),内核可使用该属性项创建VMID。

...<resource>  <fibrechannel appid='userProvidedID'/></resource>...

使用该特征项要求支持光纤通道的硬件,内核编译时配置有 CONFIG_BLK_CGROUP_FC_APPID选项,且nvme_fc内核模块已加载。自7.7.0起。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(7)Memory Allocation、Memory Backing和Memory Tuning + + http://example.com/2022/11/10/domain_xml_format7_memory_allocation/ + 2022-11-10T03:56:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

Memory Allocation

<domain>  ...  <maxMemory slots='16' unit='KiB'>1524288</maxMemory>  <memory unit='KiB'>524288</memory>  <currentMemory unit='KiB'>524288</currentMemory>  ...</domain>

memory

boot时虚拟机的最大分配内存。内存分配包含了在启动时或在之后热插拔的全部可能的额外内存设备。该值的单位由可选属性项unit设置,默认以KiB为单位(kibibytes,2^10或1024字节块)。有效的单位还有b或byte代表字节,KB代表kilobytes(10^3或1000字节),k或KiB代表kibibytes(1024字节)。MB代表megabytes(10^6或1000000字节),M或MiB代表 mebibytes(2^20或1048576字节),GB代表gigabytes(10^9或1000000000字节),T或TiB代表tebibytes(2^40或1099511627776字节).然而,该值会被libvirt四舍五入到最近的kibibytes,也可能进一步四舍五入到虚拟机管理程序支持的粒度。一些虚拟机管理程序也会强制设置一个最小值,例如4000KiB。这种情况下,虚拟机配置的numa的memory元素会被忽略。如果发生crash,可选属性项dumpCore可用来控制是否将虚拟机内存包含在生成的coredump文件中(属性值为on和off)。unit自0.9.11起,dumpCore自0.10.2起(仅限于qemu)。

maxMemory

运行时虚拟机的最大内存分配。被numa的memory元素或者numa cell大小配置的初始内存可通过内存热插拔方式增长到该元素限制的最大内存大小。unit属性项与memory中的同名属性项表现完全一致。slots属性项指定了将内存添加到虚拟机时可用slot的数量。数量上下限与虚拟机管理程序有关。注意由于内存对齐的限制,通过内存条热插拔所获得的完整内存大小可能无法达到该元素的设置值。自1.2.14起在qemu中支持。

currentMemory

虚拟机真实分配的内存。该值小于最大分配数值,允许动态扩充虚拟机内存。如果未设置该项,则默认与memory元素具有同样的数值设置。unit属性项与memory中的unit作用一致。

Memory Backing

<domain>  ...  <memoryBacking>    <hugepages>      <page size="1" unit="G" nodeset="0-3,5"/>      <page size="2" unit="M" nodeset="4"/>    </hugepages>    <nosharepages/>    <locked/>    <source type="file|anonymous|memfd"/>    <access mode="shared|private"/>    <allocation mode="immediate|ondemand" threads='8'/>    <discard/>  </memoryBacking>  ...</domain>

可选元素项memoryBacking包含多个影响主机内存页备份虚拟内存的元素项。

hugepages

该元素项告知虚拟机管理程序虚拟机使用大页而非通常的主机内存页大小进行内存分配。自1.2.5起。可以为每个numa节点详尽的设置大页。page元素项被引入。其包含一个强制属性项size用于指定具体使用哪些大页(在支持不同大小大页的系统中尤其有用)。size属性项的默认单位是kilobytes(1024的整数倍)。如果想要使用不同的单位,可使用可选属性项unit。对于numa系统,可选的nodeset属性可能会派上用场,因为它将给定虚拟机的NUMA节点与某些大页大小相关联。在某些例子中,除了4#节点之外的其他numa节点都使用了1G大小的大页。相关语法可以查看NUMA Node Tuning章节。

nosharepages

告知虚拟机管理程序虚拟机禁用共享内存页(内存合并,ksm)。自1.0.6起。

locked

当虚拟机管理程序支持并设置相关数值后,虚拟机内存页将会在主机内存中锁定,而且主机也不会将其换出,这在某些工况下如实时场景中是十分必要的。对于QEMU/KVM虚拟机,qemu进程自身使用的内存也会被锁定;与虚拟机内存不同,libvirt无法提前获取相应内存的数量,所以必须移除对于锁定内存的限制。因此,启用该可选元素项存在潜在的安全风险;当主机内存耗尽时主机无法从虚拟机回复内存,这就意味着大量的虚拟机将会造成大量内存被锁定,主机上会遭受到拒绝服务攻击。因为该原因,除非确实需要否则不建议使用该元素项;即便如此,强烈推荐为特定环境的内存分配设置 hard_limit属性项(参阅Memory Tuning章节),这可以有效的缓解上述描述的风险的发生。自1.0.6起。

source

使用type属性项,可提供“file”以利用文件内存备份或保持默认的”anonymous”。自4.10.0起,也可选择memfd备份。(仅限于QEMU/KVM)

access

使用mode属性项,指明内存是shared还是private。该元素会被每个numa节点的memAccess元素覆盖。

allocation

使用可选属性项mode,通过提供”immediate”或”ondemand”参数指明何时进行内存分配。自8.2.0起。也可以通过threads属性设置虚拟机管理程序用来进行内存分配的线程数量。

discard

当虚拟机管理程序支持并设置该元素时,在虚拟机关机之前(或当DIMM模块被拔出时)内存内容被丢弃。注意这仅仅只是一个优化项,并不保证在所有场景下都会起作用(例如虚拟机管理程序crash时)。自4.4.0起(仅限于QEMU/KVM)。

Memory Tuning

<domain>  ...  <memtune>    <hard_limit unit='G'>1</hard_limit>    <soft_limit unit='M'>128</soft_limit>    <swap_hard_limit unit='G'>2</swap_hard_limit>    <min_guarantee unit='bytes'>67108864</min_guarantee>  </memtune>  ...</domain>

memtune

可选元素项memtune提供与虚拟机内存调度参数相关的细节描述。如果忽略该项设置,则默认由操作系统提供默认值。对于QEMU/KVM,应用于qemu进程的参数被看做一个整体。因此,对其进行计数时,需要加上虚拟机ram、虚拟机显卡ram和qemu自身的一些内存。最后一点很难确定具体数值,因此需要猜想与尝试。对于每个可调参数,可以使用与memory相同的值来指定输入时数字所使用的单位。为了向后兼容,输出始终以KiB为单位。unit自0.9.11起。全部*_limit参数的可能值在0到VIR_DOMAIN_MEMORY_PARAM_UNLIMITED的范围内。

hard_limit

可选元素项hard_limit是虚拟机可使用的最大内存。该值的单位是kibibytes(1024字节的块)。qemu和kvm的使用者清冽建议不要设置相关限制项,因为如果虚拟机运行过少则虚拟机可能被内核关闭,决定进程运行所需的内存是一个无法决定的问题;也就是说,如果因工况需要已在 Memory Backing中设置了locked属性,您必须考虑部署的具体细节,并确定一个足够大的hard_limit值,以支持您的虚拟机的内存需求,但是该值足够小也可以保护主机以避免虚拟机锁定全部的内存。

soft_limit

可选元素项soft_limit就是内存争用期间强制设置的内存限制。该值的单位是kibibytes(1024字节块)。

swap_hard_limit

可选元素项swap_hard_limit就是加上交互内存swap后虚拟机可食用的最大内存。该值的单位是kibibytes(1024字节块)。该值应大于hard_limit的设置值。

min_guarantee

可选元素项min_guarantee就是应保证的虚拟机最小分配内存。该值的单位是kibibytes(1024字节块)。该元素仅被VMware ESX和OpenVZ支持。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> + + + + + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(6)CPU Tuning + + http://example.com/2022/11/04/domain_xml_format_CPU-tuning/ + 2022-11-04T06:35:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

<domain>  ...  <cputune>    <vcpupin vcpu="0" cpuset="1-4,^2"/>    <vcpupin vcpu="1" cpuset="0,1"/>    <vcpupin vcpu="2" cpuset="2,3"/>    <vcpupin vcpu="3" cpuset="0,4"/>    <emulatorpin cpuset="1-3"/>    <iothreadpin iothread="1" cpuset="5,6"/>    <iothreadpin iothread="2" cpuset="7,8"/>    <shares>2048</shares>    <period>1000000</period>    <quota>-1</quota>    <global_period>1000000</global_period>    <global_quota>-1</global_quota>    <emulator_period>1000000</emulator_period>    <emulator_quota>-1</emulator_quota>    <iothread_period>1000000</iothread_period>    <iothread_quota>-1</iothread_quota>    <vcpusched vcpus='0-4,^3' scheduler='fifo' priority='1'/>    <iothreadsched iothreads='2' scheduler='batch'/>    <cachetune vcpus='0-3'>      <cache id='0' level='3' type='both' size='3' unit='MiB'/>      <cache id='1' level='3' type='both' size='3' unit='MiB'/>      <monitor level='3' vcpus='1'/>      <monitor level='3' vcpus='0-3'/>    </cachetune>    <cachetune vcpus='4-5'>      <monitor level='3' vcpus='4'/>      <monitor level='3' vcpus='5'/>    </cachetune>    <memorytune vcpus='0-3'>      <node id='0' bandwidth='60'/>    </memorytune>  </cputune>  ...</domain>

cputune

可选元素项cputune为虚拟机提供与cpu可调参数相关的细节设置。注意:对于qemu驱动而言,可选的vcpupin和emulatorpin绑定设置需要考虑模拟器启动和NUMA限制后再进行设置。这就意味着主机的其他物理cpu在这段时间内会被虚拟机使用,这也可以从virsh cpu-stats的输出中反映出来。自0.9.0起。

vcpupin

可选元素项vcpupin指明了虚拟机的vCPU将会被绑定到主机的哪一个物理CPU上。如果忽略该设置项,vcpu元素的cpuset属性项也没有设置,则vcpu会默认绑定到全部的物理cpu上。其包含两个所需的属性项,属性项vcpu指明了vcpu id,属性项cpuset与vcpu元素的cpuset属性项是完全一致的。qemu自0.9.0起被支持,xen自0.9.1起被支持。

emulatorpin

可选元素项emulatorpin元素指明了主机的拿个物理cpu是模拟器,这是不包含虚拟机的vcpu和iothreads绑定的cpu的子集。如果忽略该项设置,也没有设置vcpu的cpuset属性项,模拟器将会默认绑定到全部的物理cpu上。emulatorpin包含了一个必须的属性项cpuset用于指明要绑定到哪一个物理cpu中。

iothreadpin

可选元素项iothreadpin指明了IOThreads将会被绑定到哪一个物理cpu上。如果忽略该设置,且vcpu元素项的cpuset属性项也没有设置,IOThreads将会默认绑定到全部的物理cpu上。这里存在两个必须的属性项,属性项iothread指明了IOThread ID,属性项cpuset指明了绑定的物理cpu。可查看IOThreads Allocation章节查看iothread的有效值。自1.2.9起。

shares

可选元素项shares指明了虚拟机的比例加权份额。如果忽略该项设置,默认使用操作系统提供的默认值。注意,这个值没有单位,是建立在其他虚拟机设置上的相对量度。一个设置为2048的虚拟机将会相对另一个设置为1024的虚拟机得到两倍长的cpu时间。这个值得范围是2-262144.自0.9.0起。

period

可选元素项period指明了执行间隔(单位:毫秒)。在该元素项内,虚拟机的每一个vcpu都不允许消耗超过配额的运行时间。该值的范围是1000-1000000.设置为0则意味着未设置数值。qemu自0.9.4起支持,lxc自0.9.10起支持。

quota

可选元素项quota指定了最大允许带宽(单位:毫秒)。quota设置为负值的虚拟机表明对于其vcpu线程具有无限制的带宽,即其不受带宽控制。该值的范围是1000-17592186044415或小于0.quota为0意味着未设置数值。可使用该特征确保所有的vcpu以相同速度运行。qemu自0.9.4起支持,lxc自0.9.10起支持。

global_period

可选元素项global_period指定整个虚拟机的强制CFS调度程序间隔(单位:微秒),与强制每个vCPU间隔的period形成对比。该值的范围是1000-1000000.global_period设置为0意味着未设置数值。qemu自1.3.3起支持。

global_quota

可选元素项global_quota指明了全虚拟机在周期时间内的最大允许带宽(单位:毫秒)。global_quota设置为负值的虚拟机表明虚拟机具有无限制的带宽,即其不受带宽控制。该值的范围是1000-17592186044415或小于0.global_quota为0意味着未设置数值。qemu自1.3.3起支持。

emulator_period

可选元素项emulator_period指明了强制间隔(单位:毫秒)。使用emulator_period设置,则虚拟机的模拟器线程(包括vcpu)不允许消耗超过emulator_quota的运行时间。该值的范围是1000-1000000.为0意味着未设置数值。qemu自0.10.0起支持.

emulator_quota

可选元素项emulator_quota指明了虚拟机模拟器线程(包括vcpu)最大允许带宽(单位:毫秒)。带有负值emulator_quota设置的虚拟机的模拟器线程(包括vcpu)拥有无限带宽,即其不受带宽控制。值范围是1000-17592186044415或小于0.quota设置为0意味着未设置值。qemu自0.10.0起支持.

iothread_period

可选元素项iothread_period指明了IOThreads的强制间隔(单位:毫秒)。使用iothread_period,虚拟机的每一个IOThreads不允许消耗超过iothread_quota设置的运行时间。该值的范围是1000-1000000.设置为0意味着未设置该值。自2.1.0起支持qemu。

iothread_quota

可选元素项iothread_quota指明了IOThreads的最大允许带宽(单位:毫秒)。iothread_quota为负值的虚拟机表明虚拟机IOThreads具有无限带宽,即其不受带宽控制。该值的范围是1000-17592186044415或小于0.设置为0意味着未设置该值。可使用该特征确保全部IOThreads以相同速度运行。自2.1.0起支持qemu。

vcpusched/iothreadsched/emulatorsched

可选元素项vcpusched、iothreadsched和emulatorsched分别指明了特定vcpu、iothread和模拟器线程的调度类型(值为batch、idle、fifo和rr)。对于vcpusched和iothreadsched,属性项vcpus和iothreads选择了该设置适用于哪些vCPUs/IOThreads,其余的设置为默认值。元素项emulatorsched不具有该属性。vcpus的有效值为0到虚拟机中设置的vcpu数量减一。iothreads的有效值在IOThreads Allocation章节中有过描述。如果未定义iothreadids,然后libvirt将IOThreads编号从1到虚拟机可用的iothreads进行计数。对于实时调度(fifo、rr),也需指定优先级(非实时调度可忽略)。优先级的范围依赖于主机内核(通常为1-99)。自1.2.13起。emulatorsched自5.3.0起。

cachetune(自4.1.0起)

可选元素项cachetune使用主机上的restctrl控制cpu缓存分配。是否支持此功能可以从一些限制(如最小尺寸和所需粒度)的功能中获取。必需的属性项vcpus指定了本次分配适用于哪一个vcpu。一个vcpu只能是一个cachetune元素分配项的成员。cachetune指定的vCPU可以与memorytune 中的vCPU相同,但不允许重复指定。可选的、仅输出的id属性唯一标识缓存。支持以下子元素:

cache

该可选元素项控制cpu缓存分配并具有以下属性项:

level

用于分配的主机缓存等级。

id

用于分配的主机缓存id。

type

分配的类型。code代表指令,data代表数据。both则同时代表两者。当前分配类型只能与主机支持的类型保持一致,意味着不能在启用cdp(指令、数据优先级)的主机上使用both类型。

size

分配区域大小。默认值以字节为单位,但是unit属性项可用来缩放该值。

unit(可选)

如果unit设置为KiB、MiB、GiB或TiB(在Memory Allocation中的memory元素中描述),则size的默认值为字节。

monitor(自4.10.0起)

可选元素项monitor为当前缓存分配创建缓存管理器并拥有如下所需属性:

level

monitor所属的主机缓存等级。

vcpus

vcpu列出了monitor的适用范围。一个monitor的vcpu列表只能是相关分配的vcpu列表的一部分。默认的管理器与相关的分配具有相同的vcpu列表。对于非默认monitor,不允许重复定义vcpu。

memorytune(自4.7.0起)

可选元素项memorytune可使用主机上的resctrl控制内存带宽分配。是否支持此功能可以从一些限制(如最小尺寸和所需粒度)功能中获取。必需的属性项vcpus指定了本次分配适用于哪一个vcpu。一个vcpu只能是一个memorytune元素分配项的成员。memorytune指定的vCPU可以与cachetune中的vCPU相同,但不允许重复指定。支持以下子元素:

node

该元素控制cpu内存带宽分配,拥有以下属性。

id

分配内存带宽的主机节点id。

bandwidth

节点中用于分配的内存带宽。该值默认以百分比为单位。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> + + + + + + + + + + + + + + + + + + +
+ + + _access函数32bit和64bit编译差异 + + http://example.com/2022/10/20/access_usage/ + 2022-10-20T07:00:10.000Z + 2023-12-29T02:38:41.453Z + + 前言

最近,在进行软件开发工作中遇到了一个问题,觉得比较有趣,在此进行以下记录。

问题比较简单,需要检查windows系统下的C:\Windows\System32路径下是否存在特定的用户文件(dll格式)。在这里使用_access函数(C运行库函数)对文件进行检查,该函数主要用于检查文件或目录是否存在及其对应的读写权限。但是开发完成后,在自检过程中却发现使用_access函数无法达到预期,主要表现为文件已存在,但是函数返回结果却表明文件不存在。如果换用win32 API函数PathFileExistsA却正常,文件存在于对应目录。本文即对_access函数的异常进行分析。

分析过程

win32函数访问没问题,而使用C语言库函数存在问题,即系统调用没问题,主要问题应该出现在C语言库上。而最简单的修改项就是编译环境修改,之前为了兼容性考虑,代码选择的编译环境为32位,因此将编译环境改为64位。此时,_access函数正常执行,返回了符合预期的结果。

因此,针对这一点进行分析,最终在微软官网上找到相关说明,链接如下:https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?from_wecom=1

微软在64位操作系统上,为64位应用程序保留了%windir%\System32(通常就是C:\Windows\System32)路径。因为通常情况下,32位dll和64位dll的文件名称完全相同,因此64位dll也保存在System32路径下,而32位dll则被保存在其他路径。这就使得64位操作系统下的32位应用程序访问对应dll时,直接访问%windir%\System32路径是错误的,因此微软为64位系统下的32位程序通过访问%windir%\System32路径做了重定向。而32位要想真正访问%windir%\System32路径,就可以使用%windir%\Sysnative路径替代,也可以使用Wow64DisableWow64FsRedirection函数禁用文件重定向功能。

示例程序:

#include  <io.h>#include  <stdio.h>#include  <stdlib.h>#include <Windows.h>#include <iostream>#include "Shlwapi.h"#include <errhandlingapi.h >int main( void ){    char *file_path = "C:\\Windows\\System32\\test.dll";    char *file_path1 = "C:\\Windows\\Sysnative\\test.dll";    // Check for existence.    printf_s( "1.access system32.\n");    if( (_access( file_path, 0 )) != -1 )    {        printf_s( "File %s is exists.\n", file_path);    }    else    {        printf_s( "File %s isn't exists.\n", file_path);    }    printf_s( "2.access sysnative.\n");    if( (_access( file_path1, 0 )) != -1 )    {        printf_s( "File %s is exists.\n", file_path1);    }    else    {        printf_s( "File %s isn't exists.\n", file_path1);    }    PVOID OldValue = NULL;    //  Disable redirection immediately prior to the native API function call.    printf_s( "3.Disable Redirection.\n");    bool flag = Wow64DisableWow64FsRedirection(&OldValue);    printf("Wow64DisableWow64FsRedirection return value is %d.\n", flag);    if (!flag)    {        DWORD dw = GetLastError();        printf("Errorcode is %d.\n", dw);    }    if( (_access( file_path, 0 )) != -1 )    {        printf_s( "File %s is exists.\n", file_path);    }    else    {        printf_s( "File %s isn't exists.\n", file_path);    }    return 0;}

在32位编译环境下输出如下:

1.access system32.File C:\Windows\System32\test.dll isn't exists.2.access sysnative.File C:\Windows\Sysnative\test.dll is exists.3.Disable Redirection.Wow64DisableWow64FsRedirection return value is 1.File C:\Windows\System32\test.dll is exists.

(1)C:\Windows\System32路径无法直接访问;

(2)C:\Windows\Sysnative路径会被重定位到C:\Windows\System32;

(3)Wow64DisableWow64FsRedirection函数返回1代表函数正常运行,此后再使用C:\Windows\System32路径,则无重定向操作,获取到了正确的结果;

在64位编译环境下输出如下:

1.access system32.File C:\Windows\System32\test.dll is exists.2.access sysnative.File C:\Windows\Sysnative\test.dll isn't exists.3.Disable Redirection.Wow64DisableWow64FsRedirection return value is 0.Errorcode is 1.File C:\Windows\System32\test.dll is exists.

(1)C:\Windows\System32路径可以直接访问;

(2)C:\Windows\Sysnative路径不会被重定位到C:\Windows\System32;

(3)Wow64DisableWow64FsRedirection函数返回0代表函数异常运行,但不影响后续使用C:\Windows\System32路径获取正确的结果;

结论

_access函数在32bit和64bit编译环境下输出结果存在差异,主要原因即为64位windows操作系统下,%windir%\System32被保留给了对应的64位应用程序,而32位应用程序访问%windir%\System32路径时就会被重定向到系统指定的其他路径上。而使用32bit编译环境最终生成的是32位应用程序,因此对应路径被重定向,直接访问%windir%\System32路径下的文件时就会出现异常。

因此建议在64位操作系统中使用64位的应用程序,而非32位的应用程序,虽然64位操作系统兼容32位应用程序,而32位操作系统不兼容64位应用程序,因此32位具有更广泛的兼容性。

如果不确定目标程序运行环境或程序存在运行于两种系统的使用场景,则在使用文件访问相关函数时:

(1)直接使用win32函数PathFileExistsA;

(2)使用_access函数前,先使用Wow64DisableWow64FsRedirection函数禁用文件重定向功能;

参考链接

(1)https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?from_wecom=1
(2)https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-wow64disablewow64fsredirection?from_wecom=1
(3)https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=msvc-170&from_wecom=1
(4)https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathfileexistsa?from_wecom=1

]]>
+ + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近,在进行软件开发工作中遇到了一个问题,觉得比较有趣,在此进行以下记录。</p> +<p>问题比较简单,需要检查windows系统下的C:\Windows\System32路径下是否存在特定的用户文件(dll格式)。在这里使用_access函数(C运行库函数)对文件进行检查,该函数主要用于检查文件或目录是否存在及其对应的读写权限。但是开发完成后,在自检过程中却发现使用_access函数无法达到预期,主要表现为文件已存在,但是函数返回结果却表明文件不存在。如果换用win32 API函数PathFileExistsA却正常,文件存在于对应目录。本文即对_access函数的异常进行分析。</p> + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(5)IOThreads Allocation + + http://example.com/2022/10/19/domain_xml_format_IOThreads-Allocation/ + 2022-10-19T09:36:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

IOThreads是支持磁盘设备的专用事件循环线程,用于执行块I/O请求,以提高可伸缩性,尤其是在具有许多LUN的SMP主机/客户机上。自1.2.8起(仅限于QEMU)。

<domain>  ...  <iothreads>4</iothreads>  ...</domain><domain>  ...  <iothreadids>    <iothread id="2"/>    <iothread id="4"/>    <iothread id="6"/>    <iothread id="8" thread_pool_min="2" thread_pool_max="32"/>  </iothreadids>  <defaultiothread thread_pool_min="8" thread_pool_max="16"/>  ...</domain>

iothreads

此可选元素的内容定义要分配给虚拟机以供支持的目标存储设备使用的 IOThread数量。每一个主机CPU只有1个或2个IOThread。可能有多个受支持的设备分配给每个IOThread。自1.2.8起。

iothreadids

可选的iothreadids元素提供了为虚拟机专门定义IOThread ID的能力。默认情况下,IOThread ID 是从1到为虚拟机定义的iothreads的数量顺序编号的。id属性项用于定义IOThread ID。id属性项必须是一个大于0的正整数。如果定义的iothreadids少于为虚拟机定义的iothreads,则libvirt将从1开始按顺序填充iothreadids,避免任何预定义的id。如果iothreadids大于为虚拟机定义的iothreads,iothreads的值将会相应调整。自1.2.15起。该元素有两个可选属性项thread_pool_min和thread_pool_max用于定义给定IOThread工作线程的上下限。前者可能是0,后者绝不会为0.自8.5.0起。

defaultiothread

该元素代表了虚拟机管理程序内部的默认事件循环,为指定特定IOThread的I/O请求会被该循环处理。该元素可设置thread_pool_min和thread_pool_max属性项,指定了默认事件循环工作线程的上下限数量。模拟器可能是多线程的并按需生成所谓的工作线程。通常两个属性项都不会去设置(使得模拟器使用其自身的默认值),除非模拟器在实时工作负载中运行,因此无法承受生成新工作线程所需时间的不可预测性。自8.5.0起。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> +<p>IOThreads是支持磁盘设备的专用事件循环线程,用于执行块I&#x2F;O请求,以提高可伸缩性,尤其是在具有许多LUN的SMP主机&#x2F;客户机上。自1.2.8起(仅限于QEMU)。</p> + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(4)CPU Allocation + + http://example.com/2022/09/05/domain_xml_format_CPU-Allocation/ + 2022-09-05T11:01:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

<domain>  ...  <vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>  <vcpus>    <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>    <vcpu id='1' enabled='no' hotpluggable='yes'/>  </vcpus>  ...</domain>

vcpu

该元素定义了分配给客户机操作系统的虚拟cpu的最大数量,该值范围为1到虚拟机管理器支持的最大数量之间。

cpuset

可选属性cpuset是以逗号分隔的物理CPU编号列表,默认情况下虚拟机进程和虚拟CPU可以固定到对应编号的物理CPU上。(注意:虚拟机进程和虚拟cpu的绑定(pinning)策略可以由cputune分别指定。如果cputune的属性项emulatorpin被设置,则vcpu指定的cpuset将会被忽略。对于vcpupin指定的虚拟cpu,cpuset制定的cpuset也会被忽略。vcpupin未指定的虚拟cpu,每一个都会被绑定到cpuset指定的物理cpu上)。列表中的每一个元素可以是一个单独的cpu编号、cpu编号范围或是插入符号后跟要从先前范围中排除的CPU编号。自0.4.4版本起支持。

current

可选属性current可用于指定是否应启用少于最大数量的虚拟CPU。从 0.8.5开始。

placement

可选属性placement可用来表明虚拟机进程的cpu布局模式。该值可为static或auto,但是如果指定了cpuset,则默认numatune的placement设置为static。使用auto表示虚拟机进程将通过查询numa固定到咨询节点集,如果指定了属性cpuset的值,则将被忽略。如果cpuset和placement都没有指定或placement设置为static,但是cpuset为指定,则虚拟机进程将会被绑定到所有的可用物理cpu上。自0.9.11起(仅限于QEMU和KVM)。

vcpus

vcpus元素项运行控制单个vCPU的状态。id属性项指定了libvirt在其他地方使用的vCPU id,例如vCPU绑定、调度程序信息和NUMA分配。请注意,在某些情况下,客户机中看到的vCPU ID可能与libvirt ID不同。有效ID 从0到vcpu元素设置的最大vCPU计数减 1。enabled属性项允许控制vCPU的状态。有效值为yes和no。hotpluggable控制在引导开机时启用CPU是否可以热插拔和热拔出给定的vCPU。请注意,所有禁用的vCPU必须是可热插拔的。有效值为yes和no。order允许指定在线vCPU的顺序。对于需要一次插入多个vCPU的虚拟机管理器/平台,该顺序可能会在需要一次启用的所有 vCPU上重复。顺序不强制指定。然后以任意顺序添加vCPU。如果使用了顺序信息,它必须用于所有在线vCPU。虚拟机管理器可能在单一操作期间清理或更新顺序信息。请注意,虚拟机管理器可能会创建不同于引导vCPU的热插拔vCPU,因此可能需要进行特殊初始化。虚拟机管理器可能要求启动时启用的不可热插拔的vCPU在开始时从ID 0开始聚集。可能还需要vCPU 0始终存在且不可热插拔。请注意,可能需要为单个CPU提供状态以支持可寻址的 vCPU热插拔,并且此功能可能不受所有虚拟机管理器的支持。QEMU要求实现以下条件。vCPU 0需启用并设置为不可热插拔。在PPC64上,同样需要启用同一内核中的vCPU。引导开机时存在的所有非热插拔CPU都需要在vCPU 0 之后进行分组。自2.2.0起(仅限于QEMU)。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(3)SMBIOS System Information + + http://example.com/2022/09/02/domain_xml_format_SMBIOS-System-Information/ + 2022-09-02T09:00:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

一些虚拟机管理器运行控制呈现给客户机的系统信息(例如虚拟机管理器可以填充SMBIOS并通过客户机的dmidecode指令进行检查)。可选元素项sysinfo包含了所有此类信息。自0.8.7版本起。

...<os>  <smbios mode='sysinfo'/>  ...</os><sysinfo type='smbios'>  <bios>    <entry name='vendor'>LENOVO</entry>  </bios>  <system>    <entry name='manufacturer'>Fedora</entry>    <entry name='product'>Virt-Manager</entry>    <entry name='version'>0.9.4</entry>  </system>  <baseBoard>    <entry name='manufacturer'>LENOVO</entry>    <entry name='product'>20BE0061MC</entry>    <entry name='version'>0B98401 Pro</entry>    <entry name='serial'>W1KS427111E</entry>  </baseBoard>  <chassis>    <entry name='manufacturer'>Dell Inc.</entry>    <entry name='version'>2.12</entry>    <entry name='serial'>65X0XF2</entry>    <entry name='asset'>40000101</entry>    <entry name='sku'>Type3Sku1</entry>  </chassis>  <oemStrings>    <entry>myappname:some arbitrary data</entry>    <entry>otherappname:more arbitrary data</entry>  </oemStrings></sysinfo><sysinfo type='fwcfg'>  <entry name='opt/com.example/name'>example value</entry>  <entry name='opt/com.coreos/config' file='/tmp/provision.ign'/></sysinfo>...

sysinfo元素包含一个强制属性项type决定了子元素的布局,支持值如下:

smbios

子元素调用特定的SMBIOS值,如果被用在os元素(参照Operating system booting)的smbios子元素间的连接上,将会影响客户机。sysinfo 的每个子元素命名一个SMBIOS块,并且在这些元素中可以是描述块内字段的条目元素列表。识别以下块和条目:

bios

这是SMBIOS的块0,条目名称来自:

  • vender:BIOS供应商名称
  • version:BIOS版本
  • date:BIOS发行日期。如果提供了该元素项,则会改为mm/dd/yy或mm/dd/yyyy的格式。如果字符串的年部分只有两个数字,则将会假定为19yy年。

system

这是SMBIOS的块1,条目名称来自:

  • manufacturer:BIOS制造商
  • product:产品名称
  • version:产品版本
  • serial:序列号
  • uuid:统一唯一标识符。如果该条目与顶层的uuid的一起设定,则两个元素值必须相同
  • sku:识别特定配置的sku编号
  • family:识别特定计算机所属的家族系列

baseBoard

这是SMBIOS的块2.该元素项可以重复多次用以描述所有的基板(base board);然而,并不是所有的虚拟机管理器都支持该元素项重复。该元素项有下列的子元素项:

  • manufacturer:BIOS的制造商
  • product:产品名称
  • version:产品版本号
  • serial:序列号
  • asset:资产标签
  • location:机箱内的位置

注意:bios、system和baseBoard块的错误条目会被忽略,而不会报错。除了uuid验证和日期格式检查之外,所有值都作为字符串传递给虚拟机管理器驱动程序。

chassis

自4.1.0起支持。SMBIOS的块3,条目名称来自:

  • manufacturer:机箱的制造商
  • version:机箱的版本号
  • serial:序列号
  • asset:资产标签
  • sku:sku编号

oemStrings

SMBIOS的块11.该元素项只出现一次,却有多个entry子元素项,每一个都提供随机字符串数据。对于entry提供的数据并没有任何限制,如果数据打算由客户机中的应用程序使用,建议使用应用程序名称作为字符串中的前缀。(自4.1.0起支持)

fwcfg

一些虚拟机管理器提供统一的方法来调整固件如何配置自身,或者可能包含要为客户机操作系统安装的表,例如引导顺序、ACPI、SMBIOS等。

如果允许用户自定义自己的配置blob(config blob)。在QEMU的例子中,然后这些出现在domain的sysfs下(如果客户机内核启用FW_CFG_SYSFS配置项),即/sys/firmware/qemu_fw_cfg路径下。注意,这些值适用于下的模式。自6.5.0起支持。

注意:由于数据槽数量有限,强烈建议不要使用fwcfg,而应使用

<sysinfo type='fwcfg'>  <entry name='opt/com.example/name'>example value</entry>  <entry name='opt/com.example/config' file='/tmp/provision.ign'/></sysinfo>

sysinfo元素项拥有多个entry子元素。每一个元素都有强制的name属性,它定义了blob的名称,并且必须以opt/开头,并且为了避免与其他名称冲突,建议采用opt/$RFQDN/$name形式,其中$RFQDN是控制的反向完全限定域名。之后,元素项也可以包含相应数值(直接设置blob值),或者file属性项(从file中设置blob值)。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> +<p>一些虚拟机管理器运行控制呈现给客户机的系统信息(例如虚拟机管理器可以填充SMBIOS并通过客户机的dmidecode指令进行检查)。可选元素项sysinfo包含了所有此类信息。自0.8.7版本起。</p> + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(2)Operating system booting + + http://example.com/2022/08/31/domain_xml_format_Operating-system-booting/ + 2022-08-31T11:00:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

这里有很多不同的方法用来引导操作系统开机,每一个都有优点与缺点。

BIOS bootloader

支持全虚拟化(full virtualization)的虚拟机管理程序可以通过BIOS引导开机。在这种情况下,BIOS存在开机顺序优先级(软盘floppy、硬盘harddisk、光盘cdrom、网络network)决定到哪里去获取/查找开机镜像(boot image)。

<!-- Xen with fullvirt loader --><!-- 使用全虚拟化加载器的Xen-->...<os>  <type>hvm</type>  <loader>/usr/lib/xen/boot/hvmloader</loader>  <boot dev='hd'/></os>...<!-- QEMU with default firmware, serial console and SMBIOS --><!-- 使用默认的硬件、串行控制台和SMBIOS的QEMU-->...<os>  <type>hvm</type>  <boot dev='cdrom'/>  <bootmenu enable='yes' timeout='3000'/>  <smbios mode='sysinfo'/>  <bios useserial='yes' rebootTimeout='0'/></os>...<!-- QEMU with UEFI manual firmware and secure boot --><!-- 使用UEFI手动固件和安全启动的QEMU-->...<os>  <type>hvm</type>  <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>  <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/var/lib/libvirt/nvram/guest_VARS.fd</nvram>  <boot dev='hd'/></os>...<!-- QEMU with UEFI manual firmware, secure boot and with NVRAM type 'file'--><!-- 使用UEFI手动固件、安全启动和NVRAM类型文件的QEMU-->...<os>  <type>hvm</type>  <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>  <nvram type='file' template='/usr/share/OVMF/OVMF_VARS.fd'>    <source file='/var/lib/libvirt/nvram/guest_VARS.fd'/>  </nvram>  <boot dev='hd'/></os>...<!-- QEMU with UEFI manual firmware, secure boot and with network backed NVRAM'--><!-- 使用UEFI手动固件、安全启动和网络支持的NVRAM的QEMU-->...<os>  <type>hvm</type>  <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>  <nvram type='network'>    <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>      <host name='example.com' port='6000'/>      <auth username='myname'>        <secret type='iscsi' usage='mycluster_myname'/>      </auth>    </source>  </nvram>  <boot dev='hd'/></os>...<!-- QEMU with automatic UEFI firmware and secure boot --><!-- 使用自动UEFI固件和安全启动的QEMU-->...<os firmware='efi'>  <type>hvm</type>  <loader secure='yes'/>  <boot dev='hd'/></os>...<!-- QEMU with automatic UEFI stateless firmware for AMD SEV --><!-- 使用适用于AMD SEV的自动UEFI无状态固件的QEMU-->...<os firmware='efi'>  <type>hvm</type>  <loader stateless='yes'/>  <boot dev='hd'/></os>...

firmware固件

firmware属性项允许管理程序自动填充元素,通过选择的固件可以使能启用一些必须的特征项。可接受的值为bios和efi。选择过程扫描在特定位置的描述已安装固件镜像的文件,使用最具体的一个填充domain的需求。选择的位置(从通用到最具体的一个)如下:

  • /usr/share/qemu/firmware
  • /etc/qemu/firmware
  • $XDG_CONFIG_HOME/qemu/firmware

对于更多信息,可以参考QEMU代码库中的docs/interop/firmware.json描述的固件元数据规范。常规用户可不关心这一点。从libvirt 5.2.0(仅限于QEMU和KVM)版本开始。

对于VMware客户机,当客户机使用UEFI时则必须设置为efi,使用BIOS时可以不设置。从libvirt 5.3.0(VMware ESX和Workstation/Player)版本开始。

type类型

type属性项指明了在虚拟机中启动的操作系统的类型。

  • hvm表明操作系统被设计用来在裸机(bare metal)上运行,因此需要全虚拟化。
  • linux(糟糕的命名)表明是一个支持Xen 3虚拟机管理程序客户机ABI的操作系统。
  • 【可选】arch表明了需要虚拟化的CPU架构。
  • 【可选】machine表明了机器类型。

xml功能(Capabilities XML)章节描述了允许值得详细信息。大多数虚拟机管理程序的驱动程序都省略了arch的设置,则所在主机host的架构就会被选择。对于test、ESX和VMWare虚拟机管理程序驱动程序,在x86_64主机上通常会选择i686架构。从libvirt 0.0.1版本开始提供支持。

firmware固件

从libvirt 7.2.0版本开始仅对QEMU/KVM提供支持。

当使用固件自动选择功能时,固件中存在不同的可用特征。特征列表限制了为虚拟机自动选择的固件。特征列表也可以通过使用0或更多的feature元素进行指定。选择硬件时,libvirt只会考虑列表中的特征,忽略其他特征项。

feature

强制属性列表:

  • enabled:可用值为yesno,告诉libvirt在自动选择固件时是否要启用或禁用对应特征项

  • name:特征项名称,特征值如下表所示:

    • enrolled-keys:选择的nvram模板是否有默认证书注册。带有安全启动(Secure Boot)特征的固件如果没有注册密钥,将会以无签名二进制文件成功开机。仅针对带有安全启动(Secure Boot)特征的固件进行校验。

    • secure-boot:固件是否实现UEFI安全启动(Secure Boot)特征。

loader

可选配置项loader标签指的是固件blob,通过绝对路径指定,被用来辅助domain创建过程。该标签为Xen全虚拟化domain使用,也被用来为QEMU/KVM domain设置QEMU BIOS文件路径。Xen自libvirt 0.1.0版本后被支持,QEMU/KVM自libvirt 0.9.12版本后被支持。该元素有两个可选的属性项:readonly(可用值为yes或no)表明镜像是可写的还是只读的。第二个属性项type可用值为rom和pflash。其告诉虚拟机管理程序文件应该被映射到客户机内存的什么位置上。例如,如果loader的路径指向一个UEFI镜像,type就应该是pflash。此外,一些固件会实现安全启动(Secure Boot)功能。属性项secure可以向虚拟机管理程序表明固件是否兼容安全启动(Secure Boot)功能。它不能用于在固件中启用或禁用功能本身。自libvirt 2.1.0版本后被支持.如果loader被标记为只读(read-only),之后在UEFI环境下,其会假定这里存在一个可用的可写NVRAM。在某些情况下,loader更倾向于在无 NVRAM环境下运行,在关机时丢弃任何配置变化。stateless标识(自libvirt 8.6.0版本后被支持)可被用来控制这种行为,如果设置为n,o则NVRAM将永远不会被创建。

nvram

一些UEFI固件可能想要使用一个非易失性存储器去保存一些变量。在主机中,这表示为一个文件,文件的绝对路径被保存在这一元素中。此外,当domain启动时,libvirt复制在qemu.conf中定义的所谓的主NVRAM存储文件。如果必要的话,template属性可用于配置文件中主NVRAM存储的每个域的覆盖映射。对于非持久性domain,如果NVRAM文件已由libvirt创建,则它会被留下,并且管理程序有责任保存和删除文件(如果需要持久保存)。自1.2.8起。

自8.5.0版本起,该元素拥有type属性项(可用值为file、block和network)。在这种情况下,NVRAM存储器被子元素使用和disk源相同的语法进行阐述。详情可参阅Hard drives, floppy disks, CDROMs章节。

注意:network支持的NVRAM不是从template中实例化的,因此使用者需人为提供一个可用的NVRAM镜像。

如果loader被标记为stateless,则提供该元素是无效的。

boot

dev属性项使用fd、hd、cdrom和network等值。用于指定下一个要考虑的引导设备。boot元素可以重复多次以安装用于轮流尝试的引导设备优先级列表。多个相同类型的设备根据它们的目标进行排序,同时保留总线的顺序。在define完domain后,libvirt返回XML配置(通过virDomainGetXMLDesc)列出已排好序的设备。排序完成之后,第一个设备被标记为可引导的(bootable)。配置为从“hd”引导且分配有vdb、hda、vda和hdc磁盘的域将从vda引导启动(排序列表为vda、vdb、hda、hdc)。类似的带有hdc、vda、vdb和hda磁盘的domain竟会从hda(磁盘排序为:hda、hdc、vda和vdb)引导启动。以所需方式进行配置将会十分麻烦,这就是引入每个设备引导元素的原因,也是提供对引导顺序的完全控制的首选方式。boot元素和每个设备引导元素是互斥的。boot元素自0.1.3起提供支持,per-device boot自0.8.8起提供支持。

smbios

如何填充客户机中可见的SMBIOS信息。mode属性项必须被指定,其值为emulate(虚拟机管理程序生成所有值)、host(从主机的SMBIOS值中复制除UUID之外的全部Block 0 和Block 1;virConnectGetSysinfo函数调用可以被用来查看哪些值被复制)或sysinfo(使用 SMBIOS System Information元素中的值)。如果未制定,则虚拟机管理程序默认使用。从0.8.7版本起提供支持。

到目前为止,BIOS/UEFI配置旋钮足够通用,可以由大多数(不是全部)固件实现。然而,从现在开始,并不是每一个单项配置都对所有固件起作用。例如,rebootTimeout对UEFI无作用,useserial可能对不产生输出到串行行的BIOS固件有影响等等。此外,固件经常不能导出其功能以便libvirt(或使用者)进行检查。固件功能的集合会随着新版本而发生变化。因此,建议用户在生产中依赖它们之前尝试他们使用的设置。

bootmenu

在客户机启动时是否启用交互式引导菜单提示。enable属性项可以是yes或no。如果没有指明,虚拟机见识程序默认使用该功能。自0.8.3版本提供支持。额外的属性项timeout指明需要多少毫秒等待启动菜单,直到它超时。允许的值是 [0, 65535] 范围内的数字,除非 enable 设置为“yes”,否则该属性项会被忽略。自1.2.8版本提供支持。

bios

该元素属性项useserial可用值为yes或no。该属性项启用或禁用串行图形适配器(Serial Graphics Adapter)功能,该功能可以使得用户在串口上看到BIOS信息。因此,需定义串口。自0.9.4版本起。自0.10.2版本起(仅限于QEMU)。还有另外一个属性项rebootTimeout,当引导启动失败时,该属性想决定是否重启以及间隔多长时间后再次尝试引导启动。该值以毫秒为单位,最大值为65535,特殊值-1禁用重启功能。

Host bootloader

采用半虚拟化的管理程序通常不会模拟BIOS,而是主机负责启动操作系统引导。在主机中使用pseudo-bootloader提供接口以便为客户机选择一个内核。一个例子便是Xen的pygrub。而Bhyve虚拟机管理程序也使用主机bootloader方式,bhyveload或grub-bhyve。

bootloader

bootloader提供了主机操作系统中引导加载程序可执行文件的完全限定路径。运行bootloader选择要引导的内核。引导加载程序所需的输出取决于使用的管理程序。自0.1.1起。

bootloader_args

可选配置项bootloader_args允许将命令行参数传递给bootloader。自0.2.3起。

Direct kernel boot

安装一个新的客户机操作系统时,直接从存储在主机操作系统中的内核和 initrd引导通常很有用,可以将命令行参数直接传递给安装程序。该功能可在半虚拟化和全虚拟化客户机中使用。

...<os>  <type>hvm</type>  <loader>/usr/lib/xen/boot/hvmloader</loader>  <kernel>/root/f8-i386-vmlinuz</kernel>  <initrd>/root/f8-i386-initrd</initrd>  <cmdline>console=ttyS0 ks=http://example.com/f8-i386/os/</cmdline>  <dtb>/root/ppc.dtb</dtb>  <acpi>    <table type='slic'>/path/to/slic.dat</table>  </acpi></os>...

type

该属性项与之前在BIOS bootloader中的描述有相同的语义。

loader

该属性项与之前在BIOS bootloader中的描述有相同的语义。

kernel

该属性项指明了主机中内核镜像的全限定路径。

initrd

该属性项指明了主机中ramdisk镜像(可选项)的全限定路径。

cmdline

该属性项指明了引导启动时传递给内核(或安装程序)的参数。这经常被用来指定备用主控制台(或串口)、安装媒体源/kickstart 文件。

dtb

该元素项指明了主机中的设备树二进制(device tree binary,dtb)镜像的全限定路径。从1.0.4版本起提供支持。

acpi

table元素包含了ACPI(Advanced Configuration and Power Interface,高级配置和电源接口)表。type属性项包含ACPI表类型(当前只有slic被支持)。自1.3.5支持QEMU。自5.9.0支持Xen。

Container boot

当使用基于虚拟化的容器引导启动一个虚拟机时,可不使用内核/引导镜像,使用init元素指定init二进制文件的路径。默认无参数加载。为指明初始化参数,使用initarg参数项,根据需要重复多次。如果设置了cmdline元素,将会被用来提供一个/proc/cmdline的替代项,但是却不会影响初始化参数(init argv)。

使用initenv元素设置环境变量,一个元素代表一个变量。

使用initdir元素为初始化设置特定的工作目录。

以给定的用户或用户组运行初始化命令,分别使用inituser或initgroup。两个元素可以被用来提供用户id或用户名称。使用**a+**前缀用户或组ID将强制将其视为数值。如果没有该项,它将首先作为用户名或组名进行尝试。

<os>  <type arch='x86_64'>exe</type>  <init>/bin/systemd</init>  <initarg>--unit</initarg>  <initarg>emergency.service</initarg>  <initenv name='MYENV'>some value</initenv>  <initdir>/my/custom/cwd</initdir>  <inituser>tester</inituser>  <initgroup>1000</initgroup></os>

如果想要使用用户命名空间,设置idmap属性项。uid和gid元素有3个属性项:

  • start:容器的第一个用户ID。其值为0.

  • target:容器的第一个用户ID将会映射到主机中的目标用户ID。

  • count:映射到主机中的用户的容器用户数量。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> +<p>这里有很多不同的方法用来引导操作系统开机,每一个都有优点与缺点。</p> + + + + + + + + + + + + + + + + + + +
+ + + 【翻译】libvirt虚拟机xml配置文件格式(1)General metadata + + http://example.com/2022/08/20/domain_xml_format_general-metadata/ + 2022-08-20T07:56:10.000Z + 2023-12-29T02:38:41.453Z + + 原文网址:https://libvirt.org/formatdomain.html#id8

所有的虚拟机都需要的根元素是domain。其包含两个属性项,typo属性项指明了被用来运行虚拟机的虚拟机管理程序(hypervisor)。该项允许的数值与驱动相关,包含了xen、kvm、hvf(从8.1.0和QEMU 2.12版本起)、qemu和lxc。第二个属性项是id,代表运行客户机(guest)的一个唯一数字标识符。不活跃的客户机没有id。

<domain type='kvm' id='1'>  <name>MyGuest</name>  <uuid>4dea22b3-1d52-d8f3-2516-782e98ab3fa0</uuid>  <genid>43dc0cf8-809b-4adb-9bea-a9abb5f3d90e</genid>  <title>A short description - title - of the domain</title>  <description>Some human readable description</description>  <metadata>    <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>    <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>  </metadata>  ...

name名称

name元素的内容为虚拟机提供了一个小名。这个名称应该仅由字母或数字字符组成,同时在母机(host)范围内该名称必须是唯一的。该名称经常被用来生成文件以储存持久化配置文件。

uuid统一唯一标识符

uuid属性项的内容为虚拟机提供了一个全局唯一标识符。uuid的格式必须符合RFC 4122标准,例如3e3fce45-4f53-4fa7-bb32-11f34168b82b。该属性项可以省略,那么当定义define或创建一个新的虚拟机时,会生成一个随机的uuid。也可以通过
SMBIOS System Information规范提供uuid。(uuid从libvirt 0.0.1版本起提供支持,sysinfo从libvirt 0.8.7版本起提供支持)

genid代际id

从libvirt 4.4.0版本开始,genid属性项使用与uuid相同格式的一个128位、随机加密的整数数值标识符代表全局唯一标识符(Globally Unique Identifier,即GUID),为虚拟机增加一个代际id。当虚拟机重复运行之前已经运行的某些操作时,该值用来帮助通知客户机。例如:

  • 虚拟机开始执行一个快照
  • 虚拟机从备份中恢复
  • 虚拟机陷入到故障恢复操作中
  • 虚拟机正在导入import、拷贝copy或克隆clone

客户机操作系统注意到了这些变化,通过标记分布式数据库的拷贝为脏数据、重新初始化它的随机数生成器等操作做出正常的反应。

libvirt xml解析器既可以接受给定的GUID数值,也可以未配置的情况,这时会生成一个GUID并保存在xml文件中。对于上述的过渡性操作,libvirt将会在重新运行之前改变GUID。

title标题

title是可选配置项,为domain提供了一个简短的描述空间。title中不应该包含任何换行。从libvirt 0.9.10版本开始提供对title的支持。

description描述

description属性项为虚拟机提供了一个人类易读的描述。该数据不会被libvirt使用,但是却包含用户想要的所有内容。从libvirt 0.7.2版本开始提供支持。

metadata元数据

metadata节点可以被应用程序以xml节点(node)/树(tree)的形式用来存储特定数据。应用程序必须使用xml节点(node)/树(tree)中的特定命名空间(namespace),每一个命名空间只有一个顶层(top-level)元素(如果应用程序需要使用结构体,则在其命名空间元素下存在相应子元素)。从libvirt 0.9.10版本开始提供支持。

本章节描述了用来代表作用域的xml文件格式,根据运行的作用于种类的格式变化,有一些可选配置项用来加载文件。对于特定虚拟机的细节可以查看
相关文件

]]>
+ + + <p>原文网址:<a href="https://libvirt.org/formatdomain.html#id8" title="Domain XML format">https://libvirt.org/formatdomain.html#id8</a></p> +<p>所有的虚拟机都需要的根元素是domain。其包含两个属性项,typo属性项指明了被用来运行虚拟机的虚拟机管理程序(hypervisor)。该项允许的数值与驱动相关,包含了xen、kvm、hvf(从8.1.0和QEMU 2.12版本起)、qemu和lxc。第二个属性项是id,代表运行客户机(guest)的一个唯一数字标识符。不活跃的客户机没有id。</p> + + + + + + + + + + + + + + + + + + +
+ + + 哲学三思之一:我是谁 + + http://example.com/2022/08/18/who_am_i-202208181900/ + 2022-08-18T10:56:10.000Z + 2023-12-29T02:38:41.453Z + + 作为一个理科生,我其实并不是十分喜欢哲学,对于一个无法证实或证伪的学科,实在是觉得谈哲学更近似于谈玄,打嘴炮为主。但是在社会生活中,哲学的诸多理论却实在的影响着人类生活的方方面面。尤其是终极三问,我是谁、我从哪里来、我到哪里去是很多人都十分感兴趣的话题,我也不例外。

对于宗教教徒来说,我是谁这个问题就简单了很多,我是神的选民或神的子民,既增加了教众的全体认同感,也使得教众多了一份相对于异教徒或无信者的优越感。我不是信徒,因此不能单纯的以神的选民的身份来搪塞自己,因此就必须实实在在的思考这个问题。

对于人而言,人的自我认同部分集中于其社会关系上,家人、朋友、同事等种种的社会关系塑造了人的外在形象,别人根据这个形象相互交往,而人自身往往也在主动或被动的维护着这个形象。但是面具戴久了,往往很难摘下来,不知不觉中,很多人产生了我为别人活的想法,认为自己主要在维护着一个光鲜的外部形象,却从没有为自己而活。我却不同意这个想法,维护外在形象固然有社会形象的影响,但更多的还是人自己的选择,因为外在形象获得社会的认同,如果不是自身乐在其中,又怎么会数十年不厌其烦的扮演呢。而后面的抱怨,也无非是生活或工作遇到了挫折,对于维护外在形象的动力减弱,但是如果再次有正反馈传来,很多人还是乐此不疲。

但是,哲学考虑的更加深入,外部的社会关系其实是一个可以重复的条件,换言之,如果另一个人拥有和自己相同的外部社会关系,那是不是就说明别人也可以成为自己,自己并不是独一无二的。站在科学的角度,如果我们可以精确的定义人及其生活环境的一切变量,那我们自然可以说可以塑造出一个人出来,把自己作为独一无二是一种傲慢,把自己作为不可复制也是一种傲慢。但是事实是在可见的未来,我们还没有办法算尽一切,因此我们也许可以制造出生物意义上的人,但这个人却无法代替你,即他不是你。如果真的有一天存在那样的高位文明可以复制你的一切,但是他们这么做有什么意义呢?最恶劣的猜想,你对于他们来说只是一个小白鼠,用于满足他们对于人这个物种的研究之用,那又如何?人的强大不是单体的强大,而是人组成的社会或文明总体的强大,一时的强弱并不重要,只要有前进的决心与努力,文明就会精彩,人生也是如此。

所以,与其思考我是谁这样的哲学命题,倒不如抓紧时间做事,做事就是做人,保持前进的欲望,走弯路也强过原地踏步。

]]>
+ + + <p>作为一个理科生,我其实并不是十分喜欢哲学,对于一个无法证实或证伪的学科,实在是觉得谈哲学更近似于谈玄,打嘴炮为主。但是在社会生活中,哲学的诸多理论却实在的影响着人类生活的方方面面。尤其是终极三问,我是谁、我从哪里来、我到哪里去是很多人都十分感兴趣的话题,我也不例外。</p> + + + + + + + + +
+ + + Virtio-设备模拟详解 + + http://example.com/2021/11/09/virtio-202111091100/ + 2021-11-09T03:00:00.000Z + 2023-12-29T02:38:41.453Z + + 什么是virtio

virtio 是一种 I/O 半虚拟化解决方案,是一套通用 I/O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备的抽象。提供了一套上层应用与各 Hypervisor 虚拟化设备(KVM,Xen,VMware等)之间的通信框架和编程接口,减少跨平台所带来的兼容性问题,大大提高驱动程序开发效率。
virtio 协议定义了各类设备与驱动,定义了它们如何初始化,如何通信,如何通知等。其中,最核心的是设备与驱动的通信机制,避免了每次访问外设寄存器都要 vm_exit/vm_enter 的问题。

Virtio架构

从总体上看,virtio 可以分为四层,包括前端 guest 中各种驱动程序模块,后端 Hypervisor (实现在Qemu上)上的处理程序模块,中间用于前后端通信的 virtio 层和 virtio-ring 层,virtio 这一层实现的是虚拟队列接口,算是前后端通信的桥梁,而 virtio-ring 则是该桥梁的具体实现,它实现了两个环形缓冲区,分别用于保存前端驱动程序和后端处理程序执行的信息。

严格来说,virtio 和 virtio-ring 可以看做是一层,virtio-ring 实现了 virtio 的具体通信机制和数据流程。或者这么理解可能更好,virtio 层属于控制层,负责前后端之间的通知机制(kick,notify)和控制流程,而 virtio-vring 则负责具体数据流转发。

vring共享内存基本原理

virtio vring 本质是共享内存,要求使用共享内存的软件模块可以访问这段内存。在虚拟化场景,guest/host 如何实现共享内存呢?

第一个问题:vring 描述符中存放的内存地址是什么?

vring 由 guest 驱动申请,所以 vring 描述符内存放的地址是 GPA。

第二个问题:guest/host 如何实现共享?

总体看有三种情况:

  1. 通过 qemu 模拟的设备,GPA 位于 qemu 的进程地址空间,qemu 天然可以访问。
  2. qemu 外部模拟的设备,比如 vhost-net/vhost-user,需要建立新的内存映射。
  3. 对于一个真实的硬件设备,需要使用 IOMMU 辅助完成地址转换。

以 vhost-net 为例简要说明:

(1)初始化过程中,qemu 通过 ioctl 命令字将 vring 的内存信息通知 vhost-net 内核模块。内存信息包括:GPA/userspace_addr/size 等。

(2)vhost-net 内核模块会记录 GPA 与 userspace_addr(qemu 进程上下文虚拟地址) 的内存映射。

(3)vhost-net 内核模块在启动内核线程时记录此线程为哪个 qemu 虚拟机服务,同时记录 qemu 虚拟机进程的页表信息,在内核线程运行时,使用对应的 qemu 虚拟机进程页表。这样 vhost-net 内核模块就可以访问 qemu 进程上下文的虚拟地址。

PCI设备概述

PCI即Peripheral Component Interconnect,中文意思是“外围器件互联”,是由PCISIG (PCI Special Interest Group)推出的一种局部并行总线标准。PCI总线是由ISA(Industy Standard Architecture)总线发展而来的,是一种同步的独立于处理器的32位或64位局部总线。从结构上看,PCI是在CPU的供应商和原来的系统总线之间插入的一级总线,具体由一个桥接电路实现对这一层的管理,并实现上下之间的接口以协调数据的传送。

PCI总线是一种共享总线,所以需要特定的仲裁器(Arbiter)来决定当前时刻的总线的控制权。一般该仲裁器位于北桥中,而仲裁器(主机)则通过一对引脚,REQ#(request) 和GNT# (grant)来与各个从机连接。
CPU可以直接通过load/store指令来访问PCI设备,PCI设备有如下三种不同内存:

  • MMIO
  • PCI IO space
  • PCI configuration space

guest前端驱动程序操作接口

驱动程序对PCI 配置的操作可以分成以下几个部分:

读写 feature bits
定义了 Guest 和 Host 支持的功能,例如 VIRTIO_NET_F_CSUM bit 表示网络设备是否支持 checksum offload。feature bits 机制提供了未来扩充功能的灵活性,以及兼容旧设备的能力。

读写配置空间

一般通过一个数据结构和一个虚拟设备关联,Guest 可以读写此空间。

读写 status bits

这是一个8bits的长度,Guest用来标识device probe的状态,当 VIRIO_CONFIG_S_DRIVE_OK被设置,那么Guest已经完成了feature协商,可以跟host进行数据交互了。

Device reset

重置设备,配置status bits。

Virtqueue的创建和销毁

提供了分配virtqueue内存和Host的IO空间的初始化操作。

对应的代码如下:

//代码路径virtio-win/VirtIO/windows/VirtIOPCIModern.cstatic const struct virtio_device_ops virtio_pci_device_ops = {.get_config = vio_modern_get_config,.set_config = vio_modern_set_config,.get_config_generation = vio_modern_get_generation,.get_status = vio_modern_get_status,.set_status = vio_modern_set_status,.reset = vio_modern_reset,.get_features = vio_modern_get_features,.set_features = vio_modern_set_features,.set_config_vector = vio_modern_set_config_vector,.set_queue_vector = vio_modern_set_queue_vector,.query_queue_alloc = vio_modern_query_vq_alloc,.setup_queue = vio_modern_setup_vq,.delete_queue = vio_modern_del_vq,};//代码路径virtio-win/virtio/virtio_pci.hstruct virtio_device_ops{// read/write device config and read config generation countervoid (*get_config)(VirtIODevice *vdev, unsigned offset, void *buf, unsigned len);void (*set_config)(VirtIODevice *vdev, unsigned offset, const void *buf, unsigned len);u32 (*get_config_generation)(VirtIODevice *vdev);// read/write device status byte and reset the deviceu8 (*get_status)(VirtIODevice *vdev);void (*set_status)(VirtIODevice *vdev, u8 status);void (*reset)(VirtIODevice *vdev);// get/set device feature bitsu64 (*get_features)(VirtIODevice *vdev);NTSTATUS (*set_features)(VirtIODevice *vdev, u64 features);// set config/queue MSI interrupt vector, returns the new vectoru16 (*set_config_vector)(VirtIODevice *vdev, u16 vector);u16 (*set_queue_vector)(struct virtqueue *vq, u16 vector);// query virtual queue size and memory requirementsNTSTATUS (*query_queue_alloc)(VirtIODevice *vdev,unsigned index, unsigned short *pNumEntries,unsigned long *pRingSize,unsigned long *pHeapSize);// allocate and initialize a queueNTSTATUS (*setup_queue)(struct virtqueue **queue,VirtIODevice *vdev, VirtIOQueueInfo *info,unsigned idx, u16 msix_vec);// tear down and deallocate a queuevoid (*delete_queue)(VirtIOQueueInfo *info);};

virtio 数据流交互机制:virtqueue

Virtio 使用 virtqueue 来实现 I/O 机制,每个 virtqueue 就是一个承载大量数据的队列,具体使用多少个队列取决于需求,例如,virtio 网络驱动程序(virtio-net)使用两个队列(一个用于接受,另一个用于发送),而 virtio 块驱动程序(virtio-blk)仅使用一个队列。

//VirtIO.hstruct virtqueue {VirtIODevice *vdev;struct vring vring;struct {u16 flags;u16 idx;} master_vring_avail;unsigned int index;unsigned int num_unused;unsigned int num_added_since_kick;u16 first_unused;u16 last_used;void *notification_addr;void (*notification_cb)(struct virtqueue *vq);void *opaque[];};

针对Virtqueue的具体操作包含:

1.向queue中添加一个新的buffer,opaque为一个非空的令牌,用于识别buffer,当buffer内容被消耗后,opaque会返回。

//VirtIORing.cint virtqueue_add_buf(struct virtqueue *vq,/* 虚拟化队列 */struct scatterlist sg[],   /* 缓存区描述符数组,长度为驱动程序->设备缓冲区描述符(in)+设备->驱动程序缓冲区描述符(out)*/unsigned int out,/* sg中驱动程序到设备缓冲区描述符数量 */unsigned int in, /* sg中设备到驱动程序缓冲区描述符数量 */void *opaque,/* virtqueue_get_buf 函数返回值(used ring缓冲区起始描述符指针)*/void *va_indirect,   /* 间接页的虚拟地址或空指针*/ULONGLONG phys_indirect) /*间接页的物理地址或空指针*/

2.Guest 通知 host 单个或者多个 buffer 已经添加到 queue 中,调用 virtqueue_notify(),notify 函数会向 queue notify(VIRTIO_PCI_QUEUE_NOTIFY)寄存器写入 queue index 来通知 host。

 //VirtIOPCICommon.cvoid virtqueue_kick(struct virtqueue *vq){if (virtqueue_kick_prepare(vq)) {virtqueue_notify(vq);}}

3.返回使用过的 buffer,len 为写入到 buffer 中数据的长度。获取数据,释放 buffer,更新 vring 描述符表格中的 index。

//VirtIORing.cvoid *virtqueue_get_buf(struct virtqueue *vq, /* the queue */unsigned int *len)/* number of bytes returned by the device */

4.示意 guest 不再需要再知道一个 buffer 已经使用了,也就是关闭 device 的中断。驱动会在初始化时注册一个回调函数,disable_cb()通常在这个 virtqueue 回调函数中使用,用于关闭再次的回调发生。

//VirtIORing.cvoid virtqueue_disable_cb(struct virtqueue *vq)

5.与 disable_cb()刚好相反,用于重新开启设备中断的上报。

//VirtIORing.cbool virtqueue_enable_cb(struct virtqueue *vq) 

virtio 的核心机制就是通过共享内存在前端驱动与后端实现间进行数据传输,共享内存区域被称作 vring。

virtio 传输机制:vring的构成与实现

vring 是 virtio 传输机制的实现,vring 引入 ring buffers 来作为数据传输的载体,包含三个部分:

// virtio_ring.hstruct vring {unsigned int num;struct vring_desc *desc;struct vring_avail *avail;struct vring_used *used;};

Descriptor Table: 描述内存 buffer,主要包括 addr/len 等信息。

// virtio_ring.h/* This marks a buffer as continuing via the next field. */#define VIRTQ_DESC_F_NEXT1/* This marks a buffer as write-only (otherwise read-only). */#define VIRTQ_DESC_F_WRITE2/* This means the buffer contains a list of buffer descriptors. */#define VIRTQ_DESC_F_INDIRECT4/* Virtio ring: 16 bytes.  These can chain together via "next". */struct vring_desc {/* Address (guest-physical). */__virtio64 addr;/* Length. */__virtio32 len;/* The flags as indicated above. */__virtio16 flags;/* We chain unused descriptors via this, too */__virtio16 next;};

Available Ring: 用于驱动通知设备有新的可用的描述符。比如,通知后端设备,有一个待发送的报文描述符。

注意:驱动提供了新的可用描述符后,设备侧不一定要立即使用,比如 virtio-net 会提供一些描述符用于报文接收,当报文到达后按需使用这些描述符即可。

// virtio_ring.h#define VIRTQ_AVAIL_F_NO_INTERRUPT1struct vring_avail {    //控制信息,比如 VIRTQ_AVAIL_F_NO_INTERRUPT 表示驱动侧不想接收通知__virtio16 flags;//idx:驱动将把下一个描述符放在哪里,即 ring 数组的下标__virtio16 idx;    //ring[]:avail 描述符在 Descriptor Table 中的 id__virtio16 ring[];};

Used Ring: 用于通知驱动设备侧已用的描述符。比如,后端设备收到一个报文,需要将报文数据放入可用的描述符,并更新Used Ring,同时通知前端驱动。

// virtio_ring.h#define VIRTQ_USED_F_NO_NOTIFY1struct vring_used_elem {/* Index of start of used descriptor chain. */__virtio32 id;/* Total length of the descriptor chain which was used (written to) */__virtio32 len;};struct vring_used {__virtio16 flags;__virtio16 idx;struct vring_used_elem ring[];};

注意:相比 avail ring 结构多了 len 字段,用于表示设备侧写入的数据长度。对于只读数据类型,不改变 len 长度。

vring 主要通过两个环形缓冲区来完成数据流的转发。

当 guest 向 virtqueue 中写数据时,实际上是向 desc 结构指向的 buffer 中填充数据,完了会更新 available ring,然后再通知 host。
当 host 收到接收数据的通知时,首先从 desc 指向的 buffer 中找到 available ring 中添加的 buffer,映射内存,同时更新 used ring,并通知 guest 接收数据完毕。

qemu后端处理模块

下面以Virtio Network Device设备的初始化为例对qemu中virtio的实现进行说明。

预定义结构体

//代码路径:QEMU/qom/object.cstatic TypeInfo object_info = {.name = TYPE_OBJECT,.instance_size = sizeof(Object),.instance_init = object_instance_init,.abstract = true,};//代码路径:QEMU/hw/core/qdev.cstatic const TypeInfo device_type_info = {.name = TYPE_DEVICE,.parent = TYPE_OBJECT,.instance_size = sizeof(DeviceState),.instance_init = device_initfn,.instance_post_init = device_post_init,.instance_finalize = device_finalize,.class_base_init = device_class_base_init,.class_init = device_class_init,.abstract = true,.class_size = sizeof(DeviceClass),};//代码路径:QEMU/hw/virtio/virtio.cstatic const TypeInfo virtio_device_info = {.name = TYPE_VIRTIO_DEVICE,.parent = TYPE_DEVICE,.instance_size = sizeof(VirtIODevice),.class_init = virtio_device_class_init,.instance_finalize = virtio_device_instance_finalize,.abstract = true,.class_size = sizeof(VirtioDeviceClass),};//代码路径:QEMU/hw/net/virtio-net.cstatic const TypeInfo virtio_net_info = {.name = TYPE_VIRTIO_NET,.parent = TYPE_VIRTIO_DEVICE,.instance_size = sizeof(VirtIONet),.instance_init = virtio_net_instance_init,.class_init = virtio_net_class_init,};static void virtio_register_types(void){type_register_static(&virtio_net_info);}type_init(virtio_register_types)

Virtio Network Device这种类的定义是有多层继承关系的,TYPE_VIRTIO_NET的父类是TYPE_VIRTIO_DEVICE,TYPE_VIRTIO_DEVICE的父类是TYPE_DEVICE,TYPE_DEVICE的父类是TYPE_OBJECT,继承关系就到头了。type_init用于注册这种类,这里面每一层都有class_init,用于从TypeImpl生成xxxClass,也有instance_init,会将xxxClass初始化为实例。

创建VirtQueue

TYPE_VIRTIO_NET层的class_init函数是virtio_net_class_init,它定义了DeviceClass的realize函数为virtio_net_device_realize,如下所示:

//代码路径:QEMU/hw/net/virtio-net.cstatic void virtio_net_class_init(ObjectClass *klass, void *data){DeviceClass *dc = DEVICE_CLASS(klass);VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);dc->props = virtio_net_properties;set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);vdc->realize = virtio_net_device_realize;vdc->unrealize = virtio_net_device_unrealize;vdc->get_config = virtio_net_get_config;vdc->set_config = virtio_net_set_config;vdc->get_features = virtio_net_get_features;vdc->set_features = virtio_net_set_features;vdc->bad_features = virtio_net_bad_features;vdc->reset = virtio_net_reset;vdc->set_status = virtio_net_set_status;vdc->preset_dma_map = virtio_net_preset_dma_map;vdc->set_host_notifier = virtio_net_set_host_notifier;vdc->unset_host_notifier = virtio_net_unset_host_notifier;vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;vdc->load = virtio_net_load_device;vdc->save = virtio_net_save_device;}static void virtio_net_device_realize(DeviceState *dev, Error **errp){VirtIODevice *vdev = VIRTIO_DEVICE(dev);VirtIONet *n = VIRTIO_NET(dev);NetClientState *nc;int i;         … …n->max_queues = MAX(n->nic_conf.peers.queues, 1);… …for (i = 0; i < n->max_queues; i++) {virtio_net_add_queue(n, i);}n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));n->status = VIRTIO_NET_S_LINK_UP;n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, virtio_net_announce_timer, n);if (n->netclient_type) {n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,  n->netclient_type, n->netclient_name, n);} else {n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,  object_get_typename(OBJECT(dev)), dev->id, n);}… …}

上述代码创建了一个VirtIODevice,而virtio_init用来初始化这个设备。VirtIODevice结构里面有一个VirtQueue数组,这就是virtio前端和后端互相传数据的队列,最多有VIRTIO_QUEUE_MAX(1024)个。

但是net设备也有与其他设备不一样的地方,即代码中存在这样的语句n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX。为什么要乘以2呢?这是因为对于网络设备来讲,应该分发送队列和接收队列两个方向。

VirtQueue队列初始化

接下来调用virtio_net_add_queue来初始化队列,可以看出这里面就有发送tx_vq和接收rx_vq两个队列,如下所示:

//代码路径:QEMU/hw/net/virtio-net.cstatic void virtio_net_add_queue(VirtIONet *n, int index){VirtIODevice *vdev = VIRTIO_DEVICE(n);n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,   virtio_net_handle_rx);if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {n->vqs[index].tx_vq =virtio_add_queue(vdev, n->net_conf.tx_queue_size, virtio_net_handle_tx_timer);n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,  virtio_net_tx_timer,  &n->vqs[index]);} else {n->vqs[index].tx_vq =virtio_add_queue(vdev, n->net_conf.tx_queue_size, virtio_net_handle_tx_bh);n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);}n->vqs[index].tx_waiting = 0;n->vqs[index].n = n;}

每个VirtQueue中,都有一个vring用来维护这个队列里面的数据;另外还有函数virtio_net_handle_rx用于处理网络包的接收;函数virtio_net_handle_tx_bh用于网络包的发送。

创建虚拟机网卡

qemu_new_nic会创建一个虚拟机里面的网卡,如下所示:

//代码路径:QEMU/net/net.cNICState *qemu_new_nic(NetClientInfo *info,   NICConf *conf,   const char *model,   const char *name,   void *opaque){NetClientState **peers = conf->peers.ncs;NICState *nic;int i, queues = MAX(1, conf->peers.queues);assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);assert(info->size >= sizeof(NICState));nic = g_malloc0(info->size + sizeof(NetClientState) * queues);nic->ncs = (void *)nic + info->size;nic->conf = conf;nic->opaque = opaque;for (i = 0; i < queues; i++) {qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,  NULL);nic->ncs[i].queue_index = i;}return nic;}static void qemu_net_client_setup(NetClientState *nc,  NetClientInfo *info,  NetClientState *peer,  const char *model,  const char *name,  NetClientDestructor *destructor){nc->info = info;nc->model = g_strdup(model);if (name) {nc->name = g_strdup(name);} else {nc->name = assign_name(nc, model);}if (peer) {assert(!peer->peer);nc->peer = peer;peer->peer = nc;}QTAILQ_INSERT_TAIL(&net_clients, nc, next);nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc);nc->destructor = destructor;QTAILQ_INIT(&nc->filters);}

kernel内核驱动程序

在虚拟机里面的进程发送一个网络包,通过文件系统和Socket调用网络协议栈到达网络设备层,只不过这个不是普通的网络设备,而是virtio_net的驱动。virtio_net的驱动程序代码在Linux操作系统的源代码里面,文件名为linux/drivers/net/virtio_net.c,如下所示:

预定义结构体

static struct virtio_driver virtio_net_driver = {    .feature_table = features,    .feature_table_size = ARRAY_SIZE(features),    .driver.name =KBUILD_MODNAME,    .driver.owner =THIS_MODULE,    .id_table =id_table,    .probe =virtnet_probe,    .remove =virtnet_remove,    .config_changed = virtnet_config_changed,#ifdef CONFIG_PM_SLEEP    .freeze =virtnet_freeze,    .restore =virtnet_restore,#endif};module_virtio_driver(virtio_net_driver);MODULE_DEVICE_TABLE(virtio, id_table);MODULE_DESCRIPTION("Virtio network driver");MODULE_LICENSE("GPL");

模块初始化

在virtio_net的驱动程序的初始化代码中,需要注册一个驱动函数virtio_net_driver。当一个设备驱动作为一个内核模块被初始化的时候,probe函数会被调用,因而来看一下virtnet_probe:

static int virtnet_probe(struct virtio_device *vdev){    int i, err;    struct net_device *dev;    struct virtnet_info *vi;    u16 max_queue_pairs;    int mtu;    … …    dev = alloc_etherdev_mq(sizeof(struct virtnet_info), max_queue_pairs);    if (!dev)        return -ENOMEM;    /* Set up network device as normal. */    dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;    dev->netdev_ops = &virtnet_netdev;    dev->features = NETIF_F_HIGHDMA;    SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops);    SET_NETDEV_DEV(dev, &vdev->dev);    … …    /* Set up our device-specific information */    vi = netdev_priv(dev);    vi->dev = dev;    vi->vdev = vdev;    vdev->priv = vi;    vi->stats = alloc_percpu(struct virtnet_stats);    err = -ENOMEM;    … …    err = init_vqs(vi);    if (err)        goto free_index;    netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs);    netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs);    virtnet_init_settings(dev);    err = register_netdev(dev);    if (err) {        pr_debug("virtio_net: registering device failed\n");        goto free_vqs;    }    virtio_device_ready(vdev);    … …    virtnet_set_queues(vi, vi->curr_queue_pairs);    … …}

在virtnet_probe中会创建struct net_device,并且通过register_netdev注册这个网络设备,这样在客户机里面就能看到这个网卡了。

初始化virtqueue

在virtnet_probe中,还有一件重要的事情就是,init_vqs会初始化发送和接收的virtqueue,如下所示:

static int init_vqs(struct virtnet_info *vi){    int ret;    /* Allocate send & receive queues */    ret = virtnet_alloc_queues(vi);    if (ret)        goto err;    ret = virtnet_find_vqs(vi);    if (ret)        goto err_free;    get_online_cpus();    virtnet_set_affinity(vi);    put_online_cpus();    return 0;err_free:    virtnet_free_queues(vi);err:    return ret;}

Virtqueue实体查找

按照之前的virtio原理,virtqueue是一个介于客户机前端和qemu后端的一个结构,用于在这两端之间传递数据,对于网络设备来讲有发送和接收两个方向的队列。这里建立的struct virtqueue是客户机前端对于队列管理的数据结构。队列的实体需要通过函数virtnet_find_vqs查找或者生成,这里还会指定接收队列的callback函数为skb_recv_done,发送队列的callback函数为skb_xmit_done。当buffer使用发生变化的时候,可以调用这个callback函数进行通知,如下所示:

static int virtnet_find_vqs(struct virtnet_info *vi){    vq_callback_t **callbacks;    struct virtqueue **vqs;    int ret = -ENOMEM;    int i, total_vqs;    const char **names;    /* We expect 1 RX virtqueue followed by 1 TX virtqueue, followed by     * possible N-1 RX/TX queue pairs used in multiqueue mode, followed by     * possible control vq.     */    total_vqs = vi->max_queue_pairs * 2 +        virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ);    /* Allocate space for find_vqs parameters */    vqs = kzalloc(total_vqs * sizeof(*vqs), GFP_KERNEL);    if (!vqs)        goto err_vq;    callbacks = kmalloc(total_vqs * sizeof(*callbacks), GFP_KERNEL);    if (!callbacks)        goto err_callback;    names = kmalloc(total_vqs * sizeof(*names), GFP_KERNEL);    if (!names)        goto err_names;    /* Parameters for control virtqueue, if any */    if (vi->has_cvq) {        callbacks[total_vqs - 1] = NULL;        names[total_vqs - 1] = "control";    }    /* Allocate/initialize parameters for send/receive virtqueues */    for (i = 0; i < vi->max_queue_pairs; i++) {        callbacks[rxq2vq(i)] = skb_recv_done;        callbacks[txq2vq(i)] = skb_xmit_done;        sprintf(vi->rq[i].name, "input.%d", i);        sprintf(vi->sq[i].name, "output.%d", i);        names[rxq2vq(i)] = vi->rq[i].name;        names[txq2vq(i)] = vi->sq[i].name;    }    ret = vi->vdev->config->find_vqs(vi->vdev, total_vqs, vqs, callbacks,                     names);    if (ret)        goto err_find;    if (vi->has_cvq) {        vi->cvq = vqs[total_vqs - 1];        if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))            vi->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;    }    for (i = 0; i < vi->max_queue_pairs; i++) {        vi->rq[i].vq = vqs[rxq2vq(i)];        vi->sq[i].vq = vqs[txq2vq(i)];    }    kfree(names);    kfree(callbacks);    kfree(vqs);    return 0;err_find:    kfree(names);err_names:    kfree(callbacks);err_callback:    kfree(vqs);err_vq:    return ret;}

这里的find_vqs是在struct virtnet_info里的struct virtio_device里的struct virtio_config_ops *config里面定义的。

根据virtio_config_ops的定义,find_vqs会调用vp_modern_find_vqs。在vp_modern_find_vqs 中,vp_find_vqs会调用vp_find_vqs_intx。在vp_find_vqs_intx 中,通过request_irq注册一个中断处理函数vp_interrupt,当设备向队列中写入信息时会产生一个中断,也就是vq中断。中断处理函数需要调用相应队列的回调函数,然后根据队列的数目,依次调用vp_setup_vq完成virtqueue、vring的分配和初始化。

同样,这些数据结构会和virtio后端的VirtIODevice、VirtQueue、vring对应起来,都应该指向刚才创建的那一段内存。客户机同样会通过调用专门给外部设备发送指令的函数iowrite告诉外部的pci设备,这些共享内存的地址。至此前端设备驱动和后端设备驱动之间的两个收发队列就关联好了。

总结

virtio 是 guest 与 host 之间通信的润滑剂,提供了一套通用框架和标准接口或协议来完成两者之间的交互过程,极大地解决了各种驱动程序和不同虚拟化解决方案之间的适配问题。

]]>
+ + + <h2 id="什么是virtio"><a href="#什么是virtio" class="headerlink" title="什么是virtio"></a>什么是virtio</h2><p>virtio 是一种 I&#x2F;O 半虚拟化解决方案,是一套通用 I&#x2F;O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I&#x2F;O 设备的抽象。提供了一套上层应用与各 Hypervisor 虚拟化设备(KVM,Xen,VMware等)之间的通信框架和编程接口,减少跨平台所带来的兼容性问题,大大提高驱动程序开发效率。<br>virtio 协议定义了各类设备与驱动,定义了它们如何初始化,如何通信,如何通知等。其中,最核心的是设备与驱动的通信机制,避免了每次访问外设寄存器都要 vm_exit&#x2F;vm_enter 的问题。</p> + + + + + + + + + + + + +
+ + + kvm windows虚拟机dmp文件转储 + + http://example.com/2021/08/17/kvm_windows_vm_dump-202208171626-karlqyang/ + 2021-08-17T08:34:10.000Z + 2023-12-29T02:38:41.453Z + + kvm下的windows虚拟机转储可从子机侧和母机侧两方面进行。

子机侧

子机侧转储windows子机信息,主要是在主机内部执行virsh inject-nmi UUID指令,通过人为模拟nmi中断实现的,之后子机触发BSOD(蓝屏),继而执行核心文件转储和重启操作。转储的文件名为MEMORY.dmp,根据设置的windows内存核心转储方式不同,该文件一般存储于c:\Windows目录或c:\Windows\Minidump目录下。

母机侧

qemu-monitor-command指令

virsh qemu-monitor-command domain
使用该类指令时,需安装qemu-monitor相关组件,否则无法使用相关指令

#故意让windows蓝屏virsh qemu-monitor-command instance-00000b2e --hmp  nmi#导出guest内存virsh qemu-monitor-command instance-00000b2e --hmp  dump-guest-memory  /home/qemu/instance-00000b2e.dump

dump指令

对应语法如下:

#virsh dump指令语法virsh dump domain corefilepath [--bypass-cache]   { [--live] | [--crash] | [--reset] }   [--verbose] [--memory-only] [--format string]

–live :使用该参数后,qemu进程在内核转储完成后继续运行

–crash :使用该参数后,qemu进程在内核转储完成后因crash事件而暂停

–reset:使用该参数后,qemu进程在内核转储完成后重启
若没有使用上述三个参数,则进程在内核转储完成后保持pause状态

-bypass-cache:使用该参数后,转储文件不会保存os文件系统缓存文件

–memory-only:使用该参数后,转储文件为elf格式文件,且仅包含虚拟机内存及cpu通用寄存器数值

–format:该参数与memory-only绑定使用,指定保存的转储文件的格式,其支持的文件格式(即string的参数值)包含elf、kdump-zlib(使用zlib压缩的kdump压缩格式)、kdump-lzo(使用lzo压缩的kdump压缩格式)、kdump-sbappy(使用sbappy压缩的kdump压缩格式)、win-dmp(windows的完全转储格式)

如果仅使用最简单的dump语法:virsh dump domain corefilepath,则生成的转储文件格式如下:

windows_dump_no-memory-only.dmp:QEMU suspend to disk image

如果使用memory-only语法,但不指定format,则默认格式如下:

windows_dump_memory-only.dmp:ELF 64-bit LSB core file x86-64, version 1 (SYSV)

转储文件格式转换

virsh dump指令在低版本不支持win-dmp格式,因此生成的dump文件的格式以elf文件为主,而windbg是无法直接使用并分析elf文件的,因此需进行转换,将elf文件转换为windows的dmp格式文件。

文件格式的转换主要使用volatilitydistorm两个python库。

#安装工具git clone https://github.com/volatilityfoundation/volatilitypython setup.py install#有inline失败的问题,把函数把inline变为非inlinegit clone https://github.com/gdabah/distormpython setup.py install#复制distorm目录下的_distorm3.so库到python sys.path否则报错cp build/lib.linux-x86_64-2.7/_distorm3.so  /usr/lib64/python2.7/#分析guest内存布局[root@rg1-ostack37 /home/huiwei]# file instance-00000b2e.dumpinstance-00000b2e.dump: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style[root@rg1-ostack37 /home/huiwei]# /bin/vol.py imageinfo -f instance-00000b2e.dumpVolatility Foundation Volatility Framework 2.6.1INFO: volatility.debug: Determining profile based on KDBG search...  Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418 AS Layer1 : WindowsAMD64PagedMemory (Kernel AS) AS Layer2 : QemuCoreDumpElf (Unnamed AS) AS Layer3 : FileAddressSpace (/home/huiwei/instance-00000b2e.dump)  PAE type : No PAE   DTB : 0x187000L  KDBG : 0xf8000164f110L  Number of Processors : 2 Image Type (Service Pack) : 1KPCR for CPU 0 : 0xfffff80001650d00LKPCR for CPU 1 : 0xfffff880009c5000L KUSER_SHARED_DATA : 0xfffff78000000000L   Image date and time : 2020-10-22 09:37:00 UTC+0000 Image local date and time : 2020-10-22 17:37:00 +0800#进行转换[root@rg1-ostack37 /home/huiwei]# /bin/vol.py -f instance-00000b2e.dump --profile Win2008R2SP1x64  raw2dmp -O   instance-00000b2e.dmpVolatility Foundation Volatility Framework 2.6.1Writing data (5.00 MB chunks): |..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................|[root@rg1-ostack37 /home/huiwei]# file instance-00000b2e.dmpinstance-00000b2e.dmp: MS Windows 64bit crash dump, full dump, 1310720 pages

windows目标文件的格式需选择,可通过/bin/vol.py -f XXX.dump –profile相关指令获取本机相关库支持的windows格式进行参数填写。

经过上述处理之后,即可使用windbg工具对相关dmp文件进行分析,以查找系统异常原因。

]]>
+ + + <p>kvm下的windows虚拟机转储可从子机侧和母机侧两方面进行。</p> + + + + + + + + + + + + + + +
+ + + Ubuntu离线情况下安装gcc编译器 + + http://example.com/2020/12/23/Ubuntu_offline_setup_gcc/ + 2020-12-23T03:38:49.000Z + 2023-12-29T02:38:41.453Z + + 系统版本

(1)Ubuntu系统版本:20.04.1LTS;
(2)gcc编译器版本:gcc-9.3.0;
当前可找到的gcc最新版本为gcc-10,但是笔者在安装gcc-10版本时发现一些依赖项无法安装,因此选取了gcc-9版本。如果是在联网情况下安装,直接使用apt相关的安装指令即可。

安装说明

安装建议在root模式下进行,因为各人的系统已安装软件不同,因此使用gcc9.3可能会造成一些依赖库降级,如果不了解的情况下,不建议降级。笔者所用的为linux虚拟机,为学习之用,近似于裸机,因此不考虑对其他软件的影响。
安装顺序不定,因为非裸机情况下,可能已安装部分支持库。笔者建议直接安装上述安装包内的gcc-9_9.3.0-10ubuntu2_amd64.deb,根据提示再去进行其他依赖库的安装。安装完成gcc之后,再安装g++-9_9.3.0-10ubuntu2_amd64.deb。
如果在安装过程中发现部分依赖库不存在,可到https://pkgs.org网站进行下载。

异常处理

安装完成之后,可通过gcc -v与g++ -v指令获取当前的编译器版本信息。但是笔者在安装完成之后发现,无法通过这两条指令获取版本信息,提示命令不存在。但是可通过gcc-9 -v与g++-9 -v指令获取编译器版本信息。因此需进行处理,处理方法如下(转载自https://www.zhihu.com/question/348286167/answer/838993075):
(1)依次执行下面三个命令:

cd //进入home目录touch .bash_profile //生成新文件vim .touch_profile //编辑'.bash_profile'文件

(2)将以下alias命令复制进 .bash_profile 文件:

alias gcc='gcc-9'alias g++='g++-9'

//切记=两边不要加空格

(3)然后执行如下命令:

source .bash_profile

然后,就可以使用gcc -v和g++ -v相关指令了。

]]>
+ + + <h2 id="系统版本"><a href="#系统版本" class="headerlink" title="系统版本"></a>系统版本</h2><p>(1)Ubuntu系统版本:20.04.1LTS;<br>(2)gcc编译器版本:gcc-9.3.0;<br>当前可找到的gcc最新版本为gcc-10,但是笔者在安装gcc-10版本时发现一些依赖项无法安装,因此选取了gcc-9版本。如果是在联网情况下安装,直接使用apt相关的安装指令即可。</p> + + + + + + + + + + +
+ +
diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..5f0c39e --- /dev/null +++ b/categories/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类 | 怀德维宁 + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + + + +
+ + + + + +
+
+ +

分类 +

+ + + +
+ + + + +
+
+
+ 目前共计 3 个分类 +
+ +
+ +
+ + + +
+ + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\345\223\262\345\255\246/index.html" "b/categories/\345\223\262\345\255\246/index.html" new file mode 100644 index 0000000..47d6425 --- /dev/null +++ "b/categories/\345\223\262\345\255\246/index.html" @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类: 哲学 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+

哲学 + 分类 +

+
+ + +
+ 2022 +
+ + + +
+
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/index.html" "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/index.html" new file mode 100644 index 0000000..f0ab2a8 --- /dev/null +++ "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/index.html" @@ -0,0 +1,576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类: 计算机科学 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + + + + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/page/2/index.html" "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/page/2/index.html" new file mode 100644 index 0000000..96e4e60 --- /dev/null +++ "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/page/2/index.html" @@ -0,0 +1,579 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类: 计算机科学 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+

计算机科学 + 分类 +

+
+ + +
+ 2022 +
+ + + + + + + + + + + + +
+ 2021 +
+ + + + +
+ 2020 +
+ + + + + +
+
+ + + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/page/3/index.html" "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/page/3/index.html" new file mode 100644 index 0000000..3cf7195 --- /dev/null +++ "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/page/3/index.html" @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类: 计算机科学 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+
+
+

计算机科学 + 分类 +

+
+ + +
+ 2019 +
+ + + +
+
+ + + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/windbg\345\256\230\346\226\271\346\226\207\346\241\243\347\277\273\350\257\221/index.html" "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/windbg\345\256\230\346\226\271\346\226\207\346\241\243\347\277\273\350\257\221/index.html" new file mode 100644 index 0000000..1d9476d --- /dev/null +++ "b/categories/\350\256\241\347\256\227\346\234\272\347\247\221\345\255\246/windbg\345\256\230\346\226\271\346\226\207\346\241\243\347\277\273\350\257\221/index.html" @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类: windbg官方文档翻译 | 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + + +
+ +
+ + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..e861559 --- /dev/null +++ b/css/main.css @@ -0,0 +1,2565 @@ +:root { + --body-bg-color: #eee; + --content-bg-color: #fff; + --card-bg-color: #f5f5f5; + --text-color: #555; + --blockquote-color: #666; + --link-color: #555; + --link-hover-color: #222; + --brand-color: #fff; + --brand-hover-color: #fff; + --table-row-odd-bg-color: #f9f9f9; + --table-row-hover-bg-color: #f5f5f5; + --menu-item-bg-color: #f5f5f5; + --btn-default-bg: #fff; + --btn-default-color: #555; + --btn-default-border-color: #555; + --btn-default-hover-bg: #222; + --btn-default-hover-color: #fff; + --btn-default-hover-border-color: #222; +} +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} +body { + margin: 0; +} +main { + display: block; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} +a { + background: transparent; +} +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} +b, +strong { + font-weight: bolder; +} +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +img { + border-style: none; +} +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} +button, +input { +/* 1 */ + overflow: visible; +} +button, +select { +/* 1 */ + text-transform: none; +} +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} +button::-moz-focus-inner, +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { + border-style: none; + padding: 0; +} +button:-moz-focusring, +[type='button']:-moz-focusring, +[type='reset']:-moz-focusring, +[type='submit']:-moz-focusring { + outline: 1px dotted ButtonText; +} +fieldset { + padding: 0.35em 0.75em 0.625em; +} +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} +progress { + vertical-align: baseline; +} +textarea { + overflow: auto; +} +[type='checkbox'], +[type='radio'] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { + height: auto; +} +[type='search'] { + outline-offset: -2px; /* 2 */ + -webkit-appearance: textfield; /* 1 */ +} +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; +} +::-webkit-file-upload-button { + font: inherit; /* 2 */ + -webkit-appearance: button; /* 1 */ +} +details { + display: block; +} +summary { + display: list-item; +} +template { + display: none; +} +[hidden] { + display: none; +} +::selection { + background: #262a30; + color: #eee; +} +html, +body { + height: 100%; +} +body { + background: var(--body-bg-color); + color: var(--text-color); + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + font-size: 1em; + line-height: 2; +} +@media (max-width: 991px) { + body { + padding-left: 0 !important; + padding-right: 0 !important; + } +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + font-weight: bold; + line-height: 1.5; + margin: 20px 0 15px; +} +h1 { + font-size: 1.5em; +} +h2 { + font-size: 1.375em; +} +h3 { + font-size: 1.25em; +} +h4 { + font-size: 1.125em; +} +h5 { + font-size: 1em; +} +h6 { + font-size: 0.875em; +} +p { + margin: 0 0 20px 0; +} +a, +span.exturl { + border-bottom: 1px solid #999; + color: var(--link-color); + outline: 0; + text-decoration: none; + overflow-wrap: break-word; + word-wrap: break-word; + cursor: pointer; +} +a:hover, +span.exturl:hover { + border-bottom-color: var(--link-hover-color); + color: var(--link-hover-color); +} +iframe, +img, +video { + display: block; + margin-left: auto; + margin-right: auto; + max-width: 100%; +} +hr { + background-image: repeating-linear-gradient(-45deg, #ddd, #ddd 4px, transparent 4px, transparent 8px); + border: 0; + height: 3px; + margin: 40px 0; +} +blockquote { + border-left: 4px solid #ddd; + color: var(--blockquote-color); + margin: 0; + padding: 0 15px; +} +blockquote cite::before { + content: '-'; + padding: 0 5px; +} +dt { + font-weight: bold; +} +dd { + margin: 0; + padding: 0; +} +kbd { + background-color: #f5f5f5; + background-image: linear-gradient(#eee, #fff, #eee); + border: 1px solid #ccc; + border-radius: 0.2em; + box-shadow: 0.1em 0.1em 0.2em rgba(0,0,0,0.1); + color: #555; + font-family: inherit; + padding: 0.1em 0.3em; + white-space: nowrap; +} +.table-container { + overflow: auto; +} +table { + border-collapse: collapse; + border-spacing: 0; + font-size: 0.875em; + margin: 0 0 20px 0; + width: 100%; +} +tbody tr:nth-of-type(odd) { + background: var(--table-row-odd-bg-color); +} +tbody tr:hover { + background: var(--table-row-hover-bg-color); +} +caption, +th, +td { + font-weight: normal; + padding: 8px; + vertical-align: middle; +} +th, +td { + border: 1px solid #ddd; + border-bottom: 3px solid #ddd; +} +th { + font-weight: 700; + padding-bottom: 10px; +} +td { + border-bottom-width: 1px; +} +.btn { + background: var(--btn-default-bg); + border: 2px solid var(--btn-default-border-color); + border-radius: 2px; + color: var(--btn-default-color); + display: inline-block; + font-size: 0.875em; + line-height: 2; + padding: 0 20px; + text-decoration: none; + transition-property: background-color; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.btn:hover { + background: var(--btn-default-hover-bg); + border-color: var(--btn-default-hover-border-color); + color: var(--btn-default-hover-color); +} +.btn + .btn { + margin: 0 0 8px 8px; +} +.btn .fa-fw { + text-align: left; + width: 1.285714285714286em; +} +.toggle { + line-height: 0; +} +.toggle .toggle-line { + background: #fff; + display: inline-block; + height: 2px; + left: 0; + position: relative; + top: 0; + transition: all 0.4s; + vertical-align: top; + width: 100%; +} +.toggle .toggle-line:not(:first-child) { + margin-top: 3px; +} +.toggle.toggle-arrow .toggle-line-first { + left: 50%; + top: 2px; + transform: rotate(45deg); + width: 50%; +} +.toggle.toggle-arrow .toggle-line-middle { + left: 2px; + width: 90%; +} +.toggle.toggle-arrow .toggle-line-last { + left: 50%; + top: -2px; + transform: rotate(-45deg); + width: 50%; +} +.toggle.toggle-close .toggle-line-first { + transform: rotate(-45deg); + top: 5px; +} +.toggle.toggle-close .toggle-line-middle { + opacity: 0; +} +.toggle.toggle-close .toggle-line-last { + transform: rotate(45deg); + top: -5px; +} +.highlight, +pre { + background: #f7f7f7; + color: #4d4d4c; + line-height: 1.6; + margin: 0 auto 20px; +} +pre, +code { + font-family: consolas, Menlo, monospace, "PingFang SC", "Microsoft YaHei"; +} +code { + background: #eee; + border-radius: 3px; + color: #555; + padding: 2px 4px; + overflow-wrap: break-word; + word-wrap: break-word; +} +.highlight *::selection { + background: #d6d6d6; +} +.highlight pre { + border: 0; + margin: 0; + padding: 10px 0; +} +.highlight table { + border: 0; + margin: 0; + width: auto; +} +.highlight td { + border: 0; + padding: 0; +} +.highlight figcaption { + background: #eff2f3; + color: #4d4d4c; + display: flex; + font-size: 0.875em; + justify-content: space-between; + line-height: 1.2; + padding: 0.5em; +} +.highlight figcaption a { + color: #4d4d4c; +} +.highlight figcaption a:hover { + border-bottom-color: #4d4d4c; +} +.highlight .gutter { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +.highlight .gutter pre { + background: #eff2f3; + color: #869194; + padding-left: 10px; + padding-right: 10px; + text-align: right; +} +.highlight .code pre { + background: #f7f7f7; + padding-left: 10px; + width: 100%; +} +.gist table { + width: auto; +} +.gist table td { + border: 0; +} +pre { + overflow: auto; + padding: 10px; +} +pre code { + background: none; + color: #4d4d4c; + font-size: 0.875em; + padding: 0; + text-shadow: none; +} +pre .deletion { + background: #fdd; +} +pre .addition { + background: #dfd; +} +pre .meta { + color: #eab700; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +pre .comment { + color: #8e908c; +} +pre .variable, +pre .attribute, +pre .tag, +pre .name, +pre .regexp, +pre .ruby .constant, +pre .xml .tag .title, +pre .xml .pi, +pre .xml .doctype, +pre .html .doctype, +pre .css .id, +pre .css .class, +pre .css .pseudo { + color: #c82829; +} +pre .number, +pre .preprocessor, +pre .built_in, +pre .builtin-name, +pre .literal, +pre .params, +pre .constant, +pre .command { + color: #f5871f; +} +pre .ruby .class .title, +pre .css .rules .attribute, +pre .string, +pre .symbol, +pre .value, +pre .inheritance, +pre .header, +pre .ruby .symbol, +pre .xml .cdata, +pre .special, +pre .formula { + color: #718c00; +} +pre .title, +pre .css .hexcolor { + color: #3e999f; +} +pre .function, +pre .python .decorator, +pre .python .title, +pre .ruby .function .title, +pre .ruby .title .keyword, +pre .perl .sub, +pre .javascript .title, +pre .coffeescript .title { + color: #4271ae; +} +pre .keyword, +pre .javascript .function { + color: #8959a8; +} +.blockquote-center { + border-left: none; + margin: 40px 0; + padding: 0; + position: relative; + text-align: center; +} +.blockquote-center .fa { + display: block; + opacity: 0.6; + position: absolute; + width: 100%; +} +.blockquote-center .fa-quote-left { + border-top: 1px solid #ccc; + text-align: left; + top: -20px; +} +.blockquote-center .fa-quote-right { + border-bottom: 1px solid #ccc; + text-align: right; + bottom: -20px; +} +.blockquote-center p, +.blockquote-center div { + text-align: center; +} +.post-body .group-picture img { + margin: 0 auto; + padding: 0 3px; +} +.group-picture-row { + margin-bottom: 6px; + overflow: hidden; +} +.group-picture-column { + float: left; + margin-bottom: 10px; +} +.post-body .label { + color: #555; + display: inline; + padding: 0 2px; +} +.post-body .label.default { + background: #f0f0f0; +} +.post-body .label.primary { + background: #efe6f7; +} +.post-body .label.info { + background: #e5f2f8; +} +.post-body .label.success { + background: #e7f4e9; +} +.post-body .label.warning { + background: #fcf6e1; +} +.post-body .label.danger { + background: #fae8eb; +} +.post-body .tabs { + margin-bottom: 20px; +} +.post-body .tabs, +.tabs-comment { + display: block; + padding-top: 10px; + position: relative; +} +.post-body .tabs ul.nav-tabs, +.tabs-comment ul.nav-tabs { + display: flex; + flex-wrap: wrap; + margin: 0; + margin-bottom: -1px; + padding: 0; +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs, + .tabs-comment ul.nav-tabs { + display: block; + margin-bottom: 5px; + } +} +.post-body .tabs ul.nav-tabs li.tab, +.tabs-comment ul.nav-tabs li.tab { + border-bottom: 1px solid #ddd; + border-left: 1px solid transparent; + border-right: 1px solid transparent; + border-top: 3px solid transparent; + flex-grow: 1; + list-style-type: none; + border-radius: 0 0 0 0; +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs li.tab, + .tabs-comment ul.nav-tabs li.tab { + border-bottom: 1px solid transparent; + border-left: 3px solid transparent; + border-right: 1px solid transparent; + border-top: 1px solid transparent; + } +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs li.tab, + .tabs-comment ul.nav-tabs li.tab { + border-radius: 0; + } +} +.post-body .tabs ul.nav-tabs li.tab a, +.tabs-comment ul.nav-tabs li.tab a { + border-bottom: initial; + display: block; + line-height: 1.8; + outline: 0; + padding: 0.25em 0.75em; + text-align: center; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-out; +} +.post-body .tabs ul.nav-tabs li.tab a i, +.tabs-comment ul.nav-tabs li.tab a i { + width: 1.285714285714286em; +} +.post-body .tabs ul.nav-tabs li.tab.active, +.tabs-comment ul.nav-tabs li.tab.active { + border-bottom: 1px solid transparent; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + border-top: 3px solid #fc6423; +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs li.tab.active, + .tabs-comment ul.nav-tabs li.tab.active { + border-bottom: 1px solid #ddd; + border-left: 3px solid #fc6423; + border-right: 1px solid #ddd; + border-top: 1px solid #ddd; + } +} +.post-body .tabs ul.nav-tabs li.tab.active a, +.tabs-comment ul.nav-tabs li.tab.active a { + color: var(--link-color); + cursor: default; +} +.post-body .tabs .tab-content .tab-pane, +.tabs-comment .tab-content .tab-pane { + border: 1px solid #ddd; + border-top: 0; + padding: 20px 20px 0 20px; + border-radius: 0; +} +.post-body .tabs .tab-content .tab-pane:not(.active), +.tabs-comment .tab-content .tab-pane:not(.active) { + display: none; +} +.post-body .tabs .tab-content .tab-pane.active, +.tabs-comment .tab-content .tab-pane.active { + display: block; +} +.post-body .tabs .tab-content .tab-pane.active:nth-of-type(1), +.tabs-comment .tab-content .tab-pane.active:nth-of-type(1) { + border-radius: 0 0 0 0; +} +@media (max-width: 413px) { + .post-body .tabs .tab-content .tab-pane.active:nth-of-type(1), + .tabs-comment .tab-content .tab-pane.active:nth-of-type(1) { + border-radius: 0; + } +} +.post-body .note { + border-radius: 3px; + margin-bottom: 20px; + padding: 1em; + position: relative; + border: 1px solid #eee; + border-left-width: 5px; +} +.post-body .note h2, +.post-body .note h3, +.post-body .note h4, +.post-body .note h5, +.post-body .note h6 { + margin-top: 0; + border-bottom: initial; + margin-bottom: 0; + padding-top: 0; +} +.post-body .note p:first-child, +.post-body .note ul:first-child, +.post-body .note ol:first-child, +.post-body .note table:first-child, +.post-body .note pre:first-child, +.post-body .note blockquote:first-child, +.post-body .note img:first-child { + margin-top: 0; +} +.post-body .note p:last-child, +.post-body .note ul:last-child, +.post-body .note ol:last-child, +.post-body .note table:last-child, +.post-body .note pre:last-child, +.post-body .note blockquote:last-child, +.post-body .note img:last-child { + margin-bottom: 0; +} +.post-body .note.default { + border-left-color: #777; +} +.post-body .note.default h2, +.post-body .note.default h3, +.post-body .note.default h4, +.post-body .note.default h5, +.post-body .note.default h6 { + color: #777; +} +.post-body .note.primary { + border-left-color: #6f42c1; +} +.post-body .note.primary h2, +.post-body .note.primary h3, +.post-body .note.primary h4, +.post-body .note.primary h5, +.post-body .note.primary h6 { + color: #6f42c1; +} +.post-body .note.info { + border-left-color: #428bca; +} +.post-body .note.info h2, +.post-body .note.info h3, +.post-body .note.info h4, +.post-body .note.info h5, +.post-body .note.info h6 { + color: #428bca; +} +.post-body .note.success { + border-left-color: #5cb85c; +} +.post-body .note.success h2, +.post-body .note.success h3, +.post-body .note.success h4, +.post-body .note.success h5, +.post-body .note.success h6 { + color: #5cb85c; +} +.post-body .note.warning { + border-left-color: #f0ad4e; +} +.post-body .note.warning h2, +.post-body .note.warning h3, +.post-body .note.warning h4, +.post-body .note.warning h5, +.post-body .note.warning h6 { + color: #f0ad4e; +} +.post-body .note.danger { + border-left-color: #d9534f; +} +.post-body .note.danger h2, +.post-body .note.danger h3, +.post-body .note.danger h4, +.post-body .note.danger h5, +.post-body .note.danger h6 { + color: #d9534f; +} +.pagination .prev, +.pagination .next, +.pagination .page-number, +.pagination .space { + display: inline-block; + margin: 0 10px; + padding: 0 11px; + position: relative; + top: -1px; +} +@media (max-width: 767px) { + .pagination .prev, + .pagination .next, + .pagination .page-number, + .pagination .space { + margin: 0 5px; + } +} +.pagination { + border-top: 1px solid #eee; + margin: 120px 0 0; + text-align: center; +} +.pagination .prev, +.pagination .next, +.pagination .page-number { + border-bottom: 0; + border-top: 1px solid #eee; + transition-property: border-color; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.pagination .prev:hover, +.pagination .next:hover, +.pagination .page-number:hover { + border-top-color: #222; +} +.pagination .space { + margin: 0; + padding: 0; +} +.pagination .prev { + margin-left: 0; +} +.pagination .next { + margin-right: 0; +} +.pagination .page-number.current { + background: #ccc; + border-top-color: #ccc; + color: #fff; +} +@media (max-width: 767px) { + .pagination { + border-top: none; + } + .pagination .prev, + .pagination .next, + .pagination .page-number { + border-bottom: 1px solid #eee; + border-top: 0; + margin-bottom: 10px; + padding: 0 10px; + } + .pagination .prev:hover, + .pagination .next:hover, + .pagination .page-number:hover { + border-bottom-color: #222; + } +} +.comments { + margin-top: 60px; + overflow: hidden; +} +.comment-button-group { + display: flex; + flex-wrap: wrap-reverse; + justify-content: center; + margin: 1em 0; +} +.comment-button-group .comment-button { + margin: 0.1em 0.2em; +} +.comment-button-group .comment-button.active { + background: var(--btn-default-hover-bg); + border-color: var(--btn-default-hover-border-color); + color: var(--btn-default-hover-color); +} +.comment-position { + display: none; +} +.comment-position.active { + display: block; +} +.tabs-comment { + background: var(--content-bg-color); + margin-top: 4em; + padding-top: 0; +} +.tabs-comment .comments { + border: 0; + box-shadow: none; + margin-top: 0; + padding-top: 0; +} +.container { + min-height: 100%; + position: relative; +} +.main-inner { + margin: 0 auto; + width: calc(100% - 20px); +} +@media (min-width: 1200px) { + .main-inner { + width: 1160px; + } +} +@media (min-width: 1600px) { + .main-inner { + width: 73%; + } +} +@media (max-width: 767px) { + .content-wrap { + padding: 0 20px; + } +} +.header { + background: transparent; +} +.header-inner { + margin: 0 auto; + width: calc(100% - 20px); +} +@media (min-width: 1200px) { + .header-inner { + width: 1160px; + } +} +@media (min-width: 1600px) { + .header-inner { + width: 73%; + } +} +.site-brand-container { + display: flex; + flex-shrink: 0; + padding: 0 10px; +} +.headband { + background: #222; + height: 3px; +} +.site-meta { + flex-grow: 1; + text-align: center; +} +@media (max-width: 767px) { + .site-meta { + text-align: center; + } +} +.brand { + border-bottom: none; + color: var(--brand-color); + display: inline-block; + line-height: 1.375em; + padding: 0 40px; + position: relative; +} +.brand:hover { + color: var(--brand-hover-color); +} +.site-title { + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + font-size: 1.375em; + font-weight: normal; + margin: 0; +} +.site-subtitle { + color: #ddd; + font-size: 0.8125em; + margin: 10px 0; +} +.use-motion .brand { + opacity: 0; +} +.use-motion .site-title, +.use-motion .site-subtitle, +.use-motion .custom-logo-image { + opacity: 0; + position: relative; + top: -10px; +} +.site-nav-toggle, +.site-nav-right { + display: none; +} +@media (max-width: 767px) { + .site-nav-toggle, + .site-nav-right { + display: flex; + flex-direction: column; + justify-content: center; + } +} +.site-nav-toggle .toggle, +.site-nav-right .toggle { + color: var(--text-color); + padding: 10px; + width: 22px; +} +.site-nav-toggle .toggle .toggle-line, +.site-nav-right .toggle .toggle-line { + background: var(--text-color); + border-radius: 1px; +} +.site-nav { + display: block; +} +@media (max-width: 767px) { + .site-nav { + clear: both; + display: none; + } +} +.site-nav.site-nav-on { + display: block; +} +.menu { + margin-top: 20px; + padding-left: 0; + text-align: center; +} +.menu-item { + display: inline-block; + list-style: none; + margin: 0 10px; +} +@media (max-width: 767px) { + .menu-item { + display: block; + margin-top: 10px; + } + .menu-item.menu-item-search { + display: none; + } +} +.menu-item a, +.menu-item span.exturl { + border-bottom: 0; + display: block; + font-size: 0.8125em; + transition-property: border-color; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +@media (hover: none) { + .menu-item a:hover, + .menu-item span.exturl:hover { + border-bottom-color: transparent !important; + } +} +.menu-item .fa, +.menu-item .fab, +.menu-item .far, +.menu-item .fas { + margin-right: 8px; +} +.menu-item .badge { + display: inline-block; + font-weight: bold; + line-height: 1; + margin-left: 0.35em; + margin-top: 0.35em; + text-align: center; + white-space: nowrap; +} +@media (max-width: 767px) { + .menu-item .badge { + float: right; + margin-left: 0; + } +} +.menu-item-active a, +.menu .menu-item a:hover, +.menu .menu-item span.exturl:hover { + background: var(--menu-item-bg-color); +} +.use-motion .menu-item { + opacity: 0; +} +.sidebar { + background: #222; + bottom: 0; + box-shadow: inset 0 2px 6px #000; + position: fixed; + top: 0; +} +@media (max-width: 991px) { + .sidebar { + display: none; + } +} +.sidebar-inner { + color: #999; + padding: 18px 10px; + text-align: center; +} +.cc-license { + margin-top: 10px; + text-align: center; +} +.cc-license .cc-opacity { + border-bottom: none; + opacity: 0.7; +} +.cc-license .cc-opacity:hover { + opacity: 0.9; +} +.cc-license img { + display: inline-block; +} +.site-author-image { + border: 1px solid #eee; + display: block; + margin: 0 auto; + max-width: 120px; + padding: 2px; +} +.site-author-name { + color: var(--text-color); + font-weight: 600; + margin: 0; + text-align: center; +} +.site-description { + color: #999; + font-size: 0.8125em; + margin-top: 0; + text-align: center; +} +.links-of-author { + margin-top: 15px; +} +.links-of-author a, +.links-of-author span.exturl { + border-bottom-color: #555; + display: inline-block; + font-size: 0.8125em; + margin-bottom: 10px; + margin-right: 10px; + vertical-align: middle; +} +.links-of-author a::before, +.links-of-author span.exturl::before { + background: #74ff0e; + border-radius: 50%; + content: ' '; + display: inline-block; + height: 4px; + margin-right: 3px; + vertical-align: middle; + width: 4px; +} +.sidebar-button { + margin-top: 15px; +} +.sidebar-button a { + border: 1px solid #fc6423; + border-radius: 4px; + color: #fc6423; + display: inline-block; + padding: 0 15px; +} +.sidebar-button a .fa, +.sidebar-button a .fab, +.sidebar-button a .far, +.sidebar-button a .fas { + margin-right: 5px; +} +.sidebar-button a:hover { + background: #fc6423; + border: 1px solid #fc6423; + color: #fff; +} +.sidebar-button a:hover .fa, +.sidebar-button a:hover .fab, +.sidebar-button a:hover .far, +.sidebar-button a:hover .fas { + color: #fff; +} +.links-of-blogroll { + font-size: 0.8125em; + margin-top: 10px; +} +.links-of-blogroll-title { + font-size: 0.875em; + font-weight: 600; + margin-top: 0; +} +.links-of-blogroll-list { + list-style: none; + margin: 0; + padding: 0; +} +#sidebar-dimmer { + display: none; +} +@media (max-width: 767px) { + #sidebar-dimmer { + background: #000; + display: block; + height: 100%; + left: 100%; + opacity: 0; + position: fixed; + top: 0; + width: 100%; + z-index: 1100; + } + .sidebar-active + #sidebar-dimmer { + opacity: 0.7; + transform: translateX(-100%); + transition: opacity 0.5s; + } +} +.sidebar-nav { + margin: 0; + padding-bottom: 20px; + padding-left: 0; +} +.sidebar-nav li { + border-bottom: 1px solid transparent; + color: var(--text-color); + cursor: pointer; + display: inline-block; + font-size: 0.875em; +} +.sidebar-nav li.sidebar-nav-overview { + margin-left: 10px; +} +.sidebar-nav li:hover { + color: #fc6423; +} +.sidebar-nav .sidebar-nav-active { + border-bottom-color: #fc6423; + color: #fc6423; +} +.sidebar-nav .sidebar-nav-active:hover { + color: #fc6423; +} +.sidebar-panel { + display: none; + overflow-x: hidden; + overflow-y: auto; +} +.sidebar-panel-active { + display: block; +} +.sidebar-toggle { + background: #222; + bottom: 45px; + cursor: pointer; + height: 14px; + left: 30px; + padding: 5px; + position: fixed; + width: 14px; + z-index: 1300; +} +@media (max-width: 991px) { + .sidebar-toggle { + left: 20px; + opacity: 0.8; + display: none; + } +} +.sidebar-toggle:hover .toggle-line { + background: #fc6423; +} +.post-toc { + font-size: 0.875em; +} +.post-toc ol { + list-style: none; + margin: 0; + padding: 0 2px 5px 10px; + text-align: left; +} +.post-toc ol > ol { + padding-left: 0; +} +.post-toc ol a { + transition-property: all; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.post-toc .nav-item { + line-height: 1.8; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.post-toc .nav .nav-child { + display: none; +} +.post-toc .nav .active > .nav-child { + display: block; +} +.post-toc .nav .active-current > .nav-child { + display: block; +} +.post-toc .nav .active-current > .nav-child > .nav-item { + display: block; +} +.post-toc .nav .active > a { + border-bottom-color: #fc6423; + color: #fc6423; +} +.post-toc .nav .active-current > a { + color: #fc6423; +} +.post-toc .nav .active-current > a:hover { + color: #fc6423; +} +.site-state { + display: flex; + justify-content: center; + line-height: 1.4; + margin-top: 10px; + overflow: hidden; + text-align: center; + white-space: nowrap; +} +.site-state-item { + padding: 0 15px; +} +.site-state-item:not(:first-child) { + border-left: 1px solid #eee; +} +.site-state-item a { + border-bottom: none; +} +.site-state-item-count { + display: block; + font-size: 1em; + font-weight: 600; + text-align: center; +} +.site-state-item-name { + color: #999; + font-size: 0.8125em; +} +.footer { + color: #999; + font-size: 0.875em; + padding: 20px 0; +} +.footer.footer-fixed { + bottom: 0; + left: 0; + position: absolute; + right: 0; +} +.footer-inner { + box-sizing: border-box; + margin: 0 auto; + text-align: center; + width: calc(100% - 20px); +} +@media (min-width: 1200px) { + .footer-inner { + width: 1160px; + } +} +@media (min-width: 1600px) { + .footer-inner { + width: 73%; + } +} +.languages { + display: inline-block; + font-size: 1.125em; + position: relative; +} +.languages .lang-select-label span { + margin: 0 0.5em; +} +.languages .lang-select { + height: 100%; + left: 0; + opacity: 0; + position: absolute; + top: 0; + width: 100%; +} +.with-love { + color: #ff0000; + display: inline-block; + margin: 0 5px; +} +.powered-by, +.theme-info { + display: inline-block; +} +@-moz-keyframes iconAnimate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.9); + } + 20%, 40%, 60%, 80% { + transform: scale(1.1); + } + 50%, 70% { + transform: scale(1.1); + } +} +@-webkit-keyframes iconAnimate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.9); + } + 20%, 40%, 60%, 80% { + transform: scale(1.1); + } + 50%, 70% { + transform: scale(1.1); + } +} +@-o-keyframes iconAnimate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.9); + } + 20%, 40%, 60%, 80% { + transform: scale(1.1); + } + 50%, 70% { + transform: scale(1.1); + } +} +@keyframes iconAnimate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.9); + } + 20%, 40%, 60%, 80% { + transform: scale(1.1); + } + 50%, 70% { + transform: scale(1.1); + } +} +.back-to-top { + font-size: 12px; + text-align: center; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.back-to-top { + background: #222; + bottom: -100px; + box-sizing: border-box; + color: #fff; + cursor: pointer; + left: 30px; + opacity: 0.6; + padding: 0 6px; + position: fixed; + transition-property: bottom; + z-index: 1300; + width: 24px; +} +.back-to-top span { + display: none; +} +.back-to-top:hover { + color: #fc6423; +} +.back-to-top.back-to-top-on { + bottom: 30px; +} +@media (max-width: 991px) { + .back-to-top { + left: 20px; + opacity: 0.8; + } +} +.post-body { + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + overflow-wrap: break-word; + word-wrap: break-word; +} +@media (min-width: 1200px) { + .post-body { + font-size: 1.125em; + } +} +.post-body .exturl .fa { + font-size: 0.875em; + margin-left: 4px; +} +.post-body .image-caption, +.post-body .figure .caption { + color: #999; + font-size: 0.875em; + font-weight: bold; + line-height: 1; + margin: -20px auto 15px; + text-align: center; +} +.post-sticky-flag { + display: inline-block; + transform: rotate(30deg); +} +.post-button { + margin-top: 40px; + text-align: center; +} +.use-motion .post-block, +.use-motion .pagination, +.use-motion .comments { + opacity: 0; +} +.use-motion .post-header { + opacity: 0; +} +.use-motion .post-body { + opacity: 0; +} +.use-motion .collection-header { + opacity: 0; +} +.posts-collapse { + margin-left: 35px; + position: relative; +} +@media (max-width: 767px) { + .posts-collapse { + margin-left: 0px; + margin-right: 0px; + } +} +.posts-collapse .collection-title { + font-size: 1.125em; + position: relative; +} +.posts-collapse .collection-title::before { + background: #999; + border: 1px solid #fff; + border-radius: 50%; + content: ' '; + height: 10px; + left: 0; + margin-left: -6px; + margin-top: -4px; + position: absolute; + top: 50%; + width: 10px; +} +.posts-collapse .collection-year { + font-size: 1.5em; + font-weight: bold; + margin: 60px 0; + position: relative; +} +.posts-collapse .collection-year::before { + background: #bbb; + border-radius: 50%; + content: ' '; + height: 8px; + left: 0; + margin-left: -4px; + margin-top: -4px; + position: absolute; + top: 50%; + width: 8px; +} +.posts-collapse .collection-header { + display: block; + margin: 0 0 0 20px; +} +.posts-collapse .collection-header small { + color: #bbb; + margin-left: 5px; +} +.posts-collapse .post-header { + border-bottom: 1px dashed #ccc; + margin: 30px 0; + padding-left: 15px; + position: relative; + transition-property: border; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.posts-collapse .post-header::before { + background: #bbb; + border: 1px solid #fff; + border-radius: 50%; + content: ' '; + height: 6px; + left: 0; + margin-left: -4px; + position: absolute; + top: 0.75em; + transition-property: background; + width: 6px; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.posts-collapse .post-header:hover { + border-bottom-color: #666; +} +.posts-collapse .post-header:hover::before { + background: #222; +} +.posts-collapse .post-meta { + display: inline; + font-size: 0.75em; + margin-right: 10px; +} +.posts-collapse .post-title { + display: inline; +} +.posts-collapse .post-title a, +.posts-collapse .post-title span.exturl { + border-bottom: none; + color: var(--link-color); +} +.posts-collapse .post-title .fa-external-link-alt { + font-size: 0.875em; + margin-left: 5px; +} +.posts-collapse::before { + background: #f5f5f5; + content: ' '; + height: 100%; + left: 0; + margin-left: -2px; + position: absolute; + top: 1.25em; + width: 4px; +} +.post-eof { + background: #ccc; + height: 1px; + margin: 80px auto 60px; + text-align: center; + width: 8%; +} +.post-block:last-of-type .post-eof { + display: none; +} +.content { + padding-top: 40px; +} +@media (min-width: 992px) { + .post-body { + text-align: justify; + } +} +@media (max-width: 991px) { + .post-body { + text-align: justify; + } +} +.post-body h1, +.post-body h2, +.post-body h3, +.post-body h4, +.post-body h5, +.post-body h6 { + padding-top: 10px; +} +.post-body h1 .header-anchor, +.post-body h2 .header-anchor, +.post-body h3 .header-anchor, +.post-body h4 .header-anchor, +.post-body h5 .header-anchor, +.post-body h6 .header-anchor { + border-bottom-style: none; + color: #ccc; + float: right; + margin-left: 10px; + visibility: hidden; +} +.post-body h1 .header-anchor:hover, +.post-body h2 .header-anchor:hover, +.post-body h3 .header-anchor:hover, +.post-body h4 .header-anchor:hover, +.post-body h5 .header-anchor:hover, +.post-body h6 .header-anchor:hover { + color: inherit; +} +.post-body h1:hover .header-anchor, +.post-body h2:hover .header-anchor, +.post-body h3:hover .header-anchor, +.post-body h4:hover .header-anchor, +.post-body h5:hover .header-anchor, +.post-body h6:hover .header-anchor { + visibility: visible; +} +.post-body iframe, +.post-body img, +.post-body video { + margin-bottom: 20px; +} +.post-body .video-container { + height: 0; + margin-bottom: 20px; + overflow: hidden; + padding-top: 75%; + position: relative; + width: 100%; +} +.post-body .video-container iframe, +.post-body .video-container object, +.post-body .video-container embed { + height: 100%; + left: 0; + margin: 0; + position: absolute; + top: 0; + width: 100%; +} +.post-gallery { + align-items: center; + display: grid; + grid-gap: 10px; + grid-template-columns: 1fr 1fr 1fr; + margin-bottom: 20px; +} +@media (max-width: 767px) { + .post-gallery { + grid-template-columns: 1fr 1fr; + } +} +.post-gallery a { + border: 0; +} +.post-gallery img { + margin: 0; +} +.posts-expand .post-header { + font-size: 1.125em; +} +.posts-expand .post-title { + font-size: 1.5em; + font-weight: normal; + margin: initial; + text-align: center; + overflow-wrap: break-word; + word-wrap: break-word; +} +.posts-expand .post-title-link { + border-bottom: none; + color: var(--link-color); + display: inline-block; + position: relative; + vertical-align: top; +} +.posts-expand .post-title-link::before { + background: var(--link-color); + bottom: 0; + content: ''; + height: 2px; + left: 0; + position: absolute; + transform: scaleX(0); + visibility: hidden; + width: 100%; + transition-delay: 0s; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.posts-expand .post-title-link:hover::before { + transform: scaleX(1); + visibility: visible; +} +.posts-expand .post-title-link .fa-external-link-alt { + font-size: 0.875em; + margin-left: 5px; +} +.posts-expand .post-meta { + color: #999; + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + font-size: 0.75em; + margin: 3px 0 60px 0; + text-align: center; +} +.posts-expand .post-meta .post-description { + font-size: 0.875em; + margin-top: 2px; +} +.posts-expand .post-meta time { + border-bottom: 1px dashed #999; + cursor: pointer; +} +.post-meta .post-meta-item + .post-meta-item::before { + content: '|'; + margin: 0 0.5em; +} +.post-meta-divider { + margin: 0 0.5em; +} +.post-meta-item-icon { + margin-right: 3px; +} +@media (max-width: 991px) { + .post-meta-item-icon { + display: inline-block; + } +} +@media (max-width: 991px) { + .post-meta-item-text { + display: none; + } +} +.post-nav { + border-top: 1px solid #eee; + display: flex; + justify-content: space-between; + margin-top: 15px; + padding: 10px 5px 0; +} +.post-nav-item { + flex: 1; +} +.post-nav-item a { + border-bottom: none; + display: block; + font-size: 0.875em; + line-height: 1.6; + position: relative; +} +.post-nav-item a:active { + top: 2px; +} +.post-nav-item .fa { + font-size: 0.75em; +} +.post-nav-item:first-child { + margin-right: 15px; +} +.post-nav-item:first-child .fa { + margin-right: 5px; +} +.post-nav-item:last-child { + margin-left: 15px; + text-align: right; +} +.post-nav-item:last-child .fa { + margin-left: 5px; +} +.rtl.post-body p, +.rtl.post-body a, +.rtl.post-body h1, +.rtl.post-body h2, +.rtl.post-body h3, +.rtl.post-body h4, +.rtl.post-body h5, +.rtl.post-body h6, +.rtl.post-body li, +.rtl.post-body ul, +.rtl.post-body ol { + direction: rtl; + font-family: UKIJ Ekran; +} +.rtl.post-title { + font-family: UKIJ Ekran; +} +.post-tags { + margin-top: 40px; + text-align: center; +} +.post-tags a { + display: inline-block; + font-size: 0.8125em; +} +.post-tags a:not(:last-child) { + margin-right: 10px; +} +.post-widgets { + border-top: 1px solid #eee; + margin-top: 15px; + text-align: center; +} +.wp_rating { + height: 20px; + line-height: 20px; + margin-top: 10px; + padding-top: 6px; + text-align: center; +} +.social-like { + display: flex; + font-size: 0.875em; + justify-content: center; + text-align: center; +} +.reward-container { + margin: 20px auto; + padding: 10px 0; + text-align: center; + width: 90%; +} +.reward-container button { + background: transparent; + border: 1px solid #fc6423; + border-radius: 0; + color: #fc6423; + cursor: pointer; + line-height: 2; + outline: 0; + padding: 0 15px; + vertical-align: text-top; +} +.reward-container button:hover { + background: #fc6423; + border: 1px solid transparent; + color: #fa9366; +} +#qr { + padding-top: 20px; +} +#qr a { + border: 0; +} +#qr img { + display: inline-block; + margin: 0.8em 2em 0 2em; + max-width: 100%; + width: 180px; +} +#qr p { + text-align: center; +} +.category-all-page .category-all-title { + text-align: center; +} +.category-all-page .category-all { + margin-top: 20px; +} +.category-all-page .category-list { + list-style: none; + margin: 0; + padding: 0; +} +.category-all-page .category-list-item { + margin: 5px 10px; +} +.category-all-page .category-list-count { + color: #bbb; +} +.category-all-page .category-list-count::before { + content: ' ('; + display: inline; +} +.category-all-page .category-list-count::after { + content: ') '; + display: inline; +} +.category-all-page .category-list-child { + padding-left: 10px; +} +.event-list { + padding: 0; +} +.event-list hr { + background: #222; + margin: 20px 0 45px 0; +} +.event-list hr::after { + background: #222; + color: #fff; + content: 'NOW'; + display: inline-block; + font-weight: bold; + padding: 0 5px; + text-align: right; +} +.event-list .event { + background: #222; + margin: 20px 0; + min-height: 40px; + padding: 15px 0 15px 10px; +} +.event-list .event .event-summary { + color: #fff; + margin: 0; + padding-bottom: 3px; +} +.event-list .event .event-summary::before { + animation: dot-flash 1s alternate infinite ease-in-out; + color: #fff; + content: '\f111'; + display: inline-block; + font-size: 10px; + margin-right: 25px; + vertical-align: middle; + font-family: 'Font Awesome 5 Free'; + font-weight: 900; +} +.event-list .event .event-relative-time { + color: #bbb; + display: inline-block; + font-size: 12px; + font-weight: normal; + padding-left: 12px; +} +.event-list .event .event-details { + color: #fff; + display: block; + line-height: 18px; + margin-left: 56px; + padding-bottom: 6px; + padding-top: 3px; + text-indent: -24px; +} +.event-list .event .event-details::before { + color: #fff; + display: inline-block; + margin-right: 9px; + text-align: center; + text-indent: 0; + width: 14px; + font-family: 'Font Awesome 5 Free'; + font-weight: 900; +} +.event-list .event .event-details.event-location::before { + content: '\f041'; +} +.event-list .event .event-details.event-duration::before { + content: '\f017'; +} +.event-list .event-past { + background: #f5f5f5; +} +.event-list .event-past .event-summary, +.event-list .event-past .event-details { + color: #bbb; + opacity: 0.9; +} +.event-list .event-past .event-summary::before, +.event-list .event-past .event-details::before { + animation: none; + color: #bbb; +} +@-moz-keyframes dot-flash { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.8); + } +} +@-webkit-keyframes dot-flash { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.8); + } +} +@-o-keyframes dot-flash { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.8); + } +} +@keyframes dot-flash { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.8); + } +} +ul.breadcrumb { + font-size: 0.75em; + list-style: none; + margin: 1em 0; + padding: 0 2em; + text-align: center; +} +ul.breadcrumb li { + display: inline; +} +ul.breadcrumb li + li::before { + content: '/\00a0'; + font-weight: normal; + padding: 0.5em; +} +ul.breadcrumb li + li:last-child { + font-weight: bold; +} +.tag-cloud { + text-align: center; +} +.tag-cloud a { + display: inline-block; + margin: 10px; +} +.tag-cloud a:hover { + color: var(--link-hover-color) !important; +} +.header { + margin: 0 auto; + position: relative; + width: calc(100% - 20px); +} +@media (min-width: 1200px) { + .header { + width: 1160px; + } +} +@media (min-width: 1600px) { + .header { + width: 73%; + } +} +@media (max-width: 991px) { + .header { + width: auto; + } +} +.header-inner { + background: var(--content-bg-color); + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12); + overflow: hidden; + padding: 0; + position: absolute; + top: 0; + width: 240px; +} +@media (min-width: 1200px) { + .header-inner { + width: 240px; + } +} +@media (max-width: 991px) { + .header-inner { + border-radius: initial; + position: relative; + width: auto; + } +} +.main-inner { + align-items: flex-start; + display: flex; + justify-content: space-between; + flex-direction: row-reverse; +} +@media (max-width: 991px) { + .main-inner { + width: auto; + } +} +.content-wrap { + background: var(--content-bg-color); + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12); + box-sizing: border-box; + padding: 40px; + width: calc(100% - 252px); +} +@media (max-width: 991px) { + .content-wrap { + border-radius: initial; + padding: 20px; + width: 100%; + } +} +.footer-inner { + padding-left: 260px; +} +.back-to-top { + left: auto; + right: 30px; +} +@media (max-width: 991px) { + .back-to-top { + right: 20px; + } +} +@media (max-width: 991px) { + .footer-inner { + padding-left: 0; + padding-right: 0; + width: auto; + } +} +.site-brand-container { + background: #222; +} +@media (max-width: 991px) { + .site-brand-container { + box-shadow: 0 0 16px rgba(0,0,0,0.5); + } +} +.site-meta { + padding: 20px 0; +} +.brand { + padding: 0; +} +.site-subtitle { + margin: 10px 10px 0; +} +.custom-logo-image { + margin-top: 20px; +} +@media (max-width: 991px) { + .custom-logo-image { + display: none; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .site-nav-toggle, + .site-nav-right { + display: flex; + flex-direction: column; + justify-content: center; + } +} +.site-nav-toggle .toggle, +.site-nav-right .toggle { + color: #fff; +} +.site-nav-toggle .toggle .toggle-line, +.site-nav-right .toggle .toggle-line { + background: #fff; +} +@media (min-width: 768px) and (max-width: 991px) { + .site-nav { + display: none; + } +} +.menu .menu-item { + display: block; + margin: 0; +} +.menu .menu-item a, +.menu .menu-item span.exturl { + padding: 5px 20px; + position: relative; + text-align: left; + transition-property: background-color; +} +@media (max-width: 991px) { + .menu .menu-item.menu-item-search { + display: none; + } +} +.menu .menu-item .badge { + background: #ccc; + border-radius: 10px; + color: #fff; + float: right; + padding: 2px 5px; + text-shadow: 1px 1px 0 rgba(0,0,0,0.1); + vertical-align: middle; +} +.main-menu .menu-item-active a::after { + background: #bbb; + border-radius: 50%; + content: ' '; + height: 6px; + margin-top: -3px; + position: absolute; + right: 15px; + top: 50%; + width: 6px; +} +.sub-menu { + background: var(--content-bg-color); + border-bottom: 1px solid #ddd; + margin: 0; + padding: 6px 0; +} +.sub-menu .menu-item { + display: inline-block; +} +.sub-menu .menu-item a, +.sub-menu .menu-item span.exturl { + background: transparent; + margin: 5px 10px; + padding: initial; +} +.sub-menu .menu-item a:hover, +.sub-menu .menu-item span.exturl:hover { + background: transparent; + color: #fc6423; +} +.sub-menu .menu-item-active a { + border-bottom-color: #fc6423; + color: #fc6423; +} +.sub-menu .menu-item-active a:hover { + border-bottom-color: #fc6423; +} +.sidebar { + background: var(--body-bg-color); + box-shadow: none; + margin-top: 100%; + position: static; + width: 240px; +} +@media (max-width: 991px) { + .sidebar { + display: none; + } +} +.sidebar-toggle { + display: none; +} +.sidebar-inner { + background: var(--content-bg-color); + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + box-sizing: border-box; + color: var(--text-color); + width: 240px; + opacity: 0; +} +.sidebar-inner.affix { + position: fixed; + top: 12px; +} +.sidebar-inner.affix-bottom { + position: absolute; +} +.site-state-item { + padding: 0 10px; +} +.sidebar-button { + border-bottom: 1px dotted #ccc; + border-top: 1px dotted #ccc; + margin-top: 10px; + text-align: center; +} +.sidebar-button a { + border: 0; + color: #fc6423; + display: block; +} +.sidebar-button a:hover { + background: none; + border: 0; + color: #e34603; +} +.sidebar-button a:hover .fa, +.sidebar-button a:hover .fab, +.sidebar-button a:hover .far, +.sidebar-button a:hover .fas { + color: #e34603; +} +.links-of-author { + display: flex; + flex-wrap: wrap; + margin-top: 10px; + justify-content: center; +} +.links-of-author-item { + margin: 5px 0 0; + width: 50%; +} +.links-of-author-item a, +.links-of-author-item span.exturl { + box-sizing: border-box; + display: inline-block; + margin-bottom: 0; + margin-right: 0; + max-width: 216px; + overflow: hidden; + padding: 0 5px; + text-overflow: ellipsis; + white-space: nowrap; +} +.links-of-author-item a, +.links-of-author-item span.exturl { + border-bottom: none; + display: block; + text-decoration: none; +} +.links-of-author-item a::before, +.links-of-author-item span.exturl::before { + display: none; +} +.links-of-author-item a:hover, +.links-of-author-item span.exturl:hover { + background: var(--body-bg-color); + border-radius: 4px; +} +.links-of-author-item .fa, +.links-of-author-item .fab, +.links-of-author-item .far, +.links-of-author-item .fas { + margin-right: 2px; +} +.links-of-blogroll-item { + padding: 0; +} +.content-wrap { + background: initial; + box-shadow: initial; + padding: initial; +} +.post-block { + background: var(--content-bg-color); + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12); + padding: 40px; +} +.post-block + .post-block { + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + margin-top: 12px; +} +.comments { + background: var(--content-bg-color); + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + margin-top: 12px; + padding: 40px; +} +.tabs-comment { + margin-top: 1em; +} +.content { + padding-top: initial; +} +.post-eof { + display: none; +} +.pagination { + background: var(--content-bg-color); + border-radius: initial; + border-top: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + margin: 12px 0 0; + padding: 10px 0 10px; +} +.pagination .prev, +.pagination .next, +.pagination .page-number { + margin-bottom: initial; + top: initial; +} +.main { + padding-bottom: initial; +} +.footer { + bottom: auto; +} +.sub-menu { + border-bottom: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12); +} +.sub-menu + .content .post-block { + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + margin-top: 12px; +} +@media (min-width: 768px) and (max-width: 991px) { + .sub-menu + .content .post-block { + margin-top: 10px; + } +} +@media (max-width: 767px) { + .sub-menu + .content .post-block { + margin-top: 8px; + } +} +.post-body h1, +.post-body h2 { + border-bottom: 1px solid #eee; +} +.post-body h3 { + border-bottom: 1px dotted #eee; +} +@media (min-width: 768px) and (max-width: 991px) { + .content-wrap { + padding: 10px; + } + .posts-expand .post-button { + margin-top: 20px; + } + .post-block { + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + padding: 20px; + } + .post-block + .post-block { + margin-top: 10px; + } + .comments { + margin-top: 10px; + padding: 10px 20px; + } + .pagination { + margin: 10px 0 0; + } +} +@media (max-width: 767px) { + .content-wrap { + padding: 8px; + } + .posts-expand .post-button { + margin: 12px 0; + } + .post-block { + border-radius: initial; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.06), 0 1px 5px 0 rgba(0,0,0,0.12), 0 -1px 0.5px 0 rgba(0,0,0,0.09); + min-height: auto; + padding: 12px; + } + .post-block + .post-block { + margin-top: 8px; + } + .comments { + margin-top: 8px; + padding: 10px 12px; + } + .pagination { + margin: 8px 0 0; + } +} diff --git a/images/algolia_logo.svg b/images/algolia_logo.svg new file mode 100644 index 0000000..4702423 --- /dev/null +++ b/images/algolia_logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/images/apple-touch-icon-next.png b/images/apple-touch-icon-next.png new file mode 100644 index 0000000..86a0d1d Binary files /dev/null and b/images/apple-touch-icon-next.png differ diff --git a/images/avatar.gif b/images/avatar.gif new file mode 100644 index 0000000..28411fd Binary files /dev/null and b/images/avatar.gif differ diff --git a/images/cc-by-nc-nd.svg b/images/cc-by-nc-nd.svg new file mode 100644 index 0000000..79a4f2e --- /dev/null +++ b/images/cc-by-nc-nd.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-nc-sa.svg b/images/cc-by-nc-sa.svg new file mode 100644 index 0000000..bf6bc26 --- /dev/null +++ b/images/cc-by-nc-sa.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-nc.svg b/images/cc-by-nc.svg new file mode 100644 index 0000000..3697349 --- /dev/null +++ b/images/cc-by-nc.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-nd.svg b/images/cc-by-nd.svg new file mode 100644 index 0000000..934c61e --- /dev/null +++ b/images/cc-by-nd.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-sa.svg b/images/cc-by-sa.svg new file mode 100644 index 0000000..463276a --- /dev/null +++ b/images/cc-by-sa.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by.svg b/images/cc-by.svg new file mode 100644 index 0000000..4bccd14 --- /dev/null +++ b/images/cc-by.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-zero.svg b/images/cc-zero.svg new file mode 100644 index 0000000..0f86639 --- /dev/null +++ b/images/cc-zero.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/favicon-16x16-next.png b/images/favicon-16x16-next.png new file mode 100644 index 0000000..de8c5d3 Binary files /dev/null and b/images/favicon-16x16-next.png differ diff --git a/images/favicon-32x32-next.png b/images/favicon-32x32-next.png new file mode 100644 index 0000000..e02f5f4 Binary files /dev/null and b/images/favicon-32x32-next.png differ diff --git a/images/logo.svg b/images/logo.svg new file mode 100644 index 0000000..cbb3937 --- /dev/null +++ b/images/logo.svg @@ -0,0 +1,23 @@ + +image/svg+xml diff --git a/index.html b/index.html new file mode 100644 index 0000000..35e419e --- /dev/null +++ b/index.html @@ -0,0 +1,1319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 怀德维宁 + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + +

怀德维宁

+ +
+

大邦维屏,大宗维翰。怀德维宁,宗子维城。

+
+ + +
+ + + + + + + + + +
+
+ + +
+ + 0% +
+ + +
+
+
+ + +
+ + + + +
+ + + + + +
+

+ + +

+ + +
+ + + + +
+ + +

大多数调试器命令都是用虚拟地址而非物理地址作为输出及输出参数。然而,也同时存在物理地址起作用的场景。

+ +
+ + 阅读全文 » + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + +
+ + + + + +
+

+ + +

+ + +
+ + + + +
+ + +

与全局变量相同,局部变量也存储于符号文件中。更加一致的是,调试器也将局部变量的名称解释为地址。可以安装与全局变量相同的方式进行读写操作。但是,如果需要向命令指定某个符号是本地符号,请在符号前面添加美元符号($)和感叹号(!),例如$!var。

+ +
+ + 阅读全文 » + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + +
+ + + + + +
+

+ + +

+ + +
+ + + + +
+ + +

全局变量的名称存储在应用程序编译时创建的符号文件中。调试器将全局变量的名称解释为一个虚拟地址。任何接受地址作为参数的命令都可以接受变量名称作为参数。因此,可以使用在之前的《通过虚拟地址访问内存》章节中描述的全部指令读写全局变量。

+ +
+ + 阅读全文 » + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + +
+ + + + + +
+

+ + +

+ + +
+ + + + +
+ + +

为了从物理地址中读取参数,可以使用!db、!dc、!dd、!dp、!du和!dw等扩展命令。

+

向物理地址中写入数据,可以使用!eb和!ed扩展命令。

+

fp(fill physical memory填充物理内存)指令向物理内存范围内写入了模板值,不停重复,知道内存被完全填充。

+

当在内核模式中使用windbg时,可以在windbg的内存窗口中直接进行物理内存的读写操作。

+

要在物理内存中搜索一段数据或一系列数据,请使用 !search 扩展命令。

+

想要查看更多与物理地址相关的信息,可以查看转换虚拟地址到物理地址这一章节。

+ + +
+ + + + +
+
+
+
+ + + + + + + +
+ + + + + +
+

+ + +

+ + +
+ + + + +
+ + +

1 名称

OPENSSL_ia32cap:x86[_64]架构处理器能力向量(processor capabilities vector)

+ +
+ + 阅读全文 » + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + +
+ + + + + +
+

+ + +

+ + +
+ + + + +
+ + +

在调试过程中可以通过使用多个指令来访问内存或内存区域。visual studio和windbg提供命令行命令时,也提供了用户图形界面,用户可以用图形界面来查看和编辑内存。详情可以参考windbg帮助文档中的在visual studio中查看和编辑内存和寄存器,以及在windbg中查看和编辑内存两个章节。

+ +
+ + 阅读全文 » + +
+ + + +
+ + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/algolia-search.js b/js/algolia-search.js new file mode 100644 index 0000000..01a5f0b --- /dev/null +++ b/js/algolia-search.js @@ -0,0 +1,124 @@ +/* global instantsearch, algoliasearch, CONFIG */ + +document.addEventListener('DOMContentLoaded', () => { + const algoliaSettings = CONFIG.algolia; + const { indexName, appID, apiKey } = algoliaSettings; + + let search = instantsearch({ + indexName, + searchClient : algoliasearch(appID, apiKey), + searchFunction: helper => { + let searchInput = document.querySelector('.search-input'); + if (searchInput.value) { + helper.search(); + } + } + }); + + window.pjax && search.on('render', () => { + window.pjax.refresh(document.getElementById('algolia-hits')); + }); + + // Registering Widgets + search.addWidgets([ + instantsearch.widgets.configure({ + hitsPerPage: algoliaSettings.hits.per_page || 10 + }), + + instantsearch.widgets.searchBox({ + container : '.search-input-container', + placeholder : algoliaSettings.labels.input_placeholder, + // Hide default icons of algolia search + showReset : false, + showSubmit : false, + showLoadingIndicator: false, + cssClasses : { + input: 'search-input' + } + }), + + instantsearch.widgets.stats({ + container: '#algolia-stats', + templates: { + text: data => { + let stats = algoliaSettings.labels.hits_stats + .replace(/\$\{hits}/, data.nbHits) + .replace(/\$\{time}/, data.processingTimeMS); + return `${stats} + + Algolia + +
`; + } + } + }), + + instantsearch.widgets.hits({ + container: '#algolia-hits', + templates: { + item: data => { + let link = data.permalink ? data.permalink : CONFIG.root + data.path; + return `${data._highlightResult.title.value}`; + }, + empty: data => { + return `
+ ${algoliaSettings.labels.hits_empty.replace(/\$\{query}/, data.query)} +
`; + } + }, + cssClasses: { + item: 'algolia-hit-item' + } + }), + + instantsearch.widgets.pagination({ + container: '#algolia-pagination', + scrollTo : false, + showFirst: false, + showLast : false, + templates: { + first : '', + last : '', + previous: '', + next : '' + }, + cssClasses: { + root : 'pagination', + item : 'pagination-item', + link : 'page-number', + selectedItem: 'current', + disabledItem: 'disabled-item' + } + }) + ]); + + search.start(); + + // Handle and trigger popup window + document.querySelectorAll('.popup-trigger').forEach(element => { + element.addEventListener('click', () => { + document.body.style.overflow = 'hidden'; + document.querySelector('.search-pop-overlay').classList.add('search-active'); + document.querySelector('.search-input').focus(); + }); + }); + + // Monitor main search box + const onPopupClose = () => { + document.body.style.overflow = ''; + document.querySelector('.search-pop-overlay').classList.remove('search-active'); + }; + + document.querySelector('.search-pop-overlay').addEventListener('click', event => { + if (event.target === document.querySelector('.search-pop-overlay')) { + onPopupClose(); + } + }); + document.querySelector('.popup-btn-close').addEventListener('click', onPopupClose); + window.addEventListener('pjax:success', onPopupClose); + window.addEventListener('keyup', event => { + if (event.key === 'Escape') { + onPopupClose(); + } + }); +}); diff --git a/js/bookmark.js b/js/bookmark.js new file mode 100644 index 0000000..7c2438e --- /dev/null +++ b/js/bookmark.js @@ -0,0 +1,56 @@ +/* global CONFIG */ + +document.addEventListener('DOMContentLoaded', () => { + 'use strict'; + + var doSaveScroll = () => { + localStorage.setItem('bookmark' + location.pathname, window.scrollY); + }; + + var scrollToMark = () => { + var top = localStorage.getItem('bookmark' + location.pathname); + top = parseInt(top, 10); + // If the page opens with a specific hash, just jump out + if (!isNaN(top) && location.hash === '') { + // Auto scroll to the position + window.anime({ + targets : document.scrollingElement, + duration : 200, + easing : 'linear', + scrollTop: top + }); + } + }; + // Register everything + var init = function(trigger) { + // Create a link element + var link = document.querySelector('.book-mark-link'); + // Scroll event + window.addEventListener('scroll', () => link.classList.toggle('book-mark-link-fixed', window.scrollY === 0)); + // Register beforeunload event when the trigger is auto + if (trigger === 'auto') { + // Register beforeunload event + window.addEventListener('beforeunload', doSaveScroll); + window.addEventListener('pjax:send', doSaveScroll); + } + // Save the position by clicking the icon + link.addEventListener('click', () => { + doSaveScroll(); + window.anime({ + targets : link, + duration: 200, + easing : 'linear', + top : -30, + complete: () => { + setTimeout(() => { + link.style.top = ''; + }, 400); + } + }); + }); + scrollToMark(); + window.addEventListener('pjax:success', scrollToMark); + }; + + init(CONFIG.bookmark.save); +}); diff --git a/js/local-search.js b/js/local-search.js new file mode 100644 index 0000000..31f945f --- /dev/null +++ b/js/local-search.js @@ -0,0 +1,278 @@ +/* global CONFIG */ + +document.addEventListener('DOMContentLoaded', () => { + // Popup Window + let isfetched = false; + let datas; + let isXml = true; + // Search DB path + let searchPath = CONFIG.path; + if (searchPath.length === 0) { + searchPath = 'search.xml'; + } else if (searchPath.endsWith('json')) { + isXml = false; + } + const input = document.querySelector('.search-input'); + const resultContent = document.getElementById('search-result'); + + const getIndexByWord = (word, text, caseSensitive) => { + if (CONFIG.localsearch.unescape) { + let div = document.createElement('div'); + div.innerText = word; + word = div.innerHTML; + } + let wordLen = word.length; + if (wordLen === 0) return []; + let startPosition = 0; + let position = []; + let index = []; + if (!caseSensitive) { + text = text.toLowerCase(); + word = word.toLowerCase(); + } + while ((position = text.indexOf(word, startPosition)) > -1) { + index.push({ position, word }); + startPosition = position + wordLen; + } + return index; + }; + + // Merge hits into slices + const mergeIntoSlice = (start, end, index, searchText) => { + let item = index[index.length - 1]; + let { position, word } = item; + let hits = []; + let searchTextCountInSlice = 0; + while (position + word.length <= end && index.length !== 0) { + if (word === searchText) { + searchTextCountInSlice++; + } + hits.push({ + position, + length: word.length + }); + let wordEnd = position + word.length; + + // Move to next position of hit + index.pop(); + while (index.length !== 0) { + item = index[index.length - 1]; + position = item.position; + word = item.word; + if (wordEnd > position) { + index.pop(); + } else { + break; + } + } + } + return { + hits, + start, + end, + searchTextCount: searchTextCountInSlice + }; + }; + + // Highlight title and content + const highlightKeyword = (text, slice) => { + let result = ''; + let prevEnd = slice.start; + slice.hits.forEach(hit => { + result += text.substring(prevEnd, hit.position); + let end = hit.position + hit.length; + result += `${text.substring(hit.position, end)}`; + prevEnd = end; + }); + result += text.substring(prevEnd, slice.end); + return result; + }; + + const inputEventFunction = () => { + if (!isfetched) return; + let searchText = input.value.trim().toLowerCase(); + let keywords = searchText.split(/[-\s]+/); + if (keywords.length > 1) { + keywords.push(searchText); + } + let resultItems = []; + if (searchText.length > 0) { + // Perform local searching + datas.forEach(({ title, content, url }) => { + let titleInLowerCase = title.toLowerCase(); + let contentInLowerCase = content.toLowerCase(); + let indexOfTitle = []; + let indexOfContent = []; + let searchTextCount = 0; + keywords.forEach(keyword => { + indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false)); + indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false)); + }); + + // Show search results + if (indexOfTitle.length > 0 || indexOfContent.length > 0) { + let hitCount = indexOfTitle.length + indexOfContent.length; + // Sort index by position of keyword + [indexOfTitle, indexOfContent].forEach(index => { + index.sort((itemLeft, itemRight) => { + if (itemRight.position !== itemLeft.position) { + return itemRight.position - itemLeft.position; + } + return itemLeft.word.length - itemRight.word.length; + }); + }); + + let slicesOfTitle = []; + if (indexOfTitle.length !== 0) { + let tmp = mergeIntoSlice(0, title.length, indexOfTitle, searchText); + searchTextCount += tmp.searchTextCountInSlice; + slicesOfTitle.push(tmp); + } + + let slicesOfContent = []; + while (indexOfContent.length !== 0) { + let item = indexOfContent[indexOfContent.length - 1]; + let { position, word } = item; + // Cut out 100 characters + let start = position - 20; + let end = position + 80; + if (start < 0) { + start = 0; + } + if (end < position + word.length) { + end = position + word.length; + } + if (end > content.length) { + end = content.length; + } + let tmp = mergeIntoSlice(start, end, indexOfContent, searchText); + searchTextCount += tmp.searchTextCountInSlice; + slicesOfContent.push(tmp); + } + + // Sort slices in content by search text's count and hits' count + slicesOfContent.sort((sliceLeft, sliceRight) => { + if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) { + return sliceRight.searchTextCount - sliceLeft.searchTextCount; + } else if (sliceLeft.hits.length !== sliceRight.hits.length) { + return sliceRight.hits.length - sliceLeft.hits.length; + } + return sliceLeft.start - sliceRight.start; + }); + + // Select top N slices in content + let upperBound = parseInt(CONFIG.localsearch.top_n_per_article, 10); + if (upperBound >= 0) { + slicesOfContent = slicesOfContent.slice(0, upperBound); + } + + let resultItem = ''; + + if (slicesOfTitle.length !== 0) { + resultItem += `
  • ${highlightKeyword(title, slicesOfTitle[0])}`; + } else { + resultItem += `
  • ${title}`; + } + + slicesOfContent.forEach(slice => { + resultItem += `

    ${highlightKeyword(content, slice)}...

    `; + }); + + resultItem += '
  • '; + resultItems.push({ + item: resultItem, + id : resultItems.length, + hitCount, + searchTextCount + }); + } + }); + } + if (keywords.length === 1 && keywords[0] === '') { + resultContent.innerHTML = '
    '; + } else if (resultItems.length === 0) { + resultContent.innerHTML = '
    '; + } else { + resultItems.sort((resultLeft, resultRight) => { + if (resultLeft.searchTextCount !== resultRight.searchTextCount) { + return resultRight.searchTextCount - resultLeft.searchTextCount; + } else if (resultLeft.hitCount !== resultRight.hitCount) { + return resultRight.hitCount - resultLeft.hitCount; + } + return resultRight.id - resultLeft.id; + }); + resultContent.innerHTML = ``; + window.pjax && window.pjax.refresh(resultContent); + } + }; + + const fetchData = () => { + fetch(CONFIG.root + searchPath) + .then(response => response.text()) + .then(res => { + // Get the contents from search data + isfetched = true; + datas = isXml ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => { + return { + title : element.querySelector('title').textContent, + content: element.querySelector('content').textContent, + url : element.querySelector('url').textContent + }; + }) : JSON.parse(res); + // Only match articles with not empty titles + datas = datas.filter(data => data.title).map(data => { + data.title = data.title.trim(); + data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''; + data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/'); + return data; + }); + // Remove loading animation + document.getElementById('no-result').innerHTML = ''; + inputEventFunction(); + }); + }; + + if (CONFIG.localsearch.preload) { + fetchData(); + } + + if (CONFIG.localsearch.trigger === 'auto') { + input.addEventListener('input', inputEventFunction); + } else { + document.querySelector('.search-icon').addEventListener('click', inputEventFunction); + input.addEventListener('keypress', event => { + if (event.key === 'Enter') { + inputEventFunction(); + } + }); + } + + // Handle and trigger popup window + document.querySelectorAll('.popup-trigger').forEach(element => { + element.addEventListener('click', () => { + document.body.style.overflow = 'hidden'; + document.querySelector('.search-pop-overlay').classList.add('search-active'); + input.focus(); + if (!isfetched) fetchData(); + }); + }); + + // Monitor main search box + const onPopupClose = () => { + document.body.style.overflow = ''; + document.querySelector('.search-pop-overlay').classList.remove('search-active'); + }; + + document.querySelector('.search-pop-overlay').addEventListener('click', event => { + if (event.target === document.querySelector('.search-pop-overlay')) { + onPopupClose(); + } + }); + document.querySelector('.popup-btn-close').addEventListener('click', onPopupClose); + window.addEventListener('pjax:success', onPopupClose); + window.addEventListener('keyup', event => { + if (event.key === 'Escape') { + onPopupClose(); + } + }); +}); diff --git a/js/motion.js b/js/motion.js new file mode 100644 index 0000000..026199a --- /dev/null +++ b/js/motion.js @@ -0,0 +1,177 @@ +/* global NexT, CONFIG, Velocity */ + +if (window.$ && window.$.Velocity) window.Velocity = window.$.Velocity; + +NexT.motion = {}; + +NexT.motion.integrator = { + queue : [], + cursor: -1, + init : function() { + this.queue = []; + this.cursor = -1; + return this; + }, + add: function(fn) { + this.queue.push(fn); + return this; + }, + next: function() { + this.cursor++; + var fn = this.queue[this.cursor]; + typeof fn === 'function' && fn(NexT.motion.integrator); + }, + bootstrap: function() { + this.next(); + } +}; + +NexT.motion.middleWares = { + logo: function(integrator) { + var sequence = []; + var brand = document.querySelector('.brand'); + var image = document.querySelector('.custom-logo-image'); + var title = document.querySelector('.site-title'); + var subtitle = document.querySelector('.site-subtitle'); + var logoLineTop = document.querySelector('.logo-line-before i'); + var logoLineBottom = document.querySelector('.logo-line-after i'); + + brand && sequence.push({ + e: brand, + p: {opacity: 1}, + o: {duration: 200} + }); + + function getMistLineSettings(element, translateX) { + return { + e: element, + p: {translateX}, + o: { + duration : 500, + sequenceQueue: false + } + }; + } + + function pushImageToSequence() { + sequence.push({ + e: image, + p: {opacity: 1, top: 0}, + o: {duration: 200} + }); + } + + CONFIG.scheme === 'Mist' && logoLineTop && logoLineBottom + && sequence.push( + getMistLineSettings(logoLineTop, '100%'), + getMistLineSettings(logoLineBottom, '-100%') + ); + + CONFIG.scheme === 'Muse' && image && pushImageToSequence(); + + title && sequence.push({ + e: title, + p: {opacity: 1, top: 0}, + o: {duration: 200} + }); + + subtitle && sequence.push({ + e: subtitle, + p: {opacity: 1, top: 0}, + o: {duration: 200} + }); + + (CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini') && image && pushImageToSequence(); + + if (sequence.length > 0) { + sequence[sequence.length - 1].o.complete = function() { + integrator.next(); + }; + Velocity.RunSequence(sequence); + } else { + integrator.next(); + } + + if (CONFIG.motion.async) { + integrator.next(); + } + }, + + menu: function(integrator) { + Velocity(document.querySelectorAll('.menu-item'), 'transition.slideDownIn', { + display : null, + duration: 200, + complete: function() { + integrator.next(); + } + }); + + if (CONFIG.motion.async) { + integrator.next(); + } + }, + + subMenu: function(integrator) { + var subMenuItem = document.querySelectorAll('.sub-menu .menu-item'); + if (subMenuItem.length > 0) { + subMenuItem.forEach(element => { + element.style.opacity = 1; + }); + } + integrator.next(); + }, + + postList: function(integrator) { + var postBlock = document.querySelectorAll('.post-block, .pagination, .comments'); + var postBlockTransition = CONFIG.motion.transition.post_block; + var postHeader = document.querySelectorAll('.post-header'); + var postHeaderTransition = CONFIG.motion.transition.post_header; + var postBody = document.querySelectorAll('.post-body'); + var postBodyTransition = CONFIG.motion.transition.post_body; + var collHeader = document.querySelectorAll('.collection-header'); + var collHeaderTransition = CONFIG.motion.transition.coll_header; + + if (postBlock.length > 0) { + var postMotionOptions = window.postMotionOptions || { + stagger : 100, + drag : true, + complete: function() { + integrator.next(); + } + }; + + if (CONFIG.motion.transition.post_block) { + Velocity(postBlock, 'transition.' + postBlockTransition, postMotionOptions); + } + if (CONFIG.motion.transition.post_header) { + Velocity(postHeader, 'transition.' + postHeaderTransition, postMotionOptions); + } + if (CONFIG.motion.transition.post_body) { + Velocity(postBody, 'transition.' + postBodyTransition, postMotionOptions); + } + if (CONFIG.motion.transition.coll_header) { + Velocity(collHeader, 'transition.' + collHeaderTransition, postMotionOptions); + } + } + if (CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini') { + integrator.next(); + } + }, + + sidebar: function(integrator) { + var sidebarAffix = document.querySelector('.sidebar-inner'); + var sidebarAffixTransition = CONFIG.motion.transition.sidebar; + // Only for Pisces | Gemini. + if (sidebarAffixTransition && (CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini')) { + Velocity(sidebarAffix, 'transition.' + sidebarAffixTransition, { + display : null, + duration: 200, + complete: function() { + // After motion complete need to remove transform from sidebar to let affix work on Pisces | Gemini. + sidebarAffix.style.transform = 'initial'; + } + }); + } + integrator.next(); + } +}; diff --git a/js/next-boot.js b/js/next-boot.js new file mode 100644 index 0000000..52ec9ae --- /dev/null +++ b/js/next-boot.js @@ -0,0 +1,114 @@ +/* global NexT, CONFIG, Velocity */ + +NexT.boot = {}; + +NexT.boot.registerEvents = function() { + + NexT.utils.registerScrollPercent(); + NexT.utils.registerCanIUseTag(); + + // Mobile top menu bar. + document.querySelector('.site-nav-toggle .toggle').addEventListener('click', () => { + event.currentTarget.classList.toggle('toggle-close'); + var siteNav = document.querySelector('.site-nav'); + var animateAction = siteNav.classList.contains('site-nav-on') ? 'slideUp' : 'slideDown'; + + if (typeof Velocity === 'function') { + Velocity(siteNav, animateAction, { + duration: 200, + complete: function() { + siteNav.classList.toggle('site-nav-on'); + } + }); + } else { + siteNav.classList.toggle('site-nav-on'); + } + }); + + var TAB_ANIMATE_DURATION = 200; + document.querySelectorAll('.sidebar-nav li').forEach((element, index) => { + element.addEventListener('click', event => { + var item = event.currentTarget; + var activeTabClassName = 'sidebar-nav-active'; + var activePanelClassName = 'sidebar-panel-active'; + if (item.classList.contains(activeTabClassName)) return; + + var targets = document.querySelectorAll('.sidebar-panel'); + var target = targets[index]; + var currentTarget = targets[1 - index]; + window.anime({ + targets : currentTarget, + duration: TAB_ANIMATE_DURATION, + easing : 'linear', + opacity : 0, + complete: () => { + // Prevent adding TOC to Overview if Overview was selected when close & open sidebar. + currentTarget.classList.remove(activePanelClassName); + target.style.opacity = 0; + target.classList.add(activePanelClassName); + window.anime({ + targets : target, + duration: TAB_ANIMATE_DURATION, + easing : 'linear', + opacity : 1 + }); + } + }); + + [...item.parentNode.children].forEach(element => { + element.classList.remove(activeTabClassName); + }); + item.classList.add(activeTabClassName); + }); + }); + + window.addEventListener('resize', NexT.utils.initSidebarDimension); + + window.addEventListener('hashchange', () => { + var tHash = location.hash; + if (tHash !== '' && !tHash.match(/%\S{2}/)) { + var target = document.querySelector(`.tabs ul.nav-tabs li a[href="${tHash}"]`); + target && target.click(); + } + }); +}; + +NexT.boot.refresh = function() { + + /** + * Register JS handlers by condition option. + * Need to add config option in Front-End at 'layout/_partials/head.swig' file. + */ + CONFIG.fancybox && NexT.utils.wrapImageWithFancyBox(); + CONFIG.mediumzoom && window.mediumZoom('.post-body :not(a) > img, .post-body > img'); + CONFIG.lazyload && window.lozad('.post-body img').observe(); + CONFIG.pangu && window.pangu.spacingPage(); + + CONFIG.exturl && NexT.utils.registerExtURL(); + CONFIG.copycode.enable && NexT.utils.registerCopyCode(); + NexT.utils.registerTabsTag(); + NexT.utils.registerActiveMenuItem(); + NexT.utils.registerLangSelect(); + NexT.utils.registerSidebarTOC(); + NexT.utils.wrapTableWithBox(); + NexT.utils.registerVideoIframe(); +}; + +NexT.boot.motion = function() { + // Define Motion Sequence & Bootstrap Motion. + if (CONFIG.motion.enable) { + NexT.motion.integrator + .add(NexT.motion.middleWares.logo) + .add(NexT.motion.middleWares.menu) + .add(NexT.motion.middleWares.postList) + .add(NexT.motion.middleWares.sidebar) + .bootstrap(); + } + NexT.utils.updateSidebarPosition(); +}; + +document.addEventListener('DOMContentLoaded', () => { + NexT.boot.registerEvents(); + NexT.boot.refresh(); + NexT.boot.motion(); +}); diff --git a/js/schemes/muse.js b/js/schemes/muse.js new file mode 100644 index 0000000..f4be56d --- /dev/null +++ b/js/schemes/muse.js @@ -0,0 +1,113 @@ +/* global NexT, CONFIG, Velocity */ + +document.addEventListener('DOMContentLoaded', () => { + + var isRight = CONFIG.sidebar.position === 'right'; + var SIDEBAR_WIDTH = CONFIG.sidebar.width || 320; + var SIDEBAR_DISPLAY_DURATION = 200; + var mousePos = {}; + + var sidebarToggleLines = { + lines: document.querySelector('.sidebar-toggle'), + init : function() { + this.lines.classList.remove('toggle-arrow', 'toggle-close'); + }, + arrow: function() { + this.lines.classList.remove('toggle-close'); + this.lines.classList.add('toggle-arrow'); + }, + close: function() { + this.lines.classList.remove('toggle-arrow'); + this.lines.classList.add('toggle-close'); + } + }; + + var sidebarToggleMotion = { + sidebarEl : document.querySelector('.sidebar'), + isSidebarVisible: false, + init : function() { + sidebarToggleLines.init(); + + window.addEventListener('mousedown', this.mousedownHandler.bind(this)); + window.addEventListener('mouseup', this.mouseupHandler.bind(this)); + document.querySelector('#sidebar-dimmer').addEventListener('click', this.clickHandler.bind(this)); + document.querySelector('.sidebar-toggle').addEventListener('click', this.clickHandler.bind(this)); + document.querySelector('.sidebar-toggle').addEventListener('mouseenter', this.mouseEnterHandler.bind(this)); + document.querySelector('.sidebar-toggle').addEventListener('mouseleave', this.mouseLeaveHandler.bind(this)); + window.addEventListener('sidebar:show', this.showSidebar.bind(this)); + window.addEventListener('sidebar:hide', this.hideSidebar.bind(this)); + }, + mousedownHandler: function(event) { + mousePos.X = event.pageX; + mousePos.Y = event.pageY; + }, + mouseupHandler: function(event) { + var deltaX = event.pageX - mousePos.X; + var deltaY = event.pageY - mousePos.Y; + var clickingBlankPart = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)) < 20 && event.target.matches('.main'); + if (this.isSidebarVisible && (clickingBlankPart || event.target.matches('img.medium-zoom-image, .fancybox img'))) { + this.hideSidebar(); + } + }, + clickHandler: function() { + this.isSidebarVisible ? this.hideSidebar() : this.showSidebar(); + }, + mouseEnterHandler: function() { + if (!this.isSidebarVisible) { + sidebarToggleLines.arrow(); + } + }, + mouseLeaveHandler: function() { + if (!this.isSidebarVisible) { + sidebarToggleLines.init(); + } + }, + showSidebar: function() { + this.isSidebarVisible = true; + this.sidebarEl.classList.add('sidebar-active'); + if (typeof Velocity === 'function') { + Velocity(document.querySelectorAll('.sidebar .motion-element'), isRight ? 'transition.slideRightIn' : 'transition.slideLeftIn', { + stagger: 50, + drag : true + }); + } + + sidebarToggleLines.close(); + NexT.utils.isDesktop() && window.anime(Object.assign({ + targets : document.body, + duration: SIDEBAR_DISPLAY_DURATION, + easing : 'linear' + }, isRight ? { + 'padding-right': SIDEBAR_WIDTH + } : { + 'padding-left': SIDEBAR_WIDTH + })); + }, + hideSidebar: function() { + this.isSidebarVisible = false; + this.sidebarEl.classList.remove('sidebar-active'); + + sidebarToggleLines.init(); + NexT.utils.isDesktop() && window.anime(Object.assign({ + targets : document.body, + duration: SIDEBAR_DISPLAY_DURATION, + easing : 'linear' + }, isRight ? { + 'padding-right': 0 + } : { + 'padding-left': 0 + })); + } + }; + sidebarToggleMotion.init(); + + function updateFooterPosition() { + var footer = document.querySelector('.footer'); + var containerHeight = document.querySelector('.header').offsetHeight + document.querySelector('.main').offsetHeight + footer.offsetHeight; + footer.classList.toggle('footer-fixed', containerHeight <= window.innerHeight); + } + + updateFooterPosition(); + window.addEventListener('resize', updateFooterPosition); + window.addEventListener('scroll', updateFooterPosition); +}); diff --git a/js/schemes/pisces.js b/js/schemes/pisces.js new file mode 100644 index 0000000..41633ea --- /dev/null +++ b/js/schemes/pisces.js @@ -0,0 +1,86 @@ +/* global NexT, CONFIG */ + +var Affix = { + init: function(element, options) { + this.element = element; + this.offset = options || 0; + this.affixed = null; + this.unpin = null; + this.pinnedOffset = null; + this.checkPosition(); + window.addEventListener('scroll', this.checkPosition.bind(this)); + window.addEventListener('click', this.checkPositionWithEventLoop.bind(this)); + window.matchMedia('(min-width: 992px)').addListener(event => { + if (event.matches) { + this.offset = NexT.utils.getAffixParam(); + this.checkPosition(); + } + }); + }, + getState: function(scrollHeight, height, offsetTop, offsetBottom) { + let scrollTop = window.scrollY; + let targetHeight = window.innerHeight; + if (offsetTop != null && this.affixed === 'top') { + if (document.querySelector('.content-wrap').offsetHeight < offsetTop) return 'top'; + return scrollTop < offsetTop ? 'top' : false; + } + if (this.affixed === 'bottom') { + if (offsetTop != null) return this.unpin <= this.element.getBoundingClientRect().top ? false : 'bottom'; + return scrollTop + targetHeight <= scrollHeight - offsetBottom ? false : 'bottom'; + } + let initializing = this.affixed === null; + let colliderTop = initializing ? scrollTop : this.element.getBoundingClientRect().top + scrollTop; + let colliderHeight = initializing ? targetHeight : height; + if (offsetTop != null && scrollTop <= offsetTop) return 'top'; + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'; + return false; + }, + getPinnedOffset: function() { + if (this.pinnedOffset) return this.pinnedOffset; + this.element.classList.remove('affix-top', 'affix-bottom'); + this.element.classList.add('affix'); + return (this.pinnedOffset = this.element.getBoundingClientRect().top); + }, + checkPositionWithEventLoop() { + setTimeout(this.checkPosition.bind(this), 1); + }, + checkPosition: function() { + if (window.getComputedStyle(this.element).display === 'none') return; + let height = this.element.offsetHeight; + let { offset } = this; + let offsetTop = offset.top; + let offsetBottom = offset.bottom; + let { scrollHeight } = document.body; + let affix = this.getState(scrollHeight, height, offsetTop, offsetBottom); + if (this.affixed !== affix) { + if (this.unpin != null) this.element.style.top = ''; + let affixType = 'affix' + (affix ? '-' + affix : ''); + this.affixed = affix; + this.unpin = affix === 'bottom' ? this.getPinnedOffset() : null; + this.element.classList.remove('affix', 'affix-top', 'affix-bottom'); + this.element.classList.add(affixType); + } + if (affix === 'bottom') { + this.element.style.top = scrollHeight - height - offsetBottom + 'px'; + } + } +}; + +NexT.utils.getAffixParam = function() { + const sidebarOffset = CONFIG.sidebar.offset || 12; + + let headerOffset = document.querySelector('.header-inner').offsetHeight; + let footerOffset = document.querySelector('.footer').offsetHeight; + + document.querySelector('.sidebar').style.marginTop = headerOffset + sidebarOffset + 'px'; + + return { + top : headerOffset, + bottom: footerOffset + }; +}; + +document.addEventListener('DOMContentLoaded', () => { + + Affix.init(document.querySelector('.sidebar-inner'), NexT.utils.getAffixParam()); +}); diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 0000000..74a6dfd --- /dev/null +++ b/js/utils.js @@ -0,0 +1,415 @@ +/* global NexT, CONFIG */ + +HTMLElement.prototype.wrap = function(wrapper) { + this.parentNode.insertBefore(wrapper, this); + this.parentNode.removeChild(this); + wrapper.appendChild(this); +}; + +NexT.utils = { + + /** + * Wrap images with fancybox. + */ + wrapImageWithFancyBox: function() { + document.querySelectorAll('.post-body :not(a) > img, .post-body > img').forEach(element => { + var $image = $(element); + var imageLink = $image.attr('data-src') || $image.attr('src'); + var $imageWrapLink = $image.wrap(``).parent('a'); + if ($image.is('.post-gallery img')) { + $imageWrapLink.attr('data-fancybox', 'gallery').attr('rel', 'gallery'); + } else if ($image.is('.group-picture img')) { + $imageWrapLink.attr('data-fancybox', 'group').attr('rel', 'group'); + } else { + $imageWrapLink.attr('data-fancybox', 'default').attr('rel', 'default'); + } + + var imageTitle = $image.attr('title') || $image.attr('alt'); + if (imageTitle) { + $imageWrapLink.append(`

    ${imageTitle}

    `); + // Make sure img title tag will show correctly in fancybox + $imageWrapLink.attr('title', imageTitle).attr('data-caption', imageTitle); + } + }); + + $.fancybox.defaults.hash = false; + $('.fancybox').fancybox({ + loop : true, + helpers: { + overlay: { + locked: false + } + } + }); + }, + + registerExtURL: function() { + document.querySelectorAll('span.exturl').forEach(element => { + let link = document.createElement('a'); + // https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings + link.href = decodeURIComponent(atob(element.dataset.url).split('').map(c => { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); + link.rel = 'noopener external nofollow noreferrer'; + link.target = '_blank'; + link.className = element.className; + link.title = element.title; + link.innerHTML = element.innerHTML; + element.parentNode.replaceChild(link, element); + }); + }, + + /** + * One-click copy code support. + */ + registerCopyCode: function() { + document.querySelectorAll('figure.highlight').forEach(element => { + const box = document.createElement('div'); + element.wrap(box); + box.classList.add('highlight-container'); + box.insertAdjacentHTML('beforeend', '
    '); + var button = element.parentNode.querySelector('.copy-btn'); + button.addEventListener('click', event => { + var target = event.currentTarget; + var code = [...target.parentNode.querySelectorAll('.code .line')].map(line => line.innerText).join('\n'); + var ta = document.createElement('textarea'); + ta.style.top = window.scrollY + 'px'; // Prevent page scrolling + ta.style.position = 'absolute'; + ta.style.opacity = '0'; + ta.readOnly = true; + ta.value = code; + document.body.append(ta); + const selection = document.getSelection(); + const selected = selection.rangeCount > 0 ? selection.getRangeAt(0) : false; + ta.select(); + ta.setSelectionRange(0, code.length); + ta.readOnly = false; + var result = document.execCommand('copy'); + if (CONFIG.copycode.show_result) { + target.querySelector('i').className = result ? 'fa fa-check fa-fw' : 'fa fa-times fa-fw'; + } + ta.blur(); // For iOS + target.blur(); + if (selected) { + selection.removeAllRanges(); + selection.addRange(selected); + } + document.body.removeChild(ta); + }); + button.addEventListener('mouseleave', event => { + setTimeout(() => { + event.target.querySelector('i').className = 'fa fa-clipboard fa-fw'; + }, 300); + }); + }); + }, + + wrapTableWithBox: function() { + document.querySelectorAll('table').forEach(element => { + const box = document.createElement('div'); + box.className = 'table-container'; + element.wrap(box); + }); + }, + + registerVideoIframe: function() { + document.querySelectorAll('iframe').forEach(element => { + const supported = [ + 'www.youtube.com', + 'player.vimeo.com', + 'player.youku.com', + 'player.bilibili.com', + 'www.tudou.com' + ].some(host => element.src.includes(host)); + if (supported && !element.parentNode.matches('.video-container')) { + const box = document.createElement('div'); + box.className = 'video-container'; + element.wrap(box); + let width = Number(element.width); + let height = Number(element.height); + if (width && height) { + element.parentNode.style.paddingTop = (height / width * 100) + '%'; + } + } + }); + }, + + registerScrollPercent: function() { + var THRESHOLD = 50; + var backToTop = document.querySelector('.back-to-top'); + var readingProgressBar = document.querySelector('.reading-progress-bar'); + // For init back to top in sidebar if page was scrolled after page refresh. + window.addEventListener('scroll', () => { + if (backToTop || readingProgressBar) { + var docHeight = document.querySelector('.container').offsetHeight; + var winHeight = window.innerHeight; + var contentVisibilityHeight = docHeight > winHeight ? docHeight - winHeight : document.body.scrollHeight - winHeight; + var scrollPercent = Math.min(100 * window.scrollY / contentVisibilityHeight, 100); + if (backToTop) { + backToTop.classList.toggle('back-to-top-on', window.scrollY > THRESHOLD); + backToTop.querySelector('span').innerText = Math.round(scrollPercent) + '%'; + } + if (readingProgressBar) { + readingProgressBar.style.width = scrollPercent.toFixed(2) + '%'; + } + } + }); + + backToTop && backToTop.addEventListener('click', () => { + window.anime({ + targets : document.scrollingElement, + duration : 500, + easing : 'linear', + scrollTop: 0 + }); + }); + }, + + /** + * Tabs tag listener (without twitter bootstrap). + */ + registerTabsTag: function() { + // Binding `nav-tabs` & `tab-content` by real time permalink changing. + document.querySelectorAll('.tabs ul.nav-tabs .tab').forEach(element => { + element.addEventListener('click', event => { + event.preventDefault(); + var target = event.currentTarget; + // Prevent selected tab to select again. + if (!target.classList.contains('active')) { + // Add & Remove active class on `nav-tabs` & `tab-content`. + [...target.parentNode.children].forEach(element => { + element.classList.remove('active'); + }); + target.classList.add('active'); + var tActive = document.getElementById(target.querySelector('a').getAttribute('href').replace('#', '')); + [...tActive.parentNode.children].forEach(element => { + element.classList.remove('active'); + }); + tActive.classList.add('active'); + // Trigger event + tActive.dispatchEvent(new Event('tabs:click', { + bubbles: true + })); + } + }); + }); + + window.dispatchEvent(new Event('tabs:register')); + }, + + registerCanIUseTag: function() { + // Get responsive height passed from iframe. + window.addEventListener('message', ({ data }) => { + if ((typeof data === 'string') && data.includes('ciu_embed')) { + var featureID = data.split(':')[1]; + var height = data.split(':')[2]; + document.querySelector(`iframe[data-feature=${featureID}]`).style.height = parseInt(height, 10) + 5 + 'px'; + } + }, false); + }, + + registerActiveMenuItem: function() { + document.querySelectorAll('.menu-item').forEach(element => { + var target = element.querySelector('a[href]'); + if (!target) return; + var isSamePath = target.pathname === location.pathname || target.pathname === location.pathname.replace('index.html', ''); + var isSubPath = !CONFIG.root.startsWith(target.pathname) && location.pathname.startsWith(target.pathname); + element.classList.toggle('menu-item-active', target.hostname === location.hostname && (isSamePath || isSubPath)); + }); + }, + + registerLangSelect: function() { + let selects = document.querySelectorAll('.lang-select'); + selects.forEach(sel => { + sel.value = CONFIG.page.lang; + sel.addEventListener('change', () => { + let target = sel.options[sel.selectedIndex]; + document.querySelectorAll('.lang-select-label span').forEach(span => span.innerText = target.text); + let url = target.dataset.href; + window.pjax ? window.pjax.loadUrl(url) : window.location.href = url; + }); + }); + }, + + registerSidebarTOC: function() { + const navItems = document.querySelectorAll('.post-toc li'); + const sections = [...navItems].map(element => { + var link = element.querySelector('a.nav-link'); + var target = document.getElementById(decodeURI(link.getAttribute('href')).replace('#', '')); + // TOC item animation navigate. + link.addEventListener('click', event => { + event.preventDefault(); + var offset = target.getBoundingClientRect().top + window.scrollY; + window.anime({ + targets : document.scrollingElement, + duration : 500, + easing : 'linear', + scrollTop: offset + 10 + }); + }); + return target; + }); + + var tocElement = document.querySelector('.post-toc-wrap'); + function activateNavByIndex(target) { + if (target.classList.contains('active-current')) return; + + document.querySelectorAll('.post-toc .active').forEach(element => { + element.classList.remove('active', 'active-current'); + }); + target.classList.add('active', 'active-current'); + var parent = target.parentNode; + while (!parent.matches('.post-toc')) { + if (parent.matches('li')) parent.classList.add('active'); + parent = parent.parentNode; + } + // Scrolling to center active TOC element if TOC content is taller then viewport. + window.anime({ + targets : tocElement, + duration : 200, + easing : 'linear', + scrollTop: tocElement.scrollTop - (tocElement.offsetHeight / 2) + target.getBoundingClientRect().top - tocElement.getBoundingClientRect().top + }); + } + + function findIndex(entries) { + let index = 0; + let entry = entries[index]; + if (entry.boundingClientRect.top > 0) { + index = sections.indexOf(entry.target); + return index === 0 ? 0 : index - 1; + } + for (; index < entries.length; index++) { + if (entries[index].boundingClientRect.top <= 0) { + entry = entries[index]; + } else { + return sections.indexOf(entry.target); + } + } + return sections.indexOf(entry.target); + } + + function createIntersectionObserver(marginTop) { + marginTop = Math.floor(marginTop + 10000); + let intersectionObserver = new IntersectionObserver((entries, observe) => { + let scrollHeight = document.documentElement.scrollHeight + 100; + if (scrollHeight > marginTop) { + observe.disconnect(); + createIntersectionObserver(scrollHeight); + return; + } + let index = findIndex(entries); + activateNavByIndex(navItems[index]); + }, { + rootMargin: marginTop + 'px 0px -100% 0px', + threshold : 0 + }); + sections.forEach(element => { + element && intersectionObserver.observe(element); + }); + } + createIntersectionObserver(document.documentElement.scrollHeight); + }, + + hasMobileUA: function() { + let ua = navigator.userAgent; + let pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g; + return pa.test(ua); + }, + + isTablet: function() { + return window.screen.width < 992 && window.screen.width > 767 && this.hasMobileUA(); + }, + + isMobile: function() { + return window.screen.width < 767 && this.hasMobileUA(); + }, + + isDesktop: function() { + return !this.isTablet() && !this.isMobile(); + }, + + supportsPDFs: function() { + let ua = navigator.userAgent; + let isFirefoxWithPDFJS = ua.includes('irefox') && parseInt(ua.split('rv:')[1].split('.')[0], 10) > 18; + let supportsPdfMimeType = typeof navigator.mimeTypes['application/pdf'] !== 'undefined'; + let isIOS = /iphone|ipad|ipod/i.test(ua.toLowerCase()); + return isFirefoxWithPDFJS || (supportsPdfMimeType && !isIOS); + }, + + /** + * Init Sidebar & TOC inner dimensions on all pages and for all schemes. + * Need for Sidebar/TOC inner scrolling if content taller then viewport. + */ + initSidebarDimension: function() { + var sidebarNav = document.querySelector('.sidebar-nav'); + var sidebarNavHeight = sidebarNav.style.display !== 'none' ? sidebarNav.offsetHeight : 0; + var sidebarOffset = CONFIG.sidebar.offset || 12; + var sidebarb2tHeight = CONFIG.back2top.enable && CONFIG.back2top.sidebar ? document.querySelector('.back-to-top').offsetHeight : 0; + var sidebarSchemePadding = (CONFIG.sidebar.padding * 2) + sidebarNavHeight + sidebarb2tHeight; + // Margin of sidebar b2t: -4px -10px -18px, brings a different of 22px. + if (CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini') sidebarSchemePadding += (sidebarOffset * 2) - 22; + // Initialize Sidebar & TOC Height. + var sidebarWrapperHeight = document.body.offsetHeight - sidebarSchemePadding + 'px'; + document.querySelector('.site-overview-wrap').style.maxHeight = sidebarWrapperHeight; + document.querySelector('.post-toc-wrap').style.maxHeight = sidebarWrapperHeight; + }, + + updateSidebarPosition: function() { + var sidebarNav = document.querySelector('.sidebar-nav'); + var hasTOC = document.querySelector('.post-toc'); + if (hasTOC) { + sidebarNav.style.display = ''; + sidebarNav.classList.add('motion-element'); + document.querySelector('.sidebar-nav-toc').click(); + } else { + sidebarNav.style.display = 'none'; + sidebarNav.classList.remove('motion-element'); + document.querySelector('.sidebar-nav-overview').click(); + } + NexT.utils.initSidebarDimension(); + if (!this.isDesktop() || CONFIG.scheme === 'Pisces' || CONFIG.scheme === 'Gemini') return; + // Expand sidebar on post detail page by default, when post has a toc. + var display = CONFIG.page.sidebar; + if (typeof display !== 'boolean') { + // There's no definition sidebar in the page front-matter. + display = CONFIG.sidebar.display === 'always' || (CONFIG.sidebar.display === 'post' && hasTOC); + } + if (display) { + window.dispatchEvent(new Event('sidebar:show')); + } + }, + + getScript: function(url, callback, condition) { + if (condition) { + callback(); + } else { + var script = document.createElement('script'); + script.onload = script.onreadystatechange = function(_, isAbort) { + if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) { + script.onload = script.onreadystatechange = null; + script = undefined; + if (!isAbort && callback) setTimeout(callback, 0); + } + }; + script.src = url; + document.head.appendChild(script); + } + }, + + loadComments: function(element, callback) { + if (!CONFIG.comments.lazyload || !element) { + callback(); + return; + } + let intersectionObserver = new IntersectionObserver((entries, observer) => { + let entry = entries[0]; + if (entry.isIntersecting) { + callback(); + observer.disconnect(); + } + }); + intersectionObserver.observe(element); + return intersectionObserver; + } +}; diff --git a/lib/anime.min.js b/lib/anime.min.js new file mode 100644 index 0000000..99b263a --- /dev/null +++ b/lib/anime.min.js @@ -0,0 +1,8 @@ +/* + * anime.js v3.1.0 + * (c) 2019 Julian Garnier + * Released under the MIT license + * animejs.com + */ + +!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):n.anime=e()}(this,function(){"use strict";var n={update:null,begin:null,loopBegin:null,changeBegin:null,change:null,changeComplete:null,loopComplete:null,complete:null,loop:1,direction:"normal",autoplay:!0,timelineOffset:0},e={duration:1e3,delay:0,endDelay:0,easing:"easeOutElastic(1, .5)",round:0},r=["translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","scale","scaleX","scaleY","scaleZ","skew","skewX","skewY","perspective"],t={CSS:{},springs:{}};function a(n,e,r){return Math.min(Math.max(n,e),r)}function o(n,e){return n.indexOf(e)>-1}function u(n,e){return n.apply(null,e)}var i={arr:function(n){return Array.isArray(n)},obj:function(n){return o(Object.prototype.toString.call(n),"Object")},pth:function(n){return i.obj(n)&&n.hasOwnProperty("totalLength")},svg:function(n){return n instanceof SVGElement},inp:function(n){return n instanceof HTMLInputElement},dom:function(n){return n.nodeType||i.svg(n)},str:function(n){return"string"==typeof n},fnc:function(n){return"function"==typeof n},und:function(n){return void 0===n},hex:function(n){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(n)},rgb:function(n){return/^rgb/.test(n)},hsl:function(n){return/^hsl/.test(n)},col:function(n){return i.hex(n)||i.rgb(n)||i.hsl(n)},key:function(r){return!n.hasOwnProperty(r)&&!e.hasOwnProperty(r)&&"targets"!==r&&"keyframes"!==r}};function c(n){var e=/\(([^)]+)\)/.exec(n);return e?e[1].split(",").map(function(n){return parseFloat(n)}):[]}function s(n,e){var r=c(n),o=a(i.und(r[0])?1:r[0],.1,100),u=a(i.und(r[1])?100:r[1],.1,100),s=a(i.und(r[2])?10:r[2],.1,100),f=a(i.und(r[3])?0:r[3],.1,100),l=Math.sqrt(u/o),d=s/(2*Math.sqrt(u*o)),p=d<1?l*Math.sqrt(1-d*d):0,h=1,v=d<1?(d*l-f)/p:-f+l;function g(n){var r=e?e*n/1e3:n;return r=d<1?Math.exp(-r*d*l)*(h*Math.cos(p*r)+v*Math.sin(p*r)):(h+v*r)*Math.exp(-r*l),0===n||1===n?n:1-r}return e?g:function(){var e=t.springs[n];if(e)return e;for(var r=0,a=0;;)if(1===g(r+=1/6)){if(++a>=16)break}else a=0;var o=r*(1/6)*1e3;return t.springs[n]=o,o}}function f(n){return void 0===n&&(n=10),function(e){return Math.round(e*n)*(1/n)}}var l,d,p=function(){var n=11,e=1/(n-1);function r(n,e){return 1-3*e+3*n}function t(n,e){return 3*e-6*n}function a(n){return 3*n}function o(n,e,o){return((r(e,o)*n+t(e,o))*n+a(e))*n}function u(n,e,o){return 3*r(e,o)*n*n+2*t(e,o)*n+a(e)}return function(r,t,a,i){if(0<=r&&r<=1&&0<=a&&a<=1){var c=new Float32Array(n);if(r!==t||a!==i)for(var s=0;s=.001?function(n,e,r,t){for(var a=0;a<4;++a){var i=u(e,r,t);if(0===i)return e;e-=(o(e,r,t)-n)/i}return e}(t,l,r,a):0===d?l:function(n,e,r,t,a){for(var u,i,c=0;(u=o(i=e+(r-e)/2,t,a)-n)>0?r=i:e=i,Math.abs(u)>1e-7&&++c<10;);return i}(t,i,i+e,r,a)}}}(),h=(l={linear:function(){return function(n){return n}}},d={Sine:function(){return function(n){return 1-Math.cos(n*Math.PI/2)}},Circ:function(){return function(n){return 1-Math.sqrt(1-n*n)}},Back:function(){return function(n){return n*n*(3*n-2)}},Bounce:function(){return function(n){for(var e,r=4;n<((e=Math.pow(2,--r))-1)/11;);return 1/Math.pow(4,3-r)-7.5625*Math.pow((3*e-2)/22-n,2)}},Elastic:function(n,e){void 0===n&&(n=1),void 0===e&&(e=.5);var r=a(n,1,10),t=a(e,.1,2);return function(n){return 0===n||1===n?n:-r*Math.pow(2,10*(n-1))*Math.sin((n-1-t/(2*Math.PI)*Math.asin(1/r))*(2*Math.PI)/t)}}},["Quad","Cubic","Quart","Quint","Expo"].forEach(function(n,e){d[n]=function(){return function(n){return Math.pow(n,e+2)}}}),Object.keys(d).forEach(function(n){var e=d[n];l["easeIn"+n]=e,l["easeOut"+n]=function(n,r){return function(t){return 1-e(n,r)(1-t)}},l["easeInOut"+n]=function(n,r){return function(t){return t<.5?e(n,r)(2*t)/2:1-e(n,r)(-2*t+2)/2}}}),l);function v(n,e){if(i.fnc(n))return n;var r=n.split("(")[0],t=h[r],a=c(n);switch(r){case"spring":return s(n,e);case"cubicBezier":return u(p,a);case"steps":return u(f,a);default:return u(t,a)}}function g(n){try{return document.querySelectorAll(n)}catch(n){return}}function m(n,e){for(var r=n.length,t=arguments.length>=2?arguments[1]:void 0,a=[],o=0;o1&&(r-=1),r<1/6?n+6*(e-n)*r:r<.5?e:r<2/3?n+(e-n)*(2/3-r)*6:n}if(0==u)e=r=t=i;else{var f=i<.5?i*(1+u):i+u-i*u,l=2*i-f;e=s(l,f,o+1/3),r=s(l,f,o),t=s(l,f,o-1/3)}return"rgba("+255*e+","+255*r+","+255*t+","+c+")"}(n):void 0;var e,r,t,a}function C(n){var e=/[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(n);if(e)return e[1]}function B(n,e){return i.fnc(n)?n(e.target,e.id,e.total):n}function P(n,e){return n.getAttribute(e)}function I(n,e,r){if(M([r,"deg","rad","turn"],C(e)))return e;var a=t.CSS[e+r];if(!i.und(a))return a;var o=document.createElement(n.tagName),u=n.parentNode&&n.parentNode!==document?n.parentNode:document.body;u.appendChild(o),o.style.position="absolute",o.style.width=100+r;var c=100/o.offsetWidth;u.removeChild(o);var s=c*parseFloat(e);return t.CSS[e+r]=s,s}function T(n,e,r){if(e in n.style){var t=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),a=n.style[e]||getComputedStyle(n).getPropertyValue(t)||"0";return r?I(n,a,r):a}}function D(n,e){return i.dom(n)&&!i.inp(n)&&(P(n,e)||i.svg(n)&&n[e])?"attribute":i.dom(n)&&M(r,e)?"transform":i.dom(n)&&"transform"!==e&&T(n,e)?"css":null!=n[e]?"object":void 0}function E(n){if(i.dom(n)){for(var e,r=n.style.transform||"",t=/(\w+)\(([^)]*)\)/g,a=new Map;e=t.exec(r);)a.set(e[1],e[2]);return a}}function F(n,e,r,t){var a,u=o(e,"scale")?1:0+(o(a=e,"translate")||"perspective"===a?"px":o(a,"rotate")||o(a,"skew")?"deg":void 0),i=E(n).get(e)||u;return r&&(r.transforms.list.set(e,i),r.transforms.last=e),t?I(n,i,t):i}function N(n,e,r,t){switch(D(n,e)){case"transform":return F(n,e,t,r);case"css":return T(n,e,r);case"attribute":return P(n,e);default:return n[e]||0}}function A(n,e){var r=/^(\*=|\+=|-=)/.exec(n);if(!r)return n;var t=C(n)||0,a=parseFloat(e),o=parseFloat(n.replace(r[0],""));switch(r[0][0]){case"+":return a+o+t;case"-":return a-o+t;case"*":return a*o+t}}function L(n,e){if(i.col(n))return O(n);if(/\s/g.test(n))return n;var r=C(n),t=r?n.substr(0,n.length-r.length):n;return e?t+e:t}function j(n,e){return Math.sqrt(Math.pow(e.x-n.x,2)+Math.pow(e.y-n.y,2))}function S(n){for(var e,r=n.points,t=0,a=0;a0&&(t+=j(e,o)),e=o}return t}function q(n){if(n.getTotalLength)return n.getTotalLength();switch(n.tagName.toLowerCase()){case"circle":return o=n,2*Math.PI*P(o,"r");case"rect":return 2*P(a=n,"width")+2*P(a,"height");case"line":return j({x:P(t=n,"x1"),y:P(t,"y1")},{x:P(t,"x2"),y:P(t,"y2")});case"polyline":return S(n);case"polygon":return r=(e=n).points,S(e)+j(r.getItem(r.numberOfItems-1),r.getItem(0))}var e,r,t,a,o}function $(n,e){var r=e||{},t=r.el||function(n){for(var e=n.parentNode;i.svg(e)&&i.svg(e.parentNode);)e=e.parentNode;return e}(n),a=t.getBoundingClientRect(),o=P(t,"viewBox"),u=a.width,c=a.height,s=r.viewBox||(o?o.split(" "):[0,0,u,c]);return{el:t,viewBox:s,x:s[0]/1,y:s[1]/1,w:u/s[2],h:c/s[3]}}function X(n,e){function r(r){void 0===r&&(r=0);var t=e+r>=1?e+r:0;return n.el.getPointAtLength(t)}var t=$(n.el,n.svg),a=r(),o=r(-1),u=r(1);switch(n.property){case"x":return(a.x-t.x)*t.w;case"y":return(a.y-t.y)*t.h;case"angle":return 180*Math.atan2(u.y-o.y,u.x-o.x)/Math.PI}}function Y(n,e){var r=/[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g,t=L(i.pth(n)?n.totalLength:n,e)+"";return{original:t,numbers:t.match(r)?t.match(r).map(Number):[0],strings:i.str(n)||e?t.split(r):[]}}function Z(n){return m(n?y(i.arr(n)?n.map(b):b(n)):[],function(n,e,r){return r.indexOf(n)===e})}function Q(n){var e=Z(n);return e.map(function(n,r){return{target:n,id:r,total:e.length,transforms:{list:E(n)}}})}function V(n,e){var r=x(e);if(/^spring/.test(r.easing)&&(r.duration=s(r.easing)),i.arr(n)){var t=n.length;2===t&&!i.obj(n[0])?n={value:n}:i.fnc(e.duration)||(r.duration=e.duration/t)}var a=i.arr(n)?n:[n];return a.map(function(n,r){var t=i.obj(n)&&!i.pth(n)?n:{value:n};return i.und(t.delay)&&(t.delay=r?0:e.delay),i.und(t.endDelay)&&(t.endDelay=r===a.length-1?e.endDelay:0),t}).map(function(n){return k(n,r)})}function z(n,e){var r=[],t=e.keyframes;for(var a in t&&(e=k(function(n){for(var e=m(y(n.map(function(n){return Object.keys(n)})),function(n){return i.key(n)}).reduce(function(n,e){return n.indexOf(e)<0&&n.push(e),n},[]),r={},t=function(t){var a=e[t];r[a]=n.map(function(n){var e={};for(var r in n)i.key(r)?r==a&&(e.value=n[r]):e[r]=n[r];return e})},a=0;a-1&&(_.splice(o,1),r=_.length)}else a.tick(e);t++}n()}else U=cancelAnimationFrame(U)}return n}();function rn(r){void 0===r&&(r={});var t,o=0,u=0,i=0,c=0,s=null;function f(n){var e=window.Promise&&new Promise(function(n){return s=n});return n.finished=e,e}var l,d,p,h,v,g,y,b,M=(d=w(n,l=r),p=w(e,l),h=z(p,l),v=Q(l.targets),g=W(v,h),y=J(g,p),b=K,K++,k(d,{id:b,children:[],animatables:v,animations:g,duration:y.duration,delay:y.delay,endDelay:y.endDelay}));f(M);function x(){var n=M.direction;"alternate"!==n&&(M.direction="normal"!==n?"normal":"reverse"),M.reversed=!M.reversed,t.forEach(function(n){return n.reversed=M.reversed})}function O(n){return M.reversed?M.duration-n:n}function C(){o=0,u=O(M.currentTime)*(1/rn.speed)}function B(n,e){e&&e.seek(n-e.timelineOffset)}function P(n){for(var e=0,r=M.animations,t=r.length;e2||(b=Math.round(b*p)/p)),h.push(b)}var k=d.length;if(k){g=d[0];for(var O=0;O0&&(M.began=!0,I("begin")),!M.loopBegan&&M.currentTime>0&&(M.loopBegan=!0,I("loopBegin")),d<=r&&0!==M.currentTime&&P(0),(d>=l&&M.currentTime!==e||!e)&&P(e),d>r&&d=e&&(u=0,M.remaining&&!0!==M.remaining&&M.remaining--,M.remaining?(o=i,I("loopComplete"),M.loopBegan=!1,"alternate"===M.direction&&x()):(M.paused=!0,M.completed||(M.completed=!0,I("loopComplete"),I("complete"),!M.passThrough&&"Promise"in window&&(s(),f(M)))))}return M.reset=function(){var n=M.direction;M.passThrough=!1,M.currentTime=0,M.progress=0,M.paused=!0,M.began=!1,M.loopBegan=!1,M.changeBegan=!1,M.completed=!1,M.changeCompleted=!1,M.reversePlayback=!1,M.reversed="reverse"===n,M.remaining=M.loop,t=M.children;for(var e=c=t.length;e--;)M.children[e].reset();(M.reversed&&!0!==M.loop||"alternate"===n&&1===M.loop)&&M.remaining++,P(M.reversed?M.duration:0)},M.set=function(n,e){return R(n,e),M},M.tick=function(n){i=n,o||(o=i),T((i+(u-o))*rn.speed)},M.seek=function(n){T(O(n))},M.pause=function(){M.paused=!0,C()},M.play=function(){M.paused&&(M.completed&&M.reset(),M.paused=!1,_.push(M),C(),U||en())},M.reverse=function(){x(),C()},M.restart=function(){M.reset(),M.play()},M.reset(),M.autoplay&&M.play(),M}function tn(n,e){for(var r=e.length;r--;)M(n,e[r].animatable.target)&&e.splice(r,1)}return"undefined"!=typeof document&&document.addEventListener("visibilitychange",function(){document.hidden?(_.forEach(function(n){return n.pause()}),nn=_.slice(0),rn.running=_=[]):nn.forEach(function(n){return n.play()})}),rn.version="3.1.0",rn.speed=1,rn.running=_,rn.remove=function(n){for(var e=Z(n),r=_.length;r--;){var t=_[r],a=t.animations,o=t.children;tn(e,a);for(var u=o.length;u--;){var i=o[u],c=i.animations;tn(e,c),c.length||i.children.length||o.splice(u,1)}a.length||o.length||t.pause()}},rn.get=N,rn.set=R,rn.convertPx=I,rn.path=function(n,e){var r=i.str(n)?g(n)[0]:n,t=e||100;return function(n){return{property:n,el:r,svg:$(r),totalLength:q(r)*(t/100)}}},rn.setDashoffset=function(n){var e=q(n);return n.setAttribute("stroke-dasharray",e),e},rn.stagger=function(n,e){void 0===e&&(e={});var r=e.direction||"normal",t=e.easing?v(e.easing):null,a=e.grid,o=e.axis,u=e.from||0,c="first"===u,s="center"===u,f="last"===u,l=i.arr(n),d=l?parseFloat(n[0]):parseFloat(n),p=l?parseFloat(n[1]):0,h=C(l?n[1]:n)||0,g=e.start||0+(l?d:0),m=[],y=0;return function(n,e,i){if(c&&(u=0),s&&(u=(i-1)/2),f&&(u=i-1),!m.length){for(var v=0;v-1&&_.splice(o,1);for(var s=0;sli{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\f95b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\f952"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\f905"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\f907"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\f95c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\f95d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\f95e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\f95f"}.fa-handshake-slash:before{content:"\f960"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\f961"}.fa-head-side-cough-slash:before{content:"\f962"}.fa-head-side-mask:before{content:"\f963"}.fa-head-side-virus:before{content:"\f964"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\f965"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\f913"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\f955"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\f966"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\f967"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\f91a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\f956"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\f968"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\f91e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\f969"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\f96a"}.fa-pump-soap:before{content:"\f96b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\f96c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\f957"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\f96e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\f96f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\f970"}.fa-store-slash:before{content:"\f971"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\f972"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\f941"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\f949"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\f974"}.fa-virus-slash:before{content:"\f975"}.fa-viruses:before{content:"\f976"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.fab,.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} \ No newline at end of file diff --git a/lib/font-awesome/webfonts/fa-brands-400.woff2 b/lib/font-awesome/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..141a90a Binary files /dev/null and b/lib/font-awesome/webfonts/fa-brands-400.woff2 differ diff --git a/lib/font-awesome/webfonts/fa-regular-400.woff2 b/lib/font-awesome/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..7e0118e Binary files /dev/null and b/lib/font-awesome/webfonts/fa-regular-400.woff2 differ diff --git a/lib/font-awesome/webfonts/fa-solid-900.woff2 b/lib/font-awesome/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..978a681 Binary files /dev/null and b/lib/font-awesome/webfonts/fa-solid-900.woff2 differ diff --git a/lib/velocity/velocity.min.js b/lib/velocity/velocity.min.js new file mode 100644 index 0000000..58244c8 --- /dev/null +++ b/lib/velocity/velocity.min.js @@ -0,0 +1,4 @@ +/*! VelocityJS.org (1.2.2). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */ +/*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */ +!function(e){function t(e){var t=e.length,r=$.type(e);return"function"===r||$.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===r||0===t||"number"==typeof t&&t>0&&t-1 in e}if(!e.jQuery){var $=function(e,t){return new $.fn.init(e,t)};$.isWindow=function(e){return null!=e&&e==e.window},$.type=function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?a[o.call(e)]||"object":typeof e},$.isArray=Array.isArray||function(e){return"array"===$.type(e)},$.isPlainObject=function(e){var t;if(!e||"object"!==$.type(e)||e.nodeType||$.isWindow(e))return!1;try{if(e.constructor&&!n.call(e,"constructor")&&!n.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}for(t in e);return void 0===t||n.call(e,t)},$.each=function(e,r,a){var n,o=0,i=e.length,s=t(e);if(a){if(s)for(;i>o&&(n=r.apply(e[o],a),n!==!1);o++);else for(o in e)if(n=r.apply(e[o],a),n===!1)break}else if(s)for(;i>o&&(n=r.call(e[o],o,e[o]),n!==!1);o++);else for(o in e)if(n=r.call(e[o],o,e[o]),n===!1)break;return e},$.data=function(e,t,a){if(void 0===a){var n=e[$.expando],o=n&&r[n];if(void 0===t)return o;if(o&&t in o)return o[t]}else if(void 0!==t){var n=e[$.expando]||(e[$.expando]=++$.uuid);return r[n]=r[n]||{},r[n][t]=a,a}},$.removeData=function(e,t){var a=e[$.expando],n=a&&r[a];n&&$.each(t,function(e,t){delete n[t]})},$.extend=function(){var e,t,r,a,n,o,i=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof i&&(u=i,i=arguments[s]||{},s++),"object"!=typeof i&&"function"!==$.type(i)&&(i={}),s===l&&(i=this,s--);l>s;s++)if(null!=(n=arguments[s]))for(a in n)e=i[a],r=n[a],i!==r&&(u&&r&&($.isPlainObject(r)||(t=$.isArray(r)))?(t?(t=!1,o=e&&$.isArray(e)?e:[]):o=e&&$.isPlainObject(e)?e:{},i[a]=$.extend(u,o,r)):void 0!==r&&(i[a]=r));return i},$.queue=function(e,r,a){function n(e,r){var a=r||[];return null!=e&&(t(Object(e))?!function(e,t){for(var r=+t.length,a=0,n=e.length;r>a;)e[n++]=t[a++];if(r!==r)for(;void 0!==t[a];)e[n++]=t[a++];return e.length=n,e}(a,"string"==typeof e?[e]:e):[].push.call(a,e)),a}if(e){r=(r||"fx")+"queue";var o=$.data(e,r);return a?(!o||$.isArray(a)?o=$.data(e,r,n(a)):o.push(a),o):o||[]}},$.dequeue=function(e,t){$.each(e.nodeType?[e]:e,function(e,r){t=t||"fx";var a=$.queue(r,t),n=a.shift();"inprogress"===n&&(n=a.shift()),n&&("fx"===t&&a.unshift("inprogress"),n.call(r,function(){$.dequeue(r,t)}))})},$.fn=$.prototype={init:function(e){if(e.nodeType)return this[0]=e,this;throw new Error("Not a DOM node.")},offset:function(){var t=this[0].getBoundingClientRect?this[0].getBoundingClientRect():{top:0,left:0};return{top:t.top+(e.pageYOffset||document.scrollTop||0)-(document.clientTop||0),left:t.left+(e.pageXOffset||document.scrollLeft||0)-(document.clientLeft||0)}},position:function(){function e(){for(var e=this.offsetParent||document;e&&"html"===!e.nodeType.toLowerCase&&"static"===e.style.position;)e=e.offsetParent;return e||document}var t=this[0],e=e.apply(t),r=this.offset(),a=/^(?:body|html)$/i.test(e.nodeName)?{top:0,left:0}:$(e).offset();return r.top-=parseFloat(t.style.marginTop)||0,r.left-=parseFloat(t.style.marginLeft)||0,e.style&&(a.top+=parseFloat(e.style.borderTopWidth)||0,a.left+=parseFloat(e.style.borderLeftWidth)||0),{top:r.top-a.top,left:r.left-a.left}}};var r={};$.expando="velocity"+(new Date).getTime(),$.uuid=0;for(var a={},n=a.hasOwnProperty,o=a.toString,i="Boolean Number String Function Array Date RegExp Object Error".split(" "),s=0;sn;++n){var o=u(r,e,a);if(0===o)return r;var i=l(r,e,a)-t;r-=i/o}return r}function p(){for(var t=0;b>t;++t)w[t]=l(t*x,e,a)}function f(t,r,n){var o,i,s=0;do i=r+(n-r)/2,o=l(i,e,a)-t,o>0?n=i:r=i;while(Math.abs(o)>h&&++s=y?c(t,s):0==l?s:f(t,r,r+x)}function g(){V=!0,(e!=r||a!=n)&&p()}var m=4,y=.001,h=1e-7,v=10,b=11,x=1/(b-1),S="Float32Array"in t;if(4!==arguments.length)return!1;for(var P=0;4>P;++P)if("number"!=typeof arguments[P]||isNaN(arguments[P])||!isFinite(arguments[P]))return!1;e=Math.min(e,1),a=Math.min(a,1),e=Math.max(e,0),a=Math.max(a,0);var w=S?new Float32Array(b):new Array(b),V=!1,C=function(t){return V||g(),e===r&&a===n?t:0===t?0:1===t?1:l(d(t),r,n)};C.getControlPoints=function(){return[{x:e,y:r},{x:a,y:n}]};var T="generateBezier("+[e,r,a,n]+")";return C.toString=function(){return T},C}function u(e,t){var r=e;return g.isString(e)?v.Easings[e]||(r=!1):r=g.isArray(e)&&1===e.length?s.apply(null,e):g.isArray(e)&&2===e.length?b.apply(null,e.concat([t])):g.isArray(e)&&4===e.length?l.apply(null,e):!1,r===!1&&(r=v.Easings[v.defaults.easing]?v.defaults.easing:h),r}function c(e){if(e){var t=(new Date).getTime(),r=v.State.calls.length;r>1e4&&(v.State.calls=n(v.State.calls));for(var o=0;r>o;o++)if(v.State.calls[o]){var s=v.State.calls[o],l=s[0],u=s[2],f=s[3],d=!!f,m=null;f||(f=v.State.calls[o][3]=t-16);for(var y=Math.min((t-f)/u.duration,1),h=0,b=l.length;b>h;h++){var S=l[h],w=S.element;if(i(w)){var V=!1;if(u.display!==a&&null!==u.display&&"none"!==u.display){if("flex"===u.display){var C=["-webkit-box","-moz-box","-ms-flexbox","-webkit-flex"];$.each(C,function(e,t){x.setPropertyValue(w,"display",t)})}x.setPropertyValue(w,"display",u.display)}u.visibility!==a&&"hidden"!==u.visibility&&x.setPropertyValue(w,"visibility",u.visibility);for(var T in S)if("element"!==T){var k=S[T],A,F=g.isString(k.easing)?v.Easings[k.easing]:k.easing;if(1===y)A=k.endValue;else{var E=k.endValue-k.startValue;if(A=k.startValue+E*F(y,u,E),!d&&A===k.currentValue)continue}if(k.currentValue=A,"tween"===T)m=A;else{if(x.Hooks.registered[T]){var j=x.Hooks.getRoot(T),H=i(w).rootPropertyValueCache[j];H&&(k.rootPropertyValue=H)}var N=x.setPropertyValue(w,T,k.currentValue+(0===parseFloat(A)?"":k.unitType),k.rootPropertyValue,k.scrollData);x.Hooks.registered[T]&&(i(w).rootPropertyValueCache[j]=x.Normalizations.registered[j]?x.Normalizations.registered[j]("extract",null,N[1]):N[1]),"transform"===N[0]&&(V=!0)}}u.mobileHA&&i(w).transformCache.translate3d===a&&(i(w).transformCache.translate3d="(0px, 0px, 0px)",V=!0),V&&x.flushTransformCache(w)}}u.display!==a&&"none"!==u.display&&(v.State.calls[o][2].display=!1),u.visibility!==a&&"hidden"!==u.visibility&&(v.State.calls[o][2].visibility=!1),u.progress&&u.progress.call(s[1],s[1],y,Math.max(0,f+u.duration-t),f,m),1===y&&p(o)}}v.State.isTicking&&P(c)}function p(e,t){if(!v.State.calls[e])return!1;for(var r=v.State.calls[e][0],n=v.State.calls[e][1],o=v.State.calls[e][2],s=v.State.calls[e][4],l=!1,u=0,c=r.length;c>u;u++){var p=r[u].element;if(t||o.loop||("none"===o.display&&x.setPropertyValue(p,"display",o.display),"hidden"===o.visibility&&x.setPropertyValue(p,"visibility",o.visibility)),o.loop!==!0&&($.queue(p)[1]===a||!/\.velocityQueueEntryFlag/i.test($.queue(p)[1]))&&i(p)){i(p).isAnimating=!1,i(p).rootPropertyValueCache={};var f=!1;$.each(x.Lists.transforms3D,function(e,t){var r=/^scale/.test(t)?1:0,n=i(p).transformCache[t];i(p).transformCache[t]!==a&&new RegExp("^\\("+r+"[^.]").test(n)&&(f=!0,delete i(p).transformCache[t])}),o.mobileHA&&(f=!0,delete i(p).transformCache.translate3d),f&&x.flushTransformCache(p),x.Values.removeClass(p,"velocity-animating")}if(!t&&o.complete&&!o.loop&&u===c-1)try{o.complete.call(n,n)}catch(d){setTimeout(function(){throw d},1)}s&&o.loop!==!0&&s(n),i(p)&&o.loop===!0&&!t&&($.each(i(p).tweensContainer,function(e,t){/^rotate/.test(e)&&360===parseFloat(t.endValue)&&(t.endValue=0,t.startValue=360),/^backgroundPosition/.test(e)&&100===parseFloat(t.endValue)&&"%"===t.unitType&&(t.endValue=0,t.startValue=100)}),v(p,"reverse",{loop:!0,delay:o.delay})),o.queue!==!1&&$.dequeue(p,o.queue)}v.State.calls[e]=!1;for(var g=0,m=v.State.calls.length;m>g;g++)if(v.State.calls[g]!==!1){l=!0;break}l===!1&&(v.State.isTicking=!1,delete v.State.calls,v.State.calls=[])}var f=function(){if(r.documentMode)return r.documentMode;for(var e=7;e>4;e--){var t=r.createElement("div");if(t.innerHTML="",t.getElementsByTagName("span").length)return t=null,e}return a}(),d=function(){var e=0;return t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(t){var r=(new Date).getTime(),a;return a=Math.max(0,16-(r-e)),e=r+a,setTimeout(function(){t(r+a)},a)}}(),g={isString:function(e){return"string"==typeof e},isArray:Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNode:function(e){return e&&e.nodeType},isNodeList:function(e){return"object"==typeof e&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(e))&&e.length!==a&&(0===e.length||"object"==typeof e[0]&&e[0].nodeType>0)},isWrapped:function(e){return e&&(e.jquery||t.Zepto&&t.Zepto.zepto.isZ(e))},isSVG:function(e){return t.SVGElement&&e instanceof t.SVGElement},isEmptyObject:function(e){for(var t in e)return!1;return!0}},$,m=!1;if(e.fn&&e.fn.jquery?($=e,m=!0):$=t.Velocity.Utilities,8>=f&&!m)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(7>=f)return void(jQuery.fn.velocity=jQuery.fn.animate);var y=400,h="swing",v={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:t.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:r.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:$,Redirects:{},Easings:{},Promise:t.Promise,defaults:{queue:"",duration:y,easing:h,begin:a,complete:a,progress:a,display:a,visibility:a,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0},init:function(e){$.data(e,"velocity",{isSVG:g.isSVG(e),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:2,patch:2},debug:!1};t.pageYOffset!==a?(v.State.scrollAnchor=t,v.State.scrollPropertyLeft="pageXOffset",v.State.scrollPropertyTop="pageYOffset"):(v.State.scrollAnchor=r.documentElement||r.body.parentNode||r.body,v.State.scrollPropertyLeft="scrollLeft",v.State.scrollPropertyTop="scrollTop");var b=function(){function e(e){return-e.tension*e.x-e.friction*e.v}function t(t,r,a){var n={x:t.x+a.dx*r,v:t.v+a.dv*r,tension:t.tension,friction:t.friction};return{dx:n.v,dv:e(n)}}function r(r,a){var n={dx:r.v,dv:e(r)},o=t(r,.5*a,n),i=t(r,.5*a,o),s=t(r,a,i),l=1/6*(n.dx+2*(o.dx+i.dx)+s.dx),u=1/6*(n.dv+2*(o.dv+i.dv)+s.dv);return r.x=r.x+l*a,r.v=r.v+u*a,r}return function a(e,t,n){var o={x:-1,v:0,tension:null,friction:null},i=[0],s=0,l=1e-4,u=.016,c,p,f;for(e=parseFloat(e)||500,t=parseFloat(t)||20,n=n||null,o.tension=e,o.friction=t,c=null!==n,c?(s=a(e,t),p=s/n*u):p=u;;)if(f=r(f||o,p),i.push(1+f.x),s+=16,!(Math.abs(f.x)>l&&Math.abs(f.v)>l))break;return c?function(e){return i[e*(i.length-1)|0]}:s}}();v.Easings={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},spring:function(e){return 1-Math.cos(4.5*e*Math.PI)*Math.exp(6*-e)}},$.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(e,t){v.Easings[t[0]]=l.apply(null,t[1])});var x=v.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var e=0;e=f)switch(e){case"name":return"filter";case"extract":var a=r.toString().match(/alpha\(opacity=(.*)\)/i);return r=a?a[1]/100:1;case"inject":return t.style.zoom=1,parseFloat(r)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(r),10)+")"}else switch(e){case"name":return"opacity";case"extract":return r;case"inject":return r}}},register:function(){9>=f||v.State.isGingerbread||(x.Lists.transformsBase=x.Lists.transformsBase.concat(x.Lists.transforms3D));for(var e=0;en&&(n=1),o=!/(\d)$/i.test(n);break;case"skew":o=!/(deg|\d)$/i.test(n);break;case"rotate":o=!/(deg|\d)$/i.test(n)}return o||(i(r).transformCache[t]="("+n+")"),i(r).transformCache[t]}}}();for(var e=0;e=f||3!==o.split(" ").length||(o+=" 1"),o;case"inject":return 8>=f?4===n.split(" ").length&&(n=n.split(/\s+/).slice(0,3).join(" ")):3===n.split(" ").length&&(n+=" 1"),(8>=f?"rgb":"rgba")+"("+n.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")"}}}()}},Names:{camelCase:function(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})},SVGAttribute:function(e){var t="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(f||v.State.isAndroid&&!v.State.isChrome)&&(t+="|transform"),new RegExp("^("+t+")$","i").test(e)},prefixCheck:function(e){if(v.State.prefixMatches[e])return[v.State.prefixMatches[e],!0];for(var t=["","Webkit","Moz","ms","O"],r=0,a=t.length;a>r;r++){var n;if(n=0===r?e:t[r]+e.replace(/^\w/,function(e){return e.toUpperCase()}),g.isString(v.State.prefixElement.style[n]))return v.State.prefixMatches[e]=n,[n,!0]}return[e,!1]}},Values:{hexToRgb:function(e){var t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,a;return e=e.replace(t,function(e,t,r,a){return t+t+r+r+a+a}),a=r.exec(e),a?[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16)]:[0,0,0]},isCSSNullValue:function(e){return 0==e||/^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(e)},getUnitType:function(e){return/^(rotate|skew)/i.test(e)?"deg":/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(e)?"":"px"},getDisplayType:function(e){var t=e&&e.tagName.toString().toLowerCase();return/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(t)?"inline":/^(li)$/i.test(t)?"list-item":/^(tr)$/i.test(t)?"table-row":/^(table)$/i.test(t)?"table":/^(tbody)$/i.test(t)?"table-row-group":"block"},addClass:function(e,t){e.classList?e.classList.add(t):e.className+=(e.className.length?" ":"")+t},removeClass:function(e,t){e.classList?e.classList.remove(t):e.className=e.className.toString().replace(new RegExp("(^|\\s)"+t.split(" ").join("|")+"(\\s|$)","gi")," ")}},getPropertyValue:function(e,r,n,o){function s(e,r){function n(){u&&x.setPropertyValue(e,"display","none")}var l=0;if(8>=f)l=$.css(e,r);else{var u=!1;if(/^(width|height)$/.test(r)&&0===x.getPropertyValue(e,"display")&&(u=!0,x.setPropertyValue(e,"display",x.Values.getDisplayType(e))),!o){if("height"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var c=e.offsetHeight-(parseFloat(x.getPropertyValue(e,"borderTopWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderBottomWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingTop"))||0)-(parseFloat(x.getPropertyValue(e,"paddingBottom"))||0);return n(),c}if("width"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var p=e.offsetWidth-(parseFloat(x.getPropertyValue(e,"borderLeftWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderRightWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingLeft"))||0)-(parseFloat(x.getPropertyValue(e,"paddingRight"))||0);return n(),p}}var d;d=i(e)===a?t.getComputedStyle(e,null):i(e).computedStyle?i(e).computedStyle:i(e).computedStyle=t.getComputedStyle(e,null),"borderColor"===r&&(r="borderTopColor"),l=9===f&&"filter"===r?d.getPropertyValue(r):d[r],(""===l||null===l)&&(l=e.style[r]),n()}if("auto"===l&&/^(top|right|bottom|left)$/i.test(r)){var g=s(e,"position");("fixed"===g||"absolute"===g&&/top|left/i.test(r))&&(l=$(e).position()[r]+"px")}return l}var l;if(x.Hooks.registered[r]){var u=r,c=x.Hooks.getRoot(u);n===a&&(n=x.getPropertyValue(e,x.Names.prefixCheck(c)[0])),x.Normalizations.registered[c]&&(n=x.Normalizations.registered[c]("extract",e,n)),l=x.Hooks.extractValue(u,n)}else if(x.Normalizations.registered[r]){var p,d;p=x.Normalizations.registered[r]("name",e),"transform"!==p&&(d=s(e,x.Names.prefixCheck(p)[0]),x.Values.isCSSNullValue(d)&&x.Hooks.templates[r]&&(d=x.Hooks.templates[r][1])),l=x.Normalizations.registered[r]("extract",e,d)}if(!/^[\d-]/.test(l))if(i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r))if(/^(height|width)$/i.test(r))try{l=e.getBBox()[r]}catch(g){l=0}else l=e.getAttribute(r);else l=s(e,x.Names.prefixCheck(r)[0]);return x.Values.isCSSNullValue(l)&&(l=0),v.debug>=2&&console.log("Get "+r+": "+l),l},setPropertyValue:function(e,r,a,n,o){var s=r;if("scroll"===r)o.container?o.container["scroll"+o.direction]=a:"Left"===o.direction?t.scrollTo(a,o.alternateValue):t.scrollTo(o.alternateValue,a);else if(x.Normalizations.registered[r]&&"transform"===x.Normalizations.registered[r]("name",e))x.Normalizations.registered[r]("inject",e,a),s="transform",a=i(e).transformCache[r];else{if(x.Hooks.registered[r]){var l=r,u=x.Hooks.getRoot(r);n=n||x.getPropertyValue(e,u),a=x.Hooks.injectValue(l,a,n),r=u}if(x.Normalizations.registered[r]&&(a=x.Normalizations.registered[r]("inject",e,a),r=x.Normalizations.registered[r]("name",e)),s=x.Names.prefixCheck(r)[0],8>=f)try{e.style[s]=a}catch(c){v.debug&&console.log("Browser does not support ["+a+"] for ["+s+"]")}else i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r)?e.setAttribute(r,a):e.style[s]=a;v.debug>=2&&console.log("Set "+r+" ("+s+"): "+a)}return[s,a]},flushTransformCache:function(e){function t(t){return parseFloat(x.getPropertyValue(e,t))}var r="";if((f||v.State.isAndroid&&!v.State.isChrome)&&i(e).isSVG){var a={translate:[t("translateX"),t("translateY")],skewX:[t("skewX")],skewY:[t("skewY")],scale:1!==t("scale")?[t("scale"),t("scale")]:[t("scaleX"),t("scaleY")],rotate:[t("rotateZ"),0,0]};$.each(i(e).transformCache,function(e){/^translate/i.test(e)?e="translate":/^scale/i.test(e)?e="scale":/^rotate/i.test(e)&&(e="rotate"),a[e]&&(r+=e+"("+a[e].join(" ")+") ",delete a[e])})}else{var n,o;$.each(i(e).transformCache,function(t){return n=i(e).transformCache[t],"transformPerspective"===t?(o=n,!0):(9===f&&"rotateZ"===t&&(t="rotate"),void(r+=t+n+" "))}),o&&(r="perspective"+o+" "+r)}x.setPropertyValue(e,"transform",r)}};x.Hooks.register(),x.Normalizations.register(),v.hook=function(e,t,r){var n=a;return e=o(e),$.each(e,function(e,o){if(i(o)===a&&v.init(o),r===a)n===a&&(n=v.CSS.getPropertyValue(o,t));else{var s=v.CSS.setPropertyValue(o,t,r);"transform"===s[0]&&v.CSS.flushTransformCache(o),n=s}}),n};var S=function(){function e(){return l?T.promise||null:f}function n(){function e(e){function p(e,t){var r=a,i=a,s=a;return g.isArray(e)?(r=e[0],!g.isArray(e[1])&&/^[\d-]/.test(e[1])||g.isFunction(e[1])||x.RegEx.isHex.test(e[1])?s=e[1]:(g.isString(e[1])&&!x.RegEx.isHex.test(e[1])||g.isArray(e[1]))&&(i=t?e[1]:u(e[1],o.duration),e[2]!==a&&(s=e[2]))):r=e,t||(i=i||o.easing),g.isFunction(r)&&(r=r.call(n,w,P)),g.isFunction(s)&&(s=s.call(n,w,P)),[r||0,i,s]}function f(e,t){var r,a;return a=(t||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(e){return r=e,""}),r||(r=x.Values.getUnitType(e)),[a,r]}function d(){var e={myParent:n.parentNode||r.body,position:x.getPropertyValue(n,"position"),fontSize:x.getPropertyValue(n,"fontSize")},a=e.position===N.lastPosition&&e.myParent===N.lastParent,o=e.fontSize===N.lastFontSize;N.lastParent=e.myParent,N.lastPosition=e.position,N.lastFontSize=e.fontSize;var s=100,l={};if(o&&a)l.emToPx=N.lastEmToPx,l.percentToPxWidth=N.lastPercentToPxWidth,l.percentToPxHeight=N.lastPercentToPxHeight;else{var u=i(n).isSVG?r.createElementNS("http://www.w3.org/2000/svg","rect"):r.createElement("div");v.init(u),e.myParent.appendChild(u),$.each(["overflow","overflowX","overflowY"],function(e,t){v.CSS.setPropertyValue(u,t,"hidden")}),v.CSS.setPropertyValue(u,"position",e.position),v.CSS.setPropertyValue(u,"fontSize",e.fontSize),v.CSS.setPropertyValue(u,"boxSizing","content-box"),$.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(e,t){v.CSS.setPropertyValue(u,t,s+"%")}),v.CSS.setPropertyValue(u,"paddingLeft",s+"em"),l.percentToPxWidth=N.lastPercentToPxWidth=(parseFloat(x.getPropertyValue(u,"width",null,!0))||1)/s,l.percentToPxHeight=N.lastPercentToPxHeight=(parseFloat(x.getPropertyValue(u,"height",null,!0))||1)/s,l.emToPx=N.lastEmToPx=(parseFloat(x.getPropertyValue(u,"paddingLeft"))||1)/s,e.myParent.removeChild(u)}return null===N.remToPx&&(N.remToPx=parseFloat(x.getPropertyValue(r.body,"fontSize"))||16),null===N.vwToPx&&(N.vwToPx=parseFloat(t.innerWidth)/100,N.vhToPx=parseFloat(t.innerHeight)/100),l.remToPx=N.remToPx,l.vwToPx=N.vwToPx,l.vhToPx=N.vhToPx,v.debug>=1&&console.log("Unit ratios: "+JSON.stringify(l),n),l}if(o.begin&&0===w)try{o.begin.call(m,m)}catch(y){setTimeout(function(){throw y},1)}if("scroll"===k){var S=/^x$/i.test(o.axis)?"Left":"Top",V=parseFloat(o.offset)||0,C,A,F;o.container?g.isWrapped(o.container)||g.isNode(o.container)?(o.container=o.container[0]||o.container,C=o.container["scroll"+S],F=C+$(n).position()[S.toLowerCase()]+V):o.container=null:(C=v.State.scrollAnchor[v.State["scrollProperty"+S]],A=v.State.scrollAnchor[v.State["scrollProperty"+("Left"===S?"Top":"Left")]],F=$(n).offset()[S.toLowerCase()]+V),s={scroll:{rootPropertyValue:!1,startValue:C,currentValue:C,endValue:F,unitType:"",easing:o.easing,scrollData:{container:o.container,direction:S,alternateValue:A}},element:n},v.debug&&console.log("tweensContainer (scroll): ",s.scroll,n)}else if("reverse"===k){if(!i(n).tweensContainer)return void $.dequeue(n,o.queue);"none"===i(n).opts.display&&(i(n).opts.display="auto"),"hidden"===i(n).opts.visibility&&(i(n).opts.visibility="visible"),i(n).opts.loop=!1,i(n).opts.begin=null,i(n).opts.complete=null,b.easing||delete o.easing,b.duration||delete o.duration,o=$.extend({},i(n).opts,o);var E=$.extend(!0,{},i(n).tweensContainer);for(var j in E)if("element"!==j){var H=E[j].startValue;E[j].startValue=E[j].currentValue=E[j].endValue,E[j].endValue=H,g.isEmptyObject(b)||(E[j].easing=o.easing),v.debug&&console.log("reverse tweensContainer ("+j+"): "+JSON.stringify(E[j]),n)}s=E}else if("start"===k){var E;i(n).tweensContainer&&i(n).isAnimating===!0&&(E=i(n).tweensContainer),$.each(h,function(e,t){if(RegExp("^"+x.Lists.colors.join("$|^")+"$").test(e)){var r=p(t,!0),n=r[0],o=r[1],i=r[2];if(x.RegEx.isHex.test(n)){for(var s=["Red","Green","Blue"],l=x.Values.hexToRgb(n),u=i?x.Values.hexToRgb(i):a,c=0;cO;O++){var z={delay:F.delay,progress:F.progress};O===R-1&&(z.display=F.display,z.visibility=F.visibility,z.complete=F.complete),S(m,"reverse",z)}return e()}};v=$.extend(S,v),v.animate=S;var P=t.requestAnimationFrame||d;return v.State.isMobile||r.hidden===a||r.addEventListener("visibilitychange",function(){r.hidden?(P=function(e){return setTimeout(function(){e(!0)},16)},c()):P=t.requestAnimationFrame||d}),e.Velocity=v,e!==t&&(e.fn.velocity=S,e.fn.velocity.defaults=v.defaults),$.each(["Down","Up"],function(e,t){v.Redirects["slide"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u=l.begin,c=l.complete,p={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},f={};l.display===a&&(l.display="Down"===t?"inline"===v.CSS.Values.getDisplayType(e)?"inline-block":"block":"none"),l.begin=function(){u&&u.call(i,i);for(var r in p){f[r]=e.style[r];var a=v.CSS.getPropertyValue(e,r);p[r]="Down"===t?[a,0]:[0,a]}f.overflow=e.style.overflow,e.style.overflow="hidden"},l.complete=function(){for(var t in f)e.style[t]=f[t];c&&c.call(i,i),s&&s.resolver(i)},v(e,p,l)}}),$.each(["In","Out"],function(e,t){v.Redirects["fade"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u={opacity:"In"===t?1:0},c=l.complete;l.complete=n!==o-1?l.begin=null:function(){c&&c.call(i,i),s&&s.resolver(i)},l.display===a&&(l.display="In"===t?"auto":"none"),v(this,u,l)}}),v}(window.jQuery||window.Zepto||window,window,document)}); \ No newline at end of file diff --git a/lib/velocity/velocity.ui.min.js b/lib/velocity/velocity.ui.min.js new file mode 100644 index 0000000..8706945 --- /dev/null +++ b/lib/velocity/velocity.ui.min.js @@ -0,0 +1,2 @@ +/* VelocityJS.org UI Pack (5.0.4). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License. Portions copyright Daniel Eden, Christian Pucci. */ +!function(t){"function"==typeof require&&"object"==typeof exports?module.exports=t():"function"==typeof define&&define.amd?define(["velocity"],t):t()}(function(){return function(t,a,e,r){function n(t,a){var e=[];return t&&a?($.each([t,a],function(t,a){var r=[];$.each(a,function(t,a){for(;a.toString().length<5;)a="0"+a;r.push(a)}),e.push(r.join(""))}),parseFloat(e[0])>parseFloat(e[1])):!1}if(!t.Velocity||!t.Velocity.Utilities)return void(a.console&&console.log("Velocity UI Pack: Velocity must be loaded first. Aborting."));var i=t.Velocity,$=i.Utilities,s=i.version,o={major:1,minor:1,patch:0};if(n(o,s)){var l="Velocity UI Pack: You need to update Velocity (jquery.velocity.js) to a newer version. Visit http://github.com/julianshapiro/velocity.";throw alert(l),new Error(l)}i.RegisterEffect=i.RegisterUI=function(t,a){function e(t,a,e,r){var n=0,s;$.each(t.nodeType?[t]:t,function(t,a){r&&(e+=t*r),s=a.parentNode,$.each(["height","paddingTop","paddingBottom","marginTop","marginBottom"],function(t,e){n+=parseFloat(i.CSS.getPropertyValue(a,e))})}),i.animate(s,{height:("In"===a?"+":"-")+"="+n},{queue:!1,easing:"ease-in-out",duration:e*("In"===a?.6:1)})}return i.Redirects[t]=function(n,s,o,l,c,u){function f(){s.display!==r&&"none"!==s.display||!/Out$/.test(t)||$.each(c.nodeType?[c]:c,function(t,a){i.CSS.setPropertyValue(a,"display","none")}),s.complete&&s.complete.call(c,c),u&&u.resolver(c||n)}var p=o===l-1;a.defaultDuration="function"==typeof a.defaultDuration?a.defaultDuration.call(c,c):parseFloat(a.defaultDuration);for(var d=0;d1&&($.each(a.reverse(),function(t,e){var r=a[t+1];if(r){var n=e.o||e.options,s=r.o||r.options,o=n&&n.sequenceQueue===!1?"begin":"complete",l=s&&s[o],c={};c[o]=function(){var t=r.e||r.elements,a=t.nodeType?[t]:t;l&&l.call(a,a),i(e)},r.o?r.o=$.extend({},s,c):r.options=$.extend({},s,c)}}),a.reverse()),i(a[0])}}(window.jQuery||window.Zepto||window,window,document)}); \ No newline at end of file diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..20fdaf6 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,1306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    前言

    最近,在进行软件开发工作中遇到了一个问题,觉得比较有趣,在此进行以下记录。

    +

    问题比较简单,需要检查windows系统下的C:\Windows\System32路径下是否存在特定的用户文件(dll格式)。在这里使用_access函数(C运行库函数)对文件进行检查,该函数主要用于检查文件或目录是否存在及其对应的读写权限。但是开发完成后,在自检过程中却发现使用_access函数无法达到预期,主要表现为文件已存在,但是函数返回结果却表明文件不存在。如果换用win32 API函数PathFileExistsA却正常,文件存在于对应目录。本文即对_access函数的异常进行分析。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    原文网址:https://libvirt.org/formatdomain.html#id8

    +

    IOThreads是支持磁盘设备的专用事件循环线程,用于执行块I/O请求,以提高可伸缩性,尤其是在具有许多LUN的SMP主机/客户机上。自1.2.8起(仅限于QEMU)。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    原文网址:https://libvirt.org/formatdomain.html#id8

    +

    一些虚拟机管理器运行控制呈现给客户机的系统信息(例如虚拟机管理器可以填充SMBIOS并通过客户机的dmidecode指令进行检查)。可选元素项sysinfo包含了所有此类信息。自0.8.7版本起。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    原文网址:https://libvirt.org/formatdomain.html#id8

    +

    这里有很多不同的方法用来引导操作系统开机,每一个都有优点与缺点。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    原文网址:https://libvirt.org/formatdomain.html#id8

    +

    所有的虚拟机都需要的根元素是domain。其包含两个属性项,typo属性项指明了被用来运行虚拟机的虚拟机管理程序(hypervisor)。该项允许的数值与驱动相关,包含了xen、kvm、hvf(从8.1.0和QEMU 2.12版本起)、qemu和lxc。第二个属性项是id,代表运行客户机(guest)的一个唯一数字标识符。不活跃的客户机没有id。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    作为一个理科生,我其实并不是十分喜欢哲学,对于一个无法证实或证伪的学科,实在是觉得谈哲学更近似于谈玄,打嘴炮为主。但是在社会生活中,哲学的诸多理论却实在的影响着人类生活的方方面面。尤其是终极三问,我是谁、我从哪里来、我到哪里去是很多人都十分感兴趣的话题,我也不例外。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    什么是virtio

    virtio 是一种 I/O 半虚拟化解决方案,是一套通用 I/O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备的抽象。提供了一套上层应用与各 Hypervisor 虚拟化设备(KVM,Xen,VMware等)之间的通信框架和编程接口,减少跨平台所带来的兼容性问题,大大提高驱动程序开发效率。
    virtio 协议定义了各类设备与驱动,定义了它们如何初始化,如何通信,如何通知等。其中,最核心的是设备与驱动的通信机制,避免了每次访问外设寄存器都要 vm_exit/vm_enter 的问题。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    kvm下的windows虚拟机转储可从子机侧和母机侧两方面进行。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    系统版本

    (1)Ubuntu系统版本:20.04.1LTS;
    (2)gcc编译器版本:gcc-9.3.0;
    当前可找到的gcc最新版本为gcc-10,但是笔者在安装gcc-10版本时发现一些依赖项无法安装,因此选取了gcc-9版本。如果是在联网情况下安装,直接使用apt相关的安装指令即可。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page/3/index.html b/page/3/index.html new file mode 100644 index 0000000..78b6f95 --- /dev/null +++ b/page/3/index.html @@ -0,0 +1,541 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    编译时找不到.a后缀的静态库

    大多数讲解QFtp配置的文章中都提到了讲pro文件中的config -= static改为config += static以生成静态库。但是需注意一点,msvc编译器与gnu编译器生产的静态库文件是不同的,msvc下的静态库文件是.lib后缀,而gnu下的静态库文件是.a后缀。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + +
    + + + + + +
    +

    + + +

    + + +
    + + + + +
    + + +

    最近有关于FTP文件传输的相关项目,因此查询相关资料编写了一个示例程序。程序运行正常,但在测试过程中使用含有中文的文件进行测试时,程序报错。

    + +
    + + 阅读全文 » + +
    + + + +
    + + + + +
    +
    +
    +
    + + + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/placeholder b/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/tags/Block-I-O-Tuning/index.html b/tags/Block-I-O-Tuning/index.html new file mode 100644 index 0000000..78577be --- /dev/null +++ b/tags/Block-I-O-Tuning/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Block I/O Tuning | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    + +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/CPU-Allocation/index.html b/tags/CPU-Allocation/index.html new file mode 100644 index 0000000..c24b5f3 --- /dev/null +++ b/tags/CPU-Allocation/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: CPU Allocation | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    CPU Allocation + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/CPU-Tuning/index.html b/tags/CPU-Tuning/index.html new file mode 100644 index 0000000..368873e --- /dev/null +++ b/tags/CPU-Tuning/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: CPU Tuning | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    CPU Tuning + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/CPU-model-and-topology/index.html b/tags/CPU-model-and-topology/index.html new file mode 100644 index 0000000..77bfcf2 --- /dev/null +++ b/tags/CPU-model-and-topology/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: CPU model and topology | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    CPU model and topology + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/C\350\257\255\350\250\200/index.html" "b/tags/C\350\257\255\350\250\200/index.html" new file mode 100644 index 0000000..82a3ffe --- /dev/null +++ "b/tags/C\350\257\255\350\250\200/index.html" @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: C语言 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    C语言 + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/FTP/index.html b/tags/FTP/index.html new file mode 100644 index 0000000..a33a185 --- /dev/null +++ b/tags/FTP/index.html @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: FTP | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    FTP + 标签 +

    +
    + + +
    + 2020 +
    + + +
    + 2019 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Fibre-Channel-VMID/index.html b/tags/Fibre-Channel-VMID/index.html new file mode 100644 index 0000000..77c769a --- /dev/null +++ b/tags/Fibre-Channel-VMID/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Fibre Channel VMID | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    + +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/General-metadata/index.html b/tags/General-metadata/index.html new file mode 100644 index 0000000..35706a5 --- /dev/null +++ b/tags/General-metadata/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: General metadata | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    General metadata + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/IOThreads-Allocation/index.html b/tags/IOThreads-Allocation/index.html new file mode 100644 index 0000000..81df7f7 --- /dev/null +++ b/tags/IOThreads-Allocation/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: IOThreads Allocation | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    IOThreads Allocation + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Memory-Allocation/index.html b/tags/Memory-Allocation/index.html new file mode 100644 index 0000000..ca4b24f --- /dev/null +++ b/tags/Memory-Allocation/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Memory Allocation | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    Memory Allocation + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Memory-Backing/index.html b/tags/Memory-Backing/index.html new file mode 100644 index 0000000..9e649b5 --- /dev/null +++ b/tags/Memory-Backing/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Memory Backing | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    Memory Backing + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Memory-Tuning/index.html b/tags/Memory-Tuning/index.html new file mode 100644 index 0000000..2c6319e --- /dev/null +++ b/tags/Memory-Tuning/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Memory Tuning | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    Memory Tuning + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/NUMA-Node-Tuning/index.html b/tags/NUMA-Node-Tuning/index.html new file mode 100644 index 0000000..3fc50ee --- /dev/null +++ b/tags/NUMA-Node-Tuning/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: NUMA Node Tuning | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    + +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/OPENSSL-ia32cap/index.html b/tags/OPENSSL-ia32cap/index.html new file mode 100644 index 0000000..25b9b65 --- /dev/null +++ b/tags/OPENSSL-ia32cap/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: OPENSSL_ia32cap | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    OPENSSL_ia32cap + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/OpenSSL/index.html b/tags/OpenSSL/index.html new file mode 100644 index 0000000..151e77b --- /dev/null +++ b/tags/OpenSSL/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: OpenSSL | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    OpenSSL + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Operating-system-booting/index.html b/tags/Operating-system-booting/index.html new file mode 100644 index 0000000..f29d55e --- /dev/null +++ b/tags/Operating-system-booting/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Operating system booting | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    Operating system booting + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/QFtp/index.html b/tags/QFtp/index.html new file mode 100644 index 0000000..ebbadc4 --- /dev/null +++ b/tags/QFtp/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: QFtp | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    QFtp + 标签 +

    +
    + + +
    + 2020 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/QT/index.html b/tags/QT/index.html new file mode 100644 index 0000000..64e9b8b --- /dev/null +++ b/tags/QT/index.html @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: QT | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    QT + 标签 +

    +
    + + +
    + 2020 +
    + + +
    + 2019 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Resource-partitioning/index.html b/tags/Resource-partitioning/index.html new file mode 100644 index 0000000..bcf5160 --- /dev/null +++ b/tags/Resource-partitioning/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Resource partitioning | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    Resource partitioning + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/SMBIOS-System-Information/index.html b/tags/SMBIOS-System-Information/index.html new file mode 100644 index 0000000..75c49d2 --- /dev/null +++ b/tags/SMBIOS-System-Information/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: SMBIOS System Information | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    SMBIOS System Information + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Ubuntu/index.html b/tags/Ubuntu/index.html new file mode 100644 index 0000000..1789d5b --- /dev/null +++ b/tags/Ubuntu/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Ubuntu | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    Ubuntu + 标签 +

    +
    + + +
    + 2020 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/access-global-variables/index.html b/tags/access-global-variables/index.html new file mode 100644 index 0000000..747e441 --- /dev/null +++ b/tags/access-global-variables/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: access global variables | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    access global variables + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/access-local-variables/index.html b/tags/access-local-variables/index.html new file mode 100644 index 0000000..fe92e85 --- /dev/null +++ b/tags/access-local-variables/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: access local variables | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    access local variables + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/access\345\207\275\346\225\260/index.html" "b/tags/access\345\207\275\346\225\260/index.html" new file mode 100644 index 0000000..6365567 --- /dev/null +++ "b/tags/access\345\207\275\346\225\260/index.html" @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: _access函数 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    _access函数 + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/convert-virtual-address-to-physical-address/index.html b/tags/convert-virtual-address-to-physical-address/index.html new file mode 100644 index 0000000..a874dad --- /dev/null +++ b/tags/convert-virtual-address-to-physical-address/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: convert virtual address to physical address | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    convert virtual address to physical address + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/coredump/index.html b/tags/coredump/index.html new file mode 100644 index 0000000..1dacf60 --- /dev/null +++ b/tags/coredump/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: coredump | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    coredump + 标签 +

    +
    + + +
    + 2021 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/gcc/index.html b/tags/gcc/index.html new file mode 100644 index 0000000..da560d1 --- /dev/null +++ b/tags/gcc/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: gcc | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    gcc + 标签 +

    +
    + + +
    + 2020 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..e536c7c --- /dev/null +++ b/tags/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签 | 怀德维宁 + + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/kvm/index.html b/tags/kvm/index.html new file mode 100644 index 0000000..29e0853 --- /dev/null +++ b/tags/kvm/index.html @@ -0,0 +1,576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: kvm | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/kvm/page/2/index.html b/tags/kvm/page/2/index.html new file mode 100644 index 0000000..7114c75 --- /dev/null +++ b/tags/kvm/page/2/index.html @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: kvm | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    kvm + 标签 +

    +
    + + +
    + 2021 +
    + + + +
    +
    + + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/libvirt/index.html b/tags/libvirt/index.html new file mode 100644 index 0000000..38c20ee --- /dev/null +++ b/tags/libvirt/index.html @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: libvirt | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/physical-address/index.html b/tags/physical-address/index.html new file mode 100644 index 0000000..a4f8c27 --- /dev/null +++ b/tags/physical-address/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: physical address | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    physical address + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/reading-and-writng-memory/index.html b/tags/reading-and-writng-memory/index.html new file mode 100644 index 0000000..8ede0ae --- /dev/null +++ b/tags/reading-and-writng-memory/index.html @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: reading and writng memory | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    reading and writng memory + 标签 +

    +
    + + +
    + 2023 +
    + + + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/standard-debugging-techniques/index.html b/tags/standard-debugging-techniques/index.html new file mode 100644 index 0000000..4c93dc5 --- /dev/null +++ b/tags/standard-debugging-techniques/index.html @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: standard debugging techniques | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    standard debugging techniques + 标签 +

    +
    + + +
    + 2023 +
    + + + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/virtio/index.html b/tags/virtio/index.html new file mode 100644 index 0000000..f695603 --- /dev/null +++ b/tags/virtio/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: virtio | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    virtio + 标签 +

    +
    + + +
    + 2021 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/virtual-address/index.html b/tags/virtual-address/index.html new file mode 100644 index 0000000..6e507c2 --- /dev/null +++ b/tags/virtual-address/index.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: virtual address | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    virtual address + 标签 +

    +
    + + +
    + 2023 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/windbg/index.html b/tags/windbg/index.html new file mode 100644 index 0000000..31ce4df --- /dev/null +++ b/tags/windbg/index.html @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: windbg | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/windows/index.html b/tags/windows/index.html new file mode 100644 index 0000000..3710a19 --- /dev/null +++ b/tags/windows/index.html @@ -0,0 +1,433 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: windows | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    windows + 标签 +

    +
    + + +
    + 2023 +
    + + + + +
    + 2021 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\346\226\207\344\273\266\346\243\200\346\237\245/index.html" "b/tags/\346\226\207\344\273\266\346\243\200\346\237\245/index.html" new file mode 100644 index 0000000..239f668 --- /dev/null +++ "b/tags/\346\226\207\344\273\266\346\243\200\346\237\245/index.html" @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 文件检查 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    文件检查 + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\347\273\210\346\236\201\344\270\211\351\227\256/index.html" "b/tags/\347\273\210\346\236\201\344\270\211\351\227\256/index.html" new file mode 100644 index 0000000..8aad061 --- /dev/null +++ "b/tags/\347\273\210\346\236\201\344\270\211\351\227\256/index.html" @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 终极三问 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    终极三问 + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\347\274\226\350\257\221\347\216\257\345\242\203/index.html" "b/tags/\347\274\226\350\257\221\347\216\257\345\242\203/index.html" new file mode 100644 index 0000000..0924ed3 --- /dev/null +++ "b/tags/\347\274\226\350\257\221\347\216\257\345\242\203/index.html" @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 编译环境 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    编译环境 + 标签 +

    +
    + + +
    + 2022 +
    + + + +
    +
    + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\347\277\273\350\257\221/index.html" "b/tags/\347\277\273\350\257\221/index.html" new file mode 100644 index 0000000..18e4c8e --- /dev/null +++ "b/tags/\347\277\273\350\257\221/index.html" @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 翻译 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\350\231\232\346\213\237\345\214\226/index.html" "b/tags/\350\231\232\346\213\237\345\214\226/index.html" new file mode 100644 index 0000000..d58cd1c --- /dev/null +++ "b/tags/\350\231\232\346\213\237\345\214\226/index.html" @@ -0,0 +1,576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 虚拟化 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\350\231\232\346\213\237\345\214\226/page/2/index.html" "b/tags/\350\231\232\346\213\237\345\214\226/page/2/index.html" new file mode 100644 index 0000000..1e628e8 --- /dev/null +++ "b/tags/\350\231\232\346\213\237\345\214\226/page/2/index.html" @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 虚拟化 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + +
    + + + + + +
    +
    +
    +

    虚拟化 + 标签 +

    +
    + + +
    + 2021 +
    + + + +
    +
    + + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/\350\231\232\346\213\237\346\234\272xml\351\205\215\347\275\256\346\226\207\344\273\266/index.html" "b/tags/\350\231\232\346\213\237\346\234\272xml\351\205\215\347\275\256\346\226\207\344\273\266/index.html" new file mode 100644 index 0000000..34530da --- /dev/null +++ "b/tags/\350\231\232\346\213\237\346\234\272xml\351\205\215\347\275\256\346\226\207\344\273\266/index.html" @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: 虚拟机xml配置文件 | 怀德维宁 + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
    + + + +

    怀德维宁

    + +
    +

    大邦维屏,大宗维翰。怀德维宁,宗子维城。

    +
    + + +
    + + + + + + + + + +
    +
    + + +
    + + 0% +
    + + +
    +
    +
    + + + + + + + +
    + + + + + + + + +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +