VFIO软件依赖——VFIO协议

news2024/11/29 4:44:12

文章目录

  • 背景
  • 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的空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年小鹏新能源汽车核心部件解密

小鹏主要硬件清单(G9车型) 感知层 从硬件上看,G9搭载两颗NVIDIA DRIVE Orin 智能辅助驾驶芯片,算力达到 508 TOPS。此外,全车周边31 个感知元器件,(800万双目、4个300万侧前侧后、4个130万环视、1个170万后视、1个100万DMS)、12个超声波雷达、5个毫米波雷达、2个速…

TeamFiltration:一款针对O365 AAD账号安全的测试框架

关于TeamFiltration TeamFiltration是一款针对O365 AAD账号安全的跨平台安全测试框架&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松对O365 AAD账号进行枚举、喷射、过滤和后门植入等操作。TeamFiltering与CrackMapExec非常相似&#xff0c;它可以创建并维护一…

四大垃圾回收算法七大垃圾回收器

JVM的运行时内存也叫做JVM堆&#xff0c;从GC的角度可以将JVM分为新生代、老年代和永久代。其中新生代默认占1/3堆内存空间&#xff0c;老年代默认占2/3堆内存空间&#xff0c;永久代占非常少的对内存空间。新生代又分为Eden区、SurvivorFrom区和SurvivorTo区&#xff0c; Eden…

Python基础知识复习以及第三方库openxel的初步使用

目录文件python文件打开函数Python中的split函数详细解释List对象list添加元素的三种方法删除元素反转Python第三方库openxel的初步使用excel文件对象open操作excel入门文件 python文件打开函数 语法&#xff1a;open&#xff08;file, mode ‘r’, buffering-1, encodingNon…

【高性价比】初学者入门吉他值得推荐购买的民谣单板吉他品牌—VEAZEN费森吉他

“在未知的世界里&#xff0c;我们是一群不疲不倦的行者&#xff0c;执念于真善美&#xff0c;热衷于事物的极致。我们抽丝剥茧&#xff0c;不断地打败自己&#xff0c;超越自己&#xff0c;我们无所畏惧终将成为巨人。”这是VEAZEN吉他官网首页上很明显的一段话&#xff0c;也…

Learning C++ No.9【STL No.1】

引言&#xff1a; 北京时间&#xff1a;2023/2/13/18:29&#xff0c;开学正式上课第一天&#xff0c;直接上午一节思想政治&#xff0c;下午一节思想政治&#xff0c;生怕我们……&#xff0c;但&#xff0c;我深知该课的无聊&#xff0c;所以充分利用时间&#xff0c;把我的小…

MongoDB 索引

MongoDB 索引 索引通常能够极大的提高查询的效率&#xff0c;如果没有索引&#xff0c;MongoDB在读取数据时必须全集合扫描选取符合查询条件的记录。这种扫描全集合的查询效率是非常低的&#xff0c;特别在处理大量的数据时&#xff0c;查询可以要花费几十秒甚至几分钟&#x…

2023年城投债投资研究报告

第一章 概况 城投债又称“准市政债”&#xff0c;是地方投融资平台作为发行主体&#xff0c;公开发行企业债和中期票据&#xff0c;其主业多为地方基础设施建设或公益性项目。从承销商到投资者&#xff0c;参与债券发行环节的人&#xff0c;都将其视为是当地政府发债。伴随“打…

高压放大器在超声导波钢轨传播中的应用

实验名称&#xff1a;高压放大器在超声导波钢轨传播中的应用研究方向&#xff1a;无损检测测试目的&#xff1a;超声导波具有传播距离远、检测距离长的特点&#xff0c;在钢轨无损检测领域受到越来越多的关注。本文使用有限元仿真方法和现场实验方法&#xff0c;对钢轨各模态超…

四类(七种)排序算法总结

一、插入排序 基本思想&#xff1a; 每次将一个待排序的对象&#xff0c;按其关键码大小&#xff0c;插入到前面已经排好序的一组对象的适当位置上&#xff0c;直到对象全部插入为止。即边插入边排序&#xff0c;保证子序列中随时都是排好序的。 基本操作——有序插入&#xff…

搭建pclpy环境与读取pandaset数据并转换为pkl格式为pcd格式

1.搭建pclpy环境 问题&#xff1a;需要处理pcd文件&#xff0c;于是开始摸索搭建环境&#xff0c;有python-pcl&#xff0c;但是安装过程频频出现问题&#xff0c;于是转向pclpy。 参考链接&#xff1a;GitHub - davidcaron/pclpy: Python bindings for the Point Cloud Libr…

通达信量化接口怎么开发的?

通达信量化接口从广义上看&#xff0c;其实是面对股市有利的系统开发&#xff0c;通过智能化或者说程序化的交易体系直接构建了持仓下单的简单运作方式&#xff0c;对此&#xff0c;通达信量化接口又是如何开发出来的呢&#xff1f;一、函数的调用&#xff1a;相关函数调用结果…

Python语言零基础入门教程(十七)

Python 文件I/O 本章只讲述所有基本的 I/O 函数&#xff0c;更多函数请参考Python标准文档。 #### 打印到屏幕 最简单的输出方法是用print语句&#xff0c;你可以给它传递零个或多个用逗号隔开的表达式。此函数把你传递的表达式转换成一个字符串表达式&#xff0c;并将结果写…

企业分布式网络监控

IT 基础架构已成为提供基本业务服务的基石&#xff0c;无论是内部管理操作还是为客户托管的应用程序服务。监控IT基础设施至关重要&#xff0c;并且已经建立。SMB IT 基础架构需要简单的网络监控工具来监控性能和报告问题。通常&#xff0c;几个 IT 管理员配置网络设备、防火墙…

private static final Long serialVersionUID= 1L详解

我们知道在对数据进行传输时&#xff0c;需要将其进行序列化&#xff0c;在Java中实现序列化的方式也很简单&#xff0c;可以直接通过实现Serializable接口。但是我们经常也会看到下面接这一行代码&#xff0c;private static final Long serialVersionUID 1L&#xff1b;这段代…

实施工程师需要知道的常见linux命令

做为现场实施人员&#xff0c;操作linux服务器几乎是每天的必备工作&#xff0c;那么有哪些命令是工作中需要掌握的&#xff0c;下面会总结出来。 一、磁盘管理 1.1 查看当前目录 使用pwd命令&#xff0c;如下图所示&#xff0c;可以看到当前所处的目录是/root这个目录 1.2 进…

thymeleaf模板注入学习与研究--查找与防御

一、日常编码中常见的两种漏洞场景 1.1 模板参数外部可控 RequestMapping("/path")public String path(RequestParam String lang) {return lang ;}实际开发过程中 依靠我丰富的想象力只能想出 换主题 这种场景下可能会出现 大佬们自行脑补吧。 1.2 使用GetMappin…

【面试题】常见前端基础面试题(HTML,CSS,JS)

大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库html语义化的理解代码结构: 使页面在没有css的情况下,也能够呈现出好的内容结构有利于SEO: 爬虫根据标签来分配关键字的权重,因此可以和搜索引擎…

电压放大器在液滴微流控芯片的功能研究中的应用

实验名称&#xff1a;电压放大器在液滴微流控芯片的功能研究中的应用研究方向&#xff1a;微流控生物芯片测试目的&#xff1a;液滴微流控技术能够在微通道内实现液滴生成&#xff0c;精准控制生成液滴的尺寸以及生成频率。结合芯片结构设计和外部控制条件&#xff0c;可以对液…

内网渗透(二十六)之Windows协议认证和密码抓取-浏览器、数据库等其他密码的抓取

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…