OpenHarmony-3.HDF框架(2)

  • OpenHarmony HDF 平台驱动

1.平台驱动概述

  系统平台驱动框架是系统驱动框架的重要组成部分,它基于HDF驱动框架、操作系统适配层(OSAL, operating system abstraction layer)以及驱动配置管理机制,为各类平台设备驱动的实现提供标准模型。

  系统平台驱动(PlatformDriver),即平台设备驱动,它用于驱动平台设备(PlatformDevice),为系统及外设驱动提供访接口。这里的平台设备,泛指I2C/UART等总线、以及GPIO/RTC等SOC片内硬件资源。

  系统平台驱动框架为外设提供了标准的平台设备访问接口,使其不必关注具体硬件及OS平台;同时为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制。

  为实现这个目标,OpenHarmony系统平台驱动框架满足如下特性:

  • 统一的平台设备访问接口:对平台设备操作接口进行统一封装,屏蔽不同SOC平台硬件差异以及不同OS形态差异。
  • 统一的平台驱动适配接口:为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制,而不必关注设备管理及公共业务流程。
  • 提供设备注册、管理、访问控制等与SOC无关的公共能力。

2.平台驱动框架介绍

  OpenHarmony系统平台驱动框架主要由平台接口层、平台核心层以及平台适配层三个部分组成。
在这里插入图片描述

  • 平台接口层 以API的形式提供标准的平台设备访问接口。

    平台接口层以设备句柄加配套API的形式对外提供统一的、标准的访问接口。

    设备句柄是DevHandle类型的实例,通过不同设备模块提供的Open/Close方法进行获取、释放。成功获取设备句柄后,即可使用相应的API执行设备操作。例如通过I2cTransfer完成一次I2C数据传输。

    这是一种代理模式,即接口层API不直接引用实际设备对象,而是通过DevHandle作为代理,间接访问设备;而所有来自外设驱动的访问,都建议走接口层,以获得最佳的稳定性。

  • 平台核心层 提供平台设备模型及公共业务框架。
    提供统一适配接口:定义了标准的设备对象模型,驱动程序仅需关注标准对象模型的适配。

    抽取公共业务框架:将不同设备模块的公共流程、算法加以抽取,使得具体设备驱动更加轻薄。

    设备管理:设备注册、注销、设备查找、访问控制。

  • 平台适配层 提供特定平台设备的适配驱动,并遵守核心层约束。

    驱动具体平台设备硬件,并创建对应的设备模型对象,注册到核心层纳入统一管理。

2.1.平台接口层分析

  接口层用DevHandle类型的设备句柄表示一个平台设备对象,然后针对不同类型设备提供一套标准的API方法用于设备访问。那么设备句柄和真实的设备对象如何关联呢?

 drivers_hdf_core/framework/include/platform/platform_if.h:
 39 /**
 40  * @brief Defines the common device handle of the platform driver.
 41  *
 42  * The handle is associated with a specific platform device and is used as the
 43  * first input parameter for all APIs of the platform driver.
 44  *
 45  * @since 1.0
 46  */
 47 typedef void* DevHandle;

  在内核态,这个指针可以直接指向实际设备对象,但是对于某些类型的平台设备,需要在用户态提供同样的DevHandle类型及配套API,而实际设备对象在内核空间,导致无法直接获取和使用内核空间的地址。

  解决办法:将平台设备对象实现为一个HDF设备服务,这样借助HDF DeviceManager的设备服务机制,可以在用户态、内核态同时获取到设备服务,而用户态同内核态通信的问题交由HDF DeviceManager处理。此时,DevHandle只需要关联到这个设备服务即可,而void*类型保证了足够的灵活性。

  根据DevHandle和设备对象关联方式的不同,接口层的设计有三种模式:

2.1.1.独立服务模式

  典型实践是UART模块。
在这里插入图片描述
  使用范围:在用户态和内核态同时提供API的设备类型,DevHandle同设备对象的关联方式为:

  • 用户态:关联到平台设备对应的设备服务;

  • 内核态:关联到实际平台设备对象或其设备服务(在内核态两者可互相转换)

  每一个设备对象,会独立发布一个设备服务,来处理外部访问,服务收到API的访问请求之后,通过提取请求参数,并调用实际设备对象的相应内部方法。

  优点:管理比较简单,因为它借助了HDF DeviceManager的服务管理能力;
  缺点:需要为每一个设备对象配置设备节点,以便发布设备服务。

2.1.2.统一服务模式

  典型实践是I2C模块。
在这里插入图片描述
  同一类型的设备对象可能会很多,例如I2C模块,可能同时有十几个控制器。如果采用独立服务的模式,每一个控制器,作为一个平台设备,为其创建一个设备服务,那么将会有十几个服务被创建,不光要配置很多设备节点,而且这些服务还会占用内存资源。

  因此为一类设备对象,创建一个平台设备管理器(PlatformManager)对象,并同时对外发布一个管理器服务,由这个管理器服务来统一处理外部访问。当用户需要打开某个设备时,先通过HDF DeviceManager获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备,并返回一个设备描述符,而这个描述符仍然可以由DevHandle类型表示。

2.1.3.无服务模式
在这里插入图片描述  用于不需要在用户态提供API的设备类型或者没有用户态、内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址。而PlatformManager的实现比较自由,它不需要实现设备服务,只需做好某种类型的设备管理即可,甚至在C语言中,由于无法进行OOP编程,很多模块直接将这个功能面向过程化了,使得没有一个具体的结构体与之对应。

2.2.平台核心层

  平台核心层的作用是承上启下,其主要内容包括:

  • 提供适配接口:为具体的平台设备驱动提供统一的适配接口

    平台驱动框架为不同设备类型,定义了标准的设备对象模型,具体设备驱动只需要关注标准设备对象的适配即可

  • 提供设备管理:提供设备的注册、注销、查找等功能、访问控制等能力

    核心层会提供一系列内部方法,用于设备的注册、注销,设备对象的查找、获取、释放,以及处理多线程访问。例如当向核心层注册一个I2C控制器对象时,使用I2cCntlrAdd;当希望获取一个I2C控制器对象时,通过I2cCntlrGet并指定控制器编号;当不再使用这个对象时,还需要通过I2cCntlrPut释放。这样做的好处是将每一个具体的操作步骤高度抽象化,减小同平台接口层及平台适配层的耦合面,便于业务解耦、演进。由于业务需求需要对I2cCntlr对象进行引用计数,那么只需要修改I2cCntlrGet/Put这对方法的实现即可,并不会影响平台接口层和平台适配层。

  • 公共业务实现:抽取公共的业务流程、算法
    凡是跟特定硬件无关的业务逻辑,都会被抽取到核心层,例如RTC时钟的时间格式转换算法,GPIO模块的线程中断实现等等。

2.3.平台适配层实现

  适配层提供具体平台硬件设备的驱动,按照核心层定义的模型创建设备对象,并完成对象的初始化(包括必要的成员变量初始化以及钩子方法挂接,以及相关的硬件初始化操作),最后使用核心层提供的注册方法将设备对象注册到核心层纳入统一管理。

3.OpenHarmony系统平台驱动适配

3.1.UART模块适配

  UART模块适配的核心环节,是UartHost对象的创建、初始化及注册。UART模块采用的是独立服务模式,要求每一个UartHost对象关联一个HDF设备服务。

1).device_info.hcs: 为每一个UART控制器配置一个HDF设备节点

 vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs:
 71             device_uart :: device {
 72                 device0 :: deviceNode {
 73                     policy = 2;
 74                     priority = 40;
 75                     permission = 0644;
 76                     moduleName = "HDF_PLATFORM_UART";
 77                     serviceName = "HDF_PLATFORM_UART_0";
 78                     deviceMatchAttr = "rockchip_rk3568_uart_0";
 79                 }
 80                 device1 :: deviceNode {
 81                     policy = 2;
 82                     permission = 0644;
 83                     priority = 40;
 84                     moduleName = "HDF_PLATFORM_UART";
 85                     serviceName = "HDF_PLATFORM_UART_1";
 86                     deviceMatchAttr = "rockchip_rk3568_uart_1";
 87                 }
 88                 device2 :: deviceNode {
 89                     policy = 2;
 90                     permission = 0644;
 91                     priority = 40;
 92                     moduleName = "HDF_PLATFORM_UART";
 93                     serviceName = "HDF_PLATFORM_UART_3";
 94                     deviceMatchAttr = "rockchip_rk3568_uart_3";
 95                 }
 96             }

说明:

  • policy大于等于1(如需对用户态可见为2,仅内核态可见为1);

  • moduleName需要与驱动Entry中moduleName 保持一致;

  • serviceName必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号;

  • deviceMatchAttr用于配置控制器私有数据,要与uart_config.hcs中对应控制器保持一致,如不需要则忽略。

2). uart_config.hcs:每一个UART控制器配置私有数据

  如果控制器需要配置一些私有数据,例如寄存器基地址,初始化波特率等等,可以在uart_config.hcs中配置,该文件将在产品配置目录的hdf.hcs中导入,具体路径可由产品自由配置。

 vendor/hihope/rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs:
  1 root {
  2     platform {
  3         uart_config {
  4             template uart_device {
  5                 serviceName = "";
  6                 match_attr = "";
  7                 driver_name = "ttyS";
  8                 num = 0;
  9             }
 10
 11             device_uart_0x0000 :: uart_device {
 12                 match_attr = "rockchip_rk3568_uart_0";
 13             }
 14             device_uart_0x0001 :: uart_device {
 15                 num = 1;
 16                 match_attr = "rockchip_rk3568_uart_1";
 17             }
 18             device_uart_0x0003 :: uart_device {
 19                 num = 3;
 20                 match_attr = "rockchip_rk3568_uart_3";
 21             }
 22         }
 23     }
 24 }
 
  hihope/rk3568/hdf_config/khdf/hdf.hcs:
  1 #include "device_info/device_info.hcs"
  2 #include "platform/adc_config_linux.hcs"
  3 #include "platform/pwm_config.hcs"
  4 #include "platform/rk3568_watchdog_config.hcs"
  5 #include "platform/rk3568_uart_config.hcs"
  6 #include "platform/sdio_config.hcs"
  7 #include "platform/emmc_config.hcs"
  8 #include "platform/rk3568_spi_config.hcs"
  9 #include "input/input_config.hcs"
 10 #include "wifi/wlan_platform.hcs"
 11 #include "wifi/wlan_chip_ap6275s.hcs"
 12 #include "camera/camera_config.hcs"
 13 #include "sensor/sensor_config.hcs"
 14 #include "audio/audio_config.hcs"
 15 #include "audio/codec_config.hcs"
 16 #include "audio/dai_config.hcs"
 17 #include "audio/dma_config.hcs"
 18 #include "audio/dsp_config.hcs"
 19 #include "audio/analog_headset_config.hcs"
 20 #include "light/light_config.hcs"
 21 #include "vibrator/vibrator_config.hcs"
 22 #include "vibrator/linear_vibrator_config.hcs"
 23 #include "vibrator/drv2605l_linear_vibrator_config.hcs"
 24 #include "lcd/lcd_config.hcs"

3).驱动的Entry结构

先执行Bind方法绑定服务,后执行Init 初始化。

drivers_hdf_core\framework\sample\platform\uart\src\uart_sample.c
struct HdfDriverEntry g_sampleUartDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "UART_SAMPLE",
    .Bind = SampleUartDriverBind,
    .Init = SampleUartDriverInit,
    .Release = SampleUartDriverRelease,
};

// Initialize HdfDriverEntry
HDF_INIT(g_sampleUartDriverEntry);
  • Bind方法调用UartHostCreate创建 UartHost 控制器对象并完成服务绑定。
drivers_hdf_coreframework\support\platform\include\uart\uart_core.h:
struct UartHost {
    struct IDeviceIoService service;
    struct HdfDeviceObject *device;
    uint32_t num;
    OsalAtomic atom;
    void *priv;
    struct UartHostMethod *method;
};

drivers_hdf_core\framework\sample\platform\uart\src\uart_sample.c
static int32_t SampleUartDriverBind(struct HdfDeviceObject *device)
{
    struct UartHost *uartHost = NULL;
    HDF_LOGD("%s: Enter", __func__);

    uartHost = UartHostCreate(device);
    uartHost->service.Dispatch = SampleDispatch;
    return HDF_SUCCESS;
}

drivers_hdf_core/framework/support/platform/src/uart/uart_core.c
 79 struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
 80 {
 81     struct UartHost *host = NULL;
 82
 88     host = (struct UartHost *)OsalMemCalloc(sizeof(*host));
 93
 94     host->device = device;
 95     device->service = &(host->service);
 96     host->device->service->Dispatch = UartIoDispatch;
 97     OsalAtomicSet(&host->atom, 0);
 98     host->priv = NULL;
 99     host->method = NULL;
100     return host;
101 }

  该方法中将UartHost对象同HdfDeviceObject进行关联:

  • 为HdfDeviceObject的service成员进行赋值,使其指向UartHost的IDeviceIoService类型的成员对象;
  • 同时为service成员的Dispatch方法赋值。
    UartHostCreate函数设置host->device->service->Dispatch = UartIoDispatch; 最后SampleUartDriverBind函数使用uartHost->service.Dispatch = SampleDispatch; 进行覆盖UartIoDispatch。

  这样做的结果:

  • 为HdfDeviceObject对象绑定IDeviceIoService类型的服务对象

  • UartHost和其IDeviceIoService类型的成员对象service可以相互转换

  • 通过UartHost对象即可获取HdfDeviceObject对象

  • 通过HdfDeviceObject对象即可间接获取UartHost对象(先获取service再转为host)

4). Init方法

  完成UartHost对象的初始化。代码调用流程:

SampleUartDriverInit
	->AttachUartDevice
		->AddUartDevice
			->AddRemoveUartDev
				->register_driver
	->host->method = &g_sampleUartHostMethod;

  代码分析:

drivers_hdf_core\framework\sample\platform\uart\src\uart_sample.c
static int32_t SampleUartDriverInit(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct UartHost *host = NULL;
    HDF_LOGD("%s: Enter", __func__);

    host = UartHostFromDevice(device);
    ret = AttachUartDevice(host, device);
    host->method = &g_sampleUartHostMethod;
    return ret;
}

struct UartHostMethod g_sampleUartHostMethod = {
    .Init = SampleUartHostInit,
    .Deinit = SampleUartHostDeinit,
    .Read = NULL,
    .Write = SampleUartHostWrite,
    .SetBaud = SampleUartHostSetBaud,
    .GetBaud = SampleUartHostGetBaud,
    .SetAttribute = NULL,
    .GetAttribute = NULL,
    .SetTransMode = NULL,
};

  通过UartHostFromDevice从HdfDeviceObject对象获取之前关联的UartHost对象,然后调用 AttachUartDevice方法完成host对象的初始化,最后为host对象挂接钩子方法g_sampleUartHostMethod。

  小结:

  UART适配关键是在驱动Entry的Bind方法中创建UartHost对象,而且是使用UartHostCreate创建。这个创建动作同时也是注册的动作,因为它将UartHost以HDF设备服务的形式同HdfDeviceObject进行绑定,这样就完成了服务的发布,HDF Manager对设备服务的管理也就是对UartHost的管理,核心层可以通过HDF提供的服务获取接口来访问UartHost。

  UART适配采用独立服务模式,每一个UartHost对象同时也是一个设备服务,其优点是可以直接利用HDF Manager进行管理;缺点是需要在device_info.hcs为每一个UartHost对象定义设备节点。

3.2.UART模块主要接口

  • drivers/hdf_core/framework/include/platform/uart_if.h
DevHandle UartOpen(uint32_t port)	UART获取设备句柄
void UartClose(DevHandle handle)	UART释放设备句柄
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size)	从UART设备中读取指定长度的数据
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size)	向UART设备中写入指定长度的数据
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate)	UART获取波特率
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate)	UART设置波特率
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute)	UART获取设备属性
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute)	UART设置设备属性
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode)	UART设置传输模式

本文涉及的UART所有接口,支持内核态及用户态使用。

3.2.1.UartOpen

UartOpen
	->UartGetObjGetByBusNum(port);
		-> DevSvcManagerClntGetService(name)  
	-> UartHostRequest((struct UartHost *)handle);
  • UartGetObjGetByBusNum 调用DevSvcManagerClntGetService,根据name=“HDF_PLATFORM_UART_port” (framework/sample/config/device_info/device_info.hcs) 获取到handle 即host。
  • UartHostRequest 调用host->method->Init,即UartHostDevInit(uart_asr.c)

3.2.2.UartWrite

int32_t UartWrite(struct DevHandle *handle, uint8_t *data, uint32_t size)
{
    int ret;
    struct HdfIoService *service = NULL;

    struct HdfSBuf *sBuf = HdfSbufObtainDefaultSize();

    if (!HdfSbufWriteBuffer(sBuf, data, size)) {
        HDF_LOGE("Failed to write sbuf");
        HdfSbufRecycle(sBuf);
        return HDF_FAILURE;
    }

    service = (struct HdfIoService *)handle->object;
    ret = service->dispatcher->Dispatch(&service->object, UART_WRITE, sBuf, NULL);

    HdfSbufRecycle(sBuf);
    return ret;
}
  • 调用Dispatch接口进行写操作,即SampleDispatch,通过传输cmd 为UART_WRITE调用SampleDispatchWrite,然后调用UartPl011Write调用底层代码进行传输数据。
drivers_hdf_core\framework\sample\platform\uart\src\uart_dispatch_sample.c
int32_t SampleDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    int32_t result = HDF_FAILURE;
    (void)reply;

    struct UartHost *uartHost = (struct UartHost *)client->device->service;

    struct UartDevice *uartDevice = (struct UartDevice *)uartHost->priv;
 
    switch (cmdId) {
        case UART_WRITE: {
            result = SampleDispatchWrite(uartDevice, data);
            break;
        }
        default:
            break;
    }
    return result;
}

static int32_t SampleDispatchWrite(struct UartDevice *device, struct HdfSBuf *txBuf)
{
    uint32_t idx;
    uint32_t dataSize = 0;
    const uint8_t *data = NULL;
    struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase;

    if (!HdfSbufReadBuffer(txBuf, (const void **)&data, &dataSize)) {
        HDF_LOGE("%s: Failed to read sbuf", __func__);
        return HDF_FAILURE;
    }
    regMap = (struct UartRegisterMap *)device->resource.physBase;
    for (idx = 0; idx < dataSize; idx++) {
        UartPl011Write(regMap, data[idx]);
    }
    return HDF_SUCCESS;
}

drivers_hdf_core\framework\sample\platform\uart\include\uart_pl011_sample.h
static inline void UartPl011Write(struct UartRegisterMap *regMap, uint8_t byte)
{
    while (UartPl011IsBusy(regMap)) {}
    regMap->dr = byte;
}

3.3.用户层测试代码

  • 通过/dev/uartdev-%d 节点访问
drivers_hdf_core\framework\sample\platform\uart\dev\hello_uart_dev.c
int main(void)
{
    int ret;
    int fd;
    const char info[INFO_SIZE] = {" HELLO UART! "};

    fd = open("/dev/uartdev-5", O_RDWR);
    if (fd < 0) {
        HDF_LOGE("uartdev-5 open failed %d", fd);
        return -1;
    }
    ret = write(fd, info, INFO_SIZE);
    if (ret != 0) {
        HDF_LOGE("write uartdev-5 ret is %d", ret);
    }
    ret = close(fd);
    if (ret != 0) {
        HDF_LOGE("uartdev-5 close failed %d", fd);
        return -1;
    }
    return ret;
}
  • 使用UartOpen 接口进行方法
drivers_hdf_core\framework\sample\platform\uart\dispatch\hello_uart_dispatch.c
int main(void)
{
    const char *info = " HELLO UART! ";

    struct DevHandle *handle = UartOpen(UART_PORT);
    int ret = UartWrite(handle, (uint8_t *)info, strlen(info));
    UartClose(handle);
    return ret;
}

refer to

  • git clone https://gitee.com/openharmony/drivers_hdf_core.git
  • https://blog.csdn.net/HarmonyOS_666/article/details/140824175
  • https://blog.csdn.net/maniuT/article/details/141064333
  • https://blog.csdn.net/zxc95279527q/article/details/143062695

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端 el-table-column 里加上el-form-item 上面有黑色悬浮

如图所示 解决方法&#xff0c;查看你的 el-table-column 是否设置了 show-overflow-tooltip 属性&#xff0c;如果是去掉即可

使用MATLAB从Excel文件读取数据并绘制堆叠柱状图

在数据可视化中&#xff0c;堆叠柱状图是展示多个变量相对比例的非常有效的方法。它通过将每个数据系列堆叠在一起&#xff0c;帮助我们理解不同数据类别在总量中所占的份额。在这篇博客中&#xff0c;我们将学习如何使用MATLAB从Excel文件导入数据&#xff0c;并使用渐变色来绘…

书生浦语第四期--入门岛-第三关

文章目录 1.破冰任务&#xff1a;自我介绍1.1 create new fork1.2 git clone 自己的分支1.3 创建关联分支1.4提交修改 任务2 &#xff1a;创建自己的仓库 1.破冰任务&#xff1a;自我介绍 1.1 create new fork 创建一个自己的分支&#xff0c;在自己分支上进行修改&#xff0…

【AI系统】轻量级CNN模型新进展

CNN 模型小型化&#xff08;下&#xff09; 在本文会接着介绍 CNN 模型的小型化&#xff0c;除了第二篇文章提到的三个模型外&#xff0c;在本章节会继续介绍 ESPNet 系列&#xff0c;FBNet 系列&#xff0c;EfficientNet 系列和 GhostNet 系列。 ESPNet 系列 ESPNetV1 ESP…

鸿蒙HarmonyOS状态管理组件吐槽

吐槽一下鸿蒙系统设计的状态管理组件 一. 定义和作用 状态管理组件其本质作用用来修饰状态变量&#xff0c;这样可以观察到变量在组件内的改变&#xff0c;还可以在不同组件层级间传递&#xff0c;其设计初衷挺好。在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&a…

unicloud微信小程序云端一体项目DEMO

最近应客户需求&#xff0c;做了一个产品展示的云开发小程序&#xff0c;从了解云开发到应用到实际项目的产品demo&#xff0c;希望大家能从中获取到对自己有用的东西。 说下心得体会吧&#xff0c;一般小项目用这种云开发确实会减少很多开发成本&#xff0c;人力成本&#xf…

爬虫专栏第一篇:深入探索爬虫世界:基础原理、类型特点与规范要点全解析

本专栏会对爬虫进行从0开始的讲解&#xff0c;每一步都十分的细致&#xff0c;如果你感兴趣希望多多点赞收藏关注支持 简介&#xff1a;文章对爬虫展开多方面剖析。起始于爬虫的基本概念&#xff0c;即依特定规则在网络抓取信息的程序或脚本&#xff0c;在搜索引擎信息提取上作…

【Spring】Spring 整合 JUnit

JUnit 是 Java 中一个广泛使用的单元测试框架。它使用简单的注解和断言方法&#xff0c;使开发者能够轻松编写和运行测试用例。在使用 IDEA 创建的 Spring 项目中&#xff0c;JUnit 框架可以方便地进行整合。下面是整合的具体步骤。这里使用一个之前整合 MyBatis 时的 Spring 项…

MATLAB数学建模之画图汇总

MATLAB是一种强大的数学软件&#xff0c;广泛应用于工程计算、控制设计、信号处理等领域。在数学建模中&#xff0c;MATLAB的绘图功能可以帮助我们直观地展示数据和模型结果。 1. 二维数据曲线图 1.1 绘制二维曲线的基本函数 plot函数用于绘制二维平面上的线性坐标曲线图&am…

Linux 远程连接服务

远程连接服务器简介 什么是远程连接服务器 远程连接服务器通过文字或图形接口方式来远程登录系统&#xff0c;让你在远程终端前登录linux主机以取得可操 作主机接口&#xff08;shell&#xff09;&#xff0c;而登录后的操作感觉就像是坐在系统前面一样。 远程连接服务器的功…

Prometheus 采集postgresql监控数据

postgres_exporter 前言 postgres_exporter 是一个用于监控 PostgreSQL 数据库的 Prometheus 导出器。它允许你收集有关 PostgreSQL 数据库性能和状态的指标,并将这些指标暴露给 Prometheus,从而可以在 Grafana 等可视化工具中进行展示和告警。 postgres_exporter download…

windows文件下换行, linux上不换行 解决CR换行符替换为LF notepad++

html文件是用回车换行的&#xff0c;在windows电脑上&#xff0c;显示正常。 文件上传到linux服务器后&#xff0c;文件不换行了。只有一行。而且相关js插件也没法正常运行。 用notepad查看&#xff0c;显示尾部换行符&#xff0c;是CR&#xff0c;这就是原因。CR是不被识别的。…

CTF show 文件上传篇(web151-170,看这一篇就够啦)

目录 一.前言 二.文件上传&#xff08;web151-170&#xff09; 1.web151&#xff08;前端绕过&#xff09; 1.1 编写一句话木马 1.2.寻找突破点 1.3.webshell连接 1.4 利用条件 2.web152&#xff08;MIME头绕过&#xff09; 2.1 知识点 2.2 绕过限制 2.3 利用条件 …

UE5 像素流进行内网https证书创建

确定证书需求 内网 HTTPS 通信通常需要以下内容&#xff1a; 自签名证书&#xff08;适用于内网环境&#xff0c;不需要通过公开的证书颁发机构 CA&#xff09; 或者通过内部的企业 CA 签发的证书&#xff08;更安全&#xff09;。 生成自签名证书 使用工具&#xff08;如 Ope…

Android 图形系统之七:SurfaceFlinger

一. 引言 什么是 SurfaceFlinger&#xff1f;SurfaceFlinger 的核心作用和地位&#xff1f;为什么需要了解 SurfaceFlinger&#xff1f; 二. SurfaceFlinger 的基本概念 Surface 和 SurfaceFlinger 的关系SurfaceFlinger 与图形渲染&#xff08;OpenGL ES 和 Vulkan&#xf…

大型制造企业IT蓝图、信息化系统技术架构规划与实施路线方案

关注 获取ppt​​​​​​全文&#xff0c;请关注作者

基于Pyhton的人脸识别(Python 3.12+face_recognition库)

使用Python进行人脸编码和比较 简介 在这个教程中&#xff0c;我们将学习如何使用Python和face_recognition库来加载图像、提取人脸编码&#xff0c;并比较两个人脸是否相似。face_recognition库是一个强大的工具&#xff0c;它基于dlib的深度学习模型&#xff0c;可以轻松实…

基于Unity实现的目标追踪任务指示模块

一、环境布置 第一人称模式&#xff1a; 二、新建脚本命名为任务指示器 Arrow:箭头图像target:指示目标8/9行通过lambad表达式&#xff0c;方便获取它们的对应属性静态的rect对象作用是简化代码 13&#xff1a;判断是否存在对象&#xff0c;防止空引用报错14&#xff1a;使用…

etcd资源超额

集群内apiserver一直重启&#xff0c;重启kubelet服务后查看日志发现一下报错&#xff1a; Error from server: etcdserver: mvcc: database space exceeded 报错原因&#xff1a; etcd服务未设置自动压缩参数&#xff08;auto-compact&#xff09; etcd 默认不会自动 compa…

关于项目二次开发那点事儿

以SpirngbootVue项目为例&#xff1a; 目前项目整体结构&#xff1a; 前端编译基本操作&#xff1a; # 安装依赖 npm install# 在 localhost:8080 启动项目 npm run serve# 运行项目中的构建脚本 npm run build npm run build 该命令执行成功之后&#xff0c;前端项目的目录下…