当前位置: 首页 > news >正文

VFIO软件依赖——VFIO协议

文章目录

  • 背景
  • PCI设备模拟
    • PCI设备抽象
    • VFIO协议
  • 实验
  • Q&A

背景

  • 在虚拟化应用场景中,虚拟机想要在访问PCI设备时达到IO性能最优,最直接的方法就是将物理设备暴露给虚拟机,虚拟机对设备的访问不经过任何中间层的转换,没有虚拟化的损耗。
  • 但我们知道Linux没有为用户程序提供这样的设备访问机制,所有PCI设备都在kernel的管理之下,即使我们能够让一个PCI物理设备不被kernel管理,直接供用户态的程序使用,在安全上也不被允许,因为我们知道PCI设备可以DMA,用户程序管理的PCI设备一旦能够DMA,那么用户程序就能借助PCI设备访问整个系统内存。延伸到虚拟化场景下,一旦一个虚拟机拥有的PCI设备能够做DMA,这个虚机就能借助PCI设备入侵整个主机系统。
  • 想要提供高IO性能的PCI设备给虚拟机,除尽量减少PCI设备访问损耗,还需要增加一层保护,防止访问PCI设备的应用程序借助PCI的DMA能力入侵系统。
  • VFIO协议应时而生,它通过定义一套标准接口将PCI设备的信息提供给用户程序,替代了传统的通过PCI driver来获取PCI设备信息的方式,从而让用户态程序也能直接访问PCI设备信息。

PCI设备模拟

PCI设备抽象

  • 我们知道访问PCI设备的传统步骤是首先通过PCI驱动框架枚举pci总线上所有设备,根据PCI硬件的要求读取其配置空间内容、bar空间类型和大小,记录到内核PCI设备相关数据结构并为内存BAR空间映射系统内存。
  • 以上方式是访问PCI最自然的方式:根据厂商要求编写驱动,访问硬件设备。而用户态程序要访问PCI设备的信息,不能采用此方式,因为用户态程序无法像内核一样利用驱动框架枚举PCI设备,也无法针对设定PCI设备进行读写以获取其信息。那用户态程序应该怎么做才能访问PCI设备信息呢?其实用户态程序并不关心何种方式获取PCI信息,只关心最终结果,即PCI设备的信息,因此只要内核为用户态程序这样的信息即可。
  • 为达到上述目的,内核的VFIO框架对PCI设备进行了抽象,将PCI设备的所有信息抽象为一组Region。如下图所示:
    请添加图片描述
  • PCI设备所有信息记录在上述region中,通过ioctl命令字暴露给用户态程序。用户态程序在虚拟化场景下可以是Qemu或者ovs程序,我们知道Qemu可以软件模拟PCI设备,在使用VFIO设备后,当客户机有访问PCI设备的请求时,Qemu可以直接通过ioctl命令从内核获取信息,直接返回给客户机,Qemu完成了将Region重新组装成PCI设备,呈现给客户机的工作,如下图所示:
    请添加图片描述

VFIO协议

  • 上一节中定义了一套接口和对应的数据结构,用于描述PCI设备信息,这套接口即所谓的VFIO协议,在最初的实现中,VFIO协议用来支持硬件PCI设备透传功能,因此协议两端分别是用户态程序和kernel,协议通过ioctl命令字实现。但该协议可以扩展到任何场景,比如vfio-user设备就是另一种场景,协议两端都在用户态,协议通过unix socket实现。我们将发起调用的一方称为客户端(client),接受调用并返回的一方称为服务端(server)。如下图所示:
    请添加图片描述
  • 在传统的VFIO-PCI硬件设备透传场景, 由于硬件机制的原因,某些PCI设备做IO可能通过物理关联的另一个设备进行,为防止这种侵入,内核在实现上提出了组和容器的概念,用于定义隔离PCI设备的最小单元,这部分实现与VFIO标准协议无关,相关命令可以认为是具体实现相关的,如上图的中server-specific command
  1. VFIO User
  • 用户态的VFIO协议不涉及PCI设备的隔离,定义了如下命令字:
USER_VERSION
DMA_MAP
DMA_UNMAP
DEVICE_GET_INFO
DEVICE_GET_REGION_INFO
DEVICE_GET_REGION_IO_FDS
DEVICE_GET_IRQ_INFO
EVICE_SET_IRQS
REGION_READ
REGION_WRITE
DMA_WRITE
DEVICE_RESET
  1. VFIO Kernel
  • 对于kernel场景,VFIO PCI设备除了实现标准的VFIO协议外,还需要定义PCI设备隔离最小单元相关命令字,如下:
  • 实现相关命令字:
GET_API_VERSION
CHECK_EXTENSION
SET_IOMMU
GROUP_SET_STATUS
GROUP_SET_CONTAINER
......

实验

  • 我们的实验非常简单,选取一个PCI设备,将其加载为VFIO PCI驱动,然后编写用户态程序访问该设备并获取其信息,具体实现我们参考了内核的VFIO文档。实验我们以透传显卡为例。
  1. 查看显卡信息
[root@Hyman_server1 ~]# lspci -s 04:00.0
04:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Turks [Radeon HD 7600 Series]
  1. 查看显卡所属的group
[root@Hyman_server1 ~]# readlink /sys/bus/pci/devices/0000\:04\:00.0/iommu_group
../../../../kernel/iommu_groups/14
  1. 查看显卡所属组包含的设备
[root@Hyman_server1 ~]# ll /sys/kernel/iommu_groups/14/devices/
total 0
lrwxrwxrwx 1 root root 0 Feb 13 23:27 0000:04:00.0 -> ../../../../devices/pci0000:00/0000:00:07.0/0000:04:00.0
lrwxrwxrwx 1 root root 0 Feb 13 23:27 0000:04:00.1 -> ../../../../devices/pci0000:00/0000:00:07.0/0000:04:00.1
  1. 卸载同组内的所有设备的内核驱动,加载为vfio-pci驱动,这里我们通过Libvirt提供的工具操作
  • 查看显卡设备,显卡所属的group包含两个设备,一个显卡,一个声卡
[root@Hyman_server1 ~]# virsh nodedev-dumpxml pci_0000_04_00_0
<device><name>pci_0000_04_00_0</name><path>/sys/devices/pci0000:00/0000:00:07.0/0000:04:00.0</path><parent>pci_0000_00_07_0</parent><driver><name>radeon</name></driver><capability type='pci'><class>0x030000</class><domain>0</domain><bus>4</bus><slot>0</slot><function>0</function><product id='0x675b'>Turks [Radeon HD 7600 Series]</product><vendor id='0x1002'>Advanced Micro Devices, Inc. [AMD/ATI]</vendor><iommuGroup number='14'><address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/><address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/></iommuGroup><pci-express><link validity='cap' port='0' speed='5' width='16'/><link validity='sta' speed='5' width='16'/></pci-express></capability>
</device>[root@Hyman_server1 ~]# virsh nodedev-dumpxml pci_0000_04_00_1
<device><name>pci_0000_04_00_1</name><path>/sys/devices/pci0000:00/0000:00:07.0/0000:04:00.1</path><parent>pci_0000_00_07_0</parent><driver><name>snd_hda_intel</name></driver><capability type='pci'><class>0x040300</class><domain>0</domain><bus>4</bus><slot>0</slot><function>1</function><product id='0xaa90'>Turks HDMI Audio [Radeon HD 6500/6600 / 6700M Series]</product><vendor id='0x1002'>Advanced Micro Devices, Inc. [AMD/ATI]</vendor><iommuGroup number='14'><address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/><address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/></iommuGroup><pci-express><link validity='cap' port='0' speed='5' width='16'/><link validity='sta' speed='5' width='16'/></pci-express></capability>
</device>
  • 卸载显卡和声卡驱动
[root@Hyman_server1 ~]# virsh nodedev-detach pci_0000_04_00_0
Device pci_0000_04_00_0 detached[root@Hyman_server1 ~]# virsh nodedev-detach pci_0000_04_00_1
Device pci_0000_04_00_1 detached
  • 检查卸载情况
[root@Hyman_server1 ~]# virsh nodedev-dumpxml pci_0000_04_00_0
<device><name>pci_0000_04_00_0</name><path>/sys/devices/pci0000:00/0000:00:07.0/0000:04:00.0</path><parent>pci_0000_00_07_0</parent><driver><name>vfio-pci</name></driver><capability type='pci'><class>0x030000</class><domain>0</domain><bus>4</bus><slot>0</slot><function>0</function><product id='0x675b'>Turks [Radeon HD 7600 Series]</product><vendor id='0x1002'>Advanced Micro Devices, Inc. [AMD/ATI]</vendor><iommuGroup number='14'><address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/><address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/></iommuGroup><pci-express><link validity='cap' port='0' speed='5' width='16'/><link validity='sta' speed='5' width='16'/></pci-express></capability>
</device>[root@Hyman_server1 ~]# virsh nodedev-dumpxml pci_0000_04_00_1
<device><name>pci_0000_04_00_1</name><path>/sys/devices/pci0000:00/0000:00:07.0/0000:04:00.1</path><parent>pci_0000_00_07_0</parent><driver><name>vfio-pci</name></driver><capability type='pci'><class>0x040300</class><domain>0</domain><bus>4</bus><slot>0</slot><function>1</function><product id='0xaa90'>Turks HDMI Audio [Radeon HD 6500/6600 / 6700M Series]</product><vendor id='0x1002'>Advanced Micro Devices, Inc. [AMD/ATI]</vendor><iommuGroup number='14'><address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/><address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/></iommuGroup><pci-express><link validity='cap' port='0' speed='5' width='16'/><link validity='sta' speed='5' width='16'/></pci-express></capability>
</device>
  1. 编写应用程序,访问vfio pci设备,参考demo
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/vfio.h>
#include <fcntl.h>void main (int argc, char *argv[]) {int container, group, device, i;struct vfio_group_status group_status ={ .argsz = sizeof(group_status) };struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };struct vfio_device_info device_info = { .argsz = sizeof(device_info) };int ret;/* Create a new container */container = open("/dev/vfio/vfio", O_RDWR);if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION) {/* Unknown API version */fprintf(stderr, "unknown api version\n");return;}if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) {/* Doesn't support the IOMMU driver we want. */fprintf(stderr, "doesn't support the IOMMU driver we want\n");return;}/* Open the group and get group fd* readlink /sys/bus/pci/devices/0000\:04\:00.0/iommu_group* */group = open("/dev/vfio/14", O_RDWR);if (group == -ENOENT) {fprintf(stderr, "group is not managed by VFIO driver\n");return;}/* Test the group is viable and available */ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status);if (ret) {fprintf(stderr, "cannot get VFIO group status, ""error %i (%s)\n", errno, strerror(errno));close(group);return;} else if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) {fprintf(stderr, "VFIO group is not viable! ""Not all devices in IOMMU group bound to VFIO or unbound\n");close(group);return;}if (!(group_status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {/* Add the group to the container */ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);if (ret) {fprintf(stderr,"cannot add VFIO group to container, error ""%i (%s)\n", errno, strerror(errno));close(group);return;}/* Enable the IOMMU model we want */ret = ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);if (!ret) {fprintf(stderr, "using IOMMU type 1\n");} else {fprintf(stderr, "failed to select IOMMU type\n");return;}}/* Get addition IOMMU info */ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);/* Allocate some space and setup a DMA mapping */dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);dma_map.size = 1024 * 1024;dma_map.iova = 0; /* 1MB starting at 0x0 from device view */dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);/* Get a file descriptor for the device */device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:04:00.0");/* Test and setup the device */ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);for (i = 0; i < device_info.num_regions; i++) {struct vfio_region_info reg = { .argsz = sizeof(reg) };reg.index = i;ioctl(device, VFIO_DEVICE_GET_REGION_INFO, &reg);/* Setup mappings... read/write offsets, mmaps* For PCI devices, config space is a region */}for (i = 0; i < device_info.num_irqs; i++) {struct vfio_irq_info irq = { .argsz = sizeof(irq) };irq.index = i;ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, &irq);/* Setup IRQs... eventfds, VFIO_DEVICE_SET_IRQS */}/* Gratuitous device reset and go... */ioctl(device, VFIO_DEVICE_RESET);ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);
}
  1. 编译测试
  • gdb调试demo程序
make
gdb demo
b main
r
  • 查看获取到的PCI设备信息
Breakpoint 1, main (argc=1, argv=0x7fffffffe478) at demo.c:14
14	    struct vfio_group_status group_status =
(gdb) bt
#0  main (argc=1, argv=0x7fffffffe478) at demo.c:14
(gdb) list
9	#include <linux/vfio.h>
10	#include <fcntl.h>
11	
12	void main (int argc, char *argv[]) {
13	    int container, group, device, i;
14	    struct vfio_group_status group_status =
15	                                    { .argsz = sizeof(group_status) };
16	    struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };
17	    struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };
18	    struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
(gdb) n
16	    struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };
(gdb) n
17	    struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };
(gdb) n
18	    struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
(gdb) n
22	    container = open("/dev/vfio/vfio", O_RDWR);
(gdb) n
24	    if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION) {
(gdb) n
30	    if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) {
(gdb) n
39	    group = open("/dev/vfio/14", O_RDWR);
(gdb) n
41	    if (group == -ENOENT) {
(gdb) p group
$1 = 8
(gdb) n
47	    ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status);
(gdb) n
48	    if (ret) {
(gdb) p ret
$2 = 0
(gdb) p group_status
$3 = {argsz = 8, flags = 1}
(gdb) n
53	    } else if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
(gdb) n
60	    if (!(group_status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {
(gdb) n
62	        ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
(gdb) p container
$4 = 7
(gdb) n
63	        if (ret) {
(gdb) p ret
$5 = 0
(gdb) n
72	        ret = ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
(gdb) n
73	        if (!ret) {
(gdb) n
74	            fprintf(stderr, "using IOMMU type 1\n");
(gdb) n
using IOMMU type 1
82	    ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
(gdb) n
85	    dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,
(gdb) p iommu_info
$6 = {argsz = 116, flags = 3, iova_pgsizes = 4096, cap_offset = 0}
(gdb) n
87	    dma_map.size = 1024 * 1024;
(gdb) n
88	    dma_map.iova = 0; /* 1MB starting at 0x0 from device view */
(gdb) n
89	    dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
(gdb) n
91	    ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
(gdb) n
94	    device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:04:00.0");
(gdb) n
97	    ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);
(gdb) p device
$7 = 9
(gdb) n
99	    for (i = 0; i < device_info.num_regions; i++) {
(gdb) p device_info
$8 = {argsz = 20, flags = 2, num_regions = 9, num_irqs = 5, cap_offset = 0}
  • 查看demo程序打开的文件
    请添加图片描述

Q&A

  1. 使用VFIO透传的设备,主机上必须开启IOMMU透传吗?
是的。IOMMU如果不透传,所有设备的DMA请求,都会通过内核配置来实现地址转换,用户进程无法决定映射关系。开启IOMMU透传后,IOMMU的DMA映射关系交给了用户程序,这样用户态程序(Qemu)才能通过接口VFIO_IOMMU_MAP_DMA灵活配置虚机做DMA的空间。

相关文章:

VFIO软件依赖——VFIO协议

文章目录背景PCI设备模拟PCI设备抽象VFIO协议实验Q&A背景 在虚拟化应用场景中&#xff0c;虚拟机想要在访问PCI设备时达到IO性能最优&#xff0c;最直接的方法就是将物理设备暴露给虚拟机&#xff0c;虚拟机对设备的访问不经过任何中间层的转换&#xff0c;没有虚拟化的损…...

C/C++【内存管理】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 Love is a choice. It is a conscious commitment. It is something you choose to make work every day with a person who has chosen the same thi…...

第8篇:Java编程语言的8大优势

目录 1、简单性 2、面向对象 3、编译解释性 4、稳健性 5、安全性 6、跨平台性...

STM32定时器实现红外接收与解码

1.NEC协议 红外遥控是一种比较常用的通讯方式&#xff0c;目前红外遥控的编码方式中&#xff0c;应用比较广泛的是NEC协议。NEC协议的特点如下&#xff1a; 载波频率为 38KHz8位地址和 8位指令长度地址和命令2次传输&#xff08;确保可靠性&#xff09;PWM 脉冲位置调制&#…...

18- Adaboost梯度提升树 (集成算法) (算法)

Adaboost 梯度提升树: from sklearn.ensemble import AdaBoostClassifier model AdaBoostClassifier(n_estimators500) model.fit(X_train,y_train) 1、Adaboost算法介绍 1.1、算法引出 AI 39年&#xff08;公元1995年&#xff09;&#xff0c;扁鹊成立了一家专治某疑难杂症…...

zlink 介绍

zlink 是一个基于 flink 开发的分布式数据开发工具&#xff0c;提供简单的易用的操作界面&#xff0c;降低用户学习 flink 的成本&#xff0c;缩短任务配置时间&#xff0c;避免配置过程中出现错误。用户可以通过拖拉拽的方式实现数据的实时同步&#xff0c;支持多数据源之间的…...

C++之std::string的resize与reverse

std::string的resize与reverse前言1.resize2.reserve前言 在C中我们经常用std::string 来保存字符串&#xff0c;其中有两个比较常用但是却平时容易被搞混的两个函数&#xff0c;分别是resize和reserve&#xff0c;模糊意识里&#xff0c;这两个方法都是对std::string的容量或元…...

在.net中运用ffmpeg 操作视频

using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Text;namespace learun.util{/// <summary>/// ffmpeg视频相关处理的类/// </summary>public class FFmpegUtil{public static int Run(string cmd){try{//…...

05- 线性回归算法 (LinearRegression) (算法)

线性回归算法(LinearRegression)就是假定一个数据集合预测值与实际值存在一定的误差, 然后假定所有的这些误差值符合正太分布, 通过方程求这个正太分布的最小均值和方差来还原原数据集合的斜率和截距。当误差值无限接近于0时, 预测值与实际值一致, 就变成了求误差的极小值。 fr…...

JAVA补充知识01之枚举enum

目录 1. 枚举类的使用 1.1 枚举类的理解 1.2 举例 1.3 开发中的建议&#xff1a; 1.4 Enum中的常用方法 1.5 熟悉Enum类中常用的方法 1.6 枚举类实现接口的操作 1.7 jdk5.0之前定义枚举类的方式 &#xff08;了解即可&#xff09; 1.8 jdk5.0之后定义枚举类的方式 1…...

jenkins下配置maven

1. 先在jenkins服务器上安装maven 下载-解压-重命名-启动 [rootVM-0-12-centos local]# wget https://mirrors.aliyun.com/apache/maven/maven-3/3.9.0/binaries/apache-maven-3.9.0-bin.tar.gz [rootVM-0-12-centos local]# tar xf apache-maven-3.9.0-bin.tar.gz [rootVM-0…...

春季开学即将到来!大学生活必备数码清单奉上

马上就要开学了&#xff0c;你的返校装备是否已经准备齐全了呢&#xff1f;对于高校学生来说&#xff0c;很多数码产品都属于必备装备&#xff0c;比如下面这几款产品就受到了大量年轻消费者的喜爱&#xff0c;在它们的帮助下能够让大家的学习时光变得更快乐。1、不入耳黑科技骨…...

ubuntu18.04 天选2 R95900hx 3060显卡驱动安装

天选2 R95900hx 3060显卡驱动安装需求问题解决内核集显显卡驱动需求 外接显示器&#xff0c;安装nvidia驱动 问题 由于一开始直接在软件和更新中附加读懂安装了nvidia-470&#xff0c;导致系统黑屏。 解决 grub页面系统选择进入ubuntu recovery模式&#xff0c;选择root&a…...

Harbor安装部署实战详细手册

文章目录前言一、安装docker二、安装docker-compose1.下载2.赋权3.测试三、安装harbor1.下载2.解压3.修改配置文件4.部署5.配置开机自启动6.登录验证7.补充说明四、harbor使用问题1.docker login问题&#xff1a;Error response from daemon: Get https://: http: server gave …...

华为OD机试真题JAVA实现【箱子之形摆放】真题+解题思路+代码(20222023)

🔥系列专栏 华为OD机试(JAVA)真题目录汇总华为OD机试(Python)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出描述示例一输入输出说明备注解题思路Code运行结果版权说明...

华为OD机试 - 事件推送(Python)| 真题+思路+考点+代码+岗位

事件推送 题目 同一个数轴 X 上有两个点的集合 A={A1, A2, …, Am} 和 B={B1, B2, …, Bn}, Ai 和 Bj 均为正整数,A、B 已经按照从小到大排好序,A、B 均不为空, 给定一个距离 R (正整数), 列出同时满足如下条件的所有(Ai, Bj)数对: Ai <= BjAi, Bj 之间的距离小于…...

【Linux】信号量

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…...

android-java同步方法和异步方法

接口 Java接口是一系列方法的声明&#xff0c;是一些方法特征的集合&#xff0c;一个接口只有方法的特征没有方法的实现&#xff0c;因此这些方法可以在不同的地方被不同的类实现&#xff0c;而这些实现可以具有不同的行为&#xff08;功能&#xff09;。 两种含义&#xff1a…...

Flask入门(5):请求和响应

目录5.请求和响应5.1 请求5.2 响应5.请求和响应 5.1 请求 request对象封装解析了请求报文中的数据&#xff0c;其大部分功能是由依赖包werkzeug完成的&#xff0c;并且每个request对象都是线程隔离的&#xff0c;保证了数据的安全性。 request对象的属性 1.request.method …...

记进组后第五次组会汇报

2023年2月14日 日记一、小组组会二、实验室组会1、汇报内容&#xff08;1&#xff09;参考文献&#xff08;2&#xff09;CQF机制a.研究现状b.相关思考&#xff08;3&#xff09;研究计划2、汇报反馈一、小组组会 上午十点整&#xff0c;小组组会开始&#xff0c;有两个同学我…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...