UEFI 图形编程:直接写 FrameBuffer 与 Blt 接口的对比

在 UEFI 环境下开发图形应用时,我们经常需要在屏幕上绘制图像、显示 Logo 或实现简单 UI。EFI_GRAPHICS_OUTPUT_PROTOCOL(简称 GOP)提供了两种最基本的绘图方式:直接操作 FrameBuffer 内存,或者调用 Blt(Block Transfer)接口。


一、背景:UEFI 的图形输出协议

UEFI(统一可扩展固件接口)中的 Graphics Output Protocol (GOP) 负责管理显示输出。通过该协议,我们可以获取当前显示模式(分辨率、像素格式)、设置新模式,并将像素数据写入屏幕。

GOP 提供了两种绘图途径:

  1. 直接映射 FrameBuffer

    通过 Gop->Mode->FrameBufferBase 获得显存基址,然后像操作普通内存一样向其中写入像素值。这种方式非常直接,但需要开发者自行处理像素格式、行对齐等底层细节。

  2. Blt 接口

    Gop->Blt() 是一个标准的块传输函数,支持填充、拷贝、内存 ↔ 显存传输等多种操作。它接受独立于硬件像素格式的 EFI_GRAPHICS_OUTPUT_BLT_PIXEL 结构体,由驱动完成格式转换。

为了直观对比,我们来看两个完整的 UEFI 应用程序:一个采用直接写 FrameBuffer,另一个使用 Blt 接口。两者功能相同------枚举显示模式、设置合适分辨率、绘制渐变背景和白色十字,最后清屏退出。


二、方式一:直接操作 FrameBuffer

这是最直接的方法,代码中直接向显存地址写入像素数据。

c 复制代码
UINT32 *FrameBuffer = (UINT32*)Gop->Mode->FrameBufferBase;
UINT32 PixelsPerScanLine = Gop->Mode->Info->PixelsPerScanLine;

for (y = 0; y < Vertical; y++) {
    for (x = 0; x < Horizontal; x++) {
        // 计算 RGB 分量
        UINT8 r = (UINT8)(x * 255 / Horizontal);
        UINT8 g = (UINT8)(y * 255 / Vertical);
        UINT8 b = 0x80;
        UINT32 color;

        // 手动处理像素字节序
        if (SwapRedBlue) {
            color = (b << 16) | (g << 8) | r;
        } else {
            color = (r << 16) | (g << 8) | b;
        }
        FrameBuffer[y * PixelsPerScanLine + x] = color;
    }
}

关键点

  • 像素格式依赖 :代码必须根据 Gop->Mode->Info->PixelFormat 自行决定 R、G、B 的排列顺序。示例中通过 SwapRedBlue 布尔值来区分 RGB 和 BGR,并手动组装 32 位颜色值。
  • 行步长 :PixelsPerScanLine 通常是显卡硬件对齐后的每行像素数,可能大于 Horizontal。直接使用 y * PixelsPerScanLine + x 才能正确定位到每个像素的地址。
  • 绘制十字和清屏 :同样使用 for 循环逐像素修改显存。

优点

  • 零额外内存开销:无需分配 CPU 侧的缓冲区,直接操作显存。
  • 灵活:可以实现任意逐像素算法(如抗锯齿、色彩变换等)。

缺点

  • 兼容性差 :若显卡采用自定义位掩码格式(PixelBitMask)或禁止直接访问(PixelBltOnly),该方式将彻底失效。
  • CPU 负担重:每次写入都跨越 PCIe 总线,对于全屏绘制,大量循环会导致明显的性能下降。
  • 代码复杂:必须谨慎处理字节序和对齐,不同显卡的行为可能存在差异。

三、方式二:使用 Blt 接口

方式二遵循 UEFI 规范推荐的标准化做法,将所有绘图操作封装为对 Gop->Blt() 的调用。

c 复制代码
// 1. 在系统内存中构建 BltBuffer
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
gBS->AllocatePool(EfiBootServicesData, 
                  Horizontal * Vertical * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
                  (VOID**)&BltBuffer);

for (y = 0; y < Vertical; y++) {
    for (x = 0; x < Horizontal; x++) {
        UINTN idx = (UINTN)y * Horizontal + x;
        BltBuffer[idx].Red   = (UINT8)(x * 255 / Horizontal);
        BltBuffer[idx].Green = (UINT8)(y * 255 / Vertical);
        BltBuffer[idx].Blue  = 0x80;
        BltBuffer[idx].Reserved = 0;
    }
}

// 2. 一次性 Blt 到屏幕
Gop->Blt(Gop, BltBuffer, EfiBltBufferToVideo,
         0, 0,        // SourceX, SourceY
         0, 0,        // DestX, DestY
         Horizontal, Vertical,
         0);          // Delta = 0,表示行宽 = Width * sizeof(PIXEL)

// 3. 用 EfiBltVideoFill 画白色十字
EFI_GRAPHICS_OUTPUT_BLT_PIXEL White = {0xFF, 0xFF, 0xFF, 0x00};
Gop->Blt(Gop, &White, EfiBltVideoFill, 0, 0, 0, cy, Horizontal, 1, 0);
Gop->Blt(Gop, &White, EfiBltVideoFill, 0, 0, cx, 0, 1, Vertical, 0);

关键点

  • 无需关心字节序 :直接填充 EFI_GRAPHICS_OUTPUT_BLT_PIXEL 的 Red、Green、Blue 字段,驱动会自动转换。
  • Delta 参数 :这里传入 0,表示源缓冲区没有额外填充;如果有偏移(SourceX/Y 非零),则需设置正确的行字节数。
  • 硬件加速指令 :EfiBltVideoFill 使用单个像素作为填充色,驱动会以最高效的方式(可能借助 GPU)填满指定矩形区域,比软件循环快得多。

优点

  • 高兼容性 :支持所有像素格式,包括 PixelBltOnly。
  • 代码简洁:不需要手动处理字节序和对齐,意图清晰。
  • 性能优异:批量传输和硬件填充减少了 CPU 与显存的交互次数。
  • 标准接口:符合 UEFI 规范,移植性更好。

缺点

  • 额外内存开销 :需要分配与全屏大小相当的 BltBuffer(例如 1080p 下约 8 MB),对于内存紧张的早期启动环境可能不友好。

四、全方位对比

对比维度 直接写 FrameBuffer Blt 接口
像素字节序处理 手动,易出错 自动转换,无需关心
行填充(Padding) 需使用 PixelsPerScanLine 手动计算 通过 Delta 参数由驱动处理
额外内存占用 无 需分配全屏大小的 BltBuffer
绘制效率(全屏) 多次 CPU 写操作,较慢 单次 DMA 传输,较快
硬件加速支持 无法利用 支持 EfiBltVideoFill 等填充操作
兼容 PixelBltOnly ❌ 不支持 ✅ 支持
代码维护性 逻辑混杂底层细节 抽象层次高,易于阅读

五、代码

方式1:

bash 复制代码
/**
 * @brief 显示系统启动时的 LOGO 图 方式1:直接写 FrameBuffer
 * 
 */
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/GraphicsOutput.h>
 
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
    EFI_STATUS                      Status;
    EFI_GRAPHICS_OUTPUT_PROTOCOL    *Gop;
    UINT32                          MaxMode;
    UINT32                          ModeNumber;
    UINT32                          SelectedMode = 0;
    UINTN                           SizeOfInfo;
    UINT32                          Horizontal = 0;
    UINT32                          Vertical = 0;
    UINTN                           Index;
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo = NULL;
    BOOLEAN                         SwapRedBlue = FALSE;

    // 1. 获取 GOP Protocol
    Status = gBS->LocateProtocol(
                    &gEfiGraphicsOutputProtocolGuid,
                    NULL,
                    (VOID**)&Gop
                    );
    if (EFI_ERROR(Status)) {
        Print(L"Failed to locate GOP\n");
        return Status;
    }
    Print(L"GOP protocol found successfully!\n");

    // 2. 枚举所有显示模式
    Print(L"=== Enumerating All Display Modes ===\n");
    MaxMode = Gop->Mode->MaxMode;
    Print(L"MaxMode = %d\n\n", MaxMode);
    
    for (ModeNumber = 0; ModeNumber < MaxMode; ModeNumber++) {
        Status = Gop->QueryMode(Gop, ModeNumber, &SizeOfInfo, &ModeInfo);
        if (EFI_ERROR(Status)) {
            Print(L"QueryMode failed for mode %d: %r\n", ModeNumber, Status);
            continue;
        }

        Print(L"Mode %2d: %4d x %4d, PixelFormat = %d\n",
            ModeNumber,
            ModeInfo->HorizontalResolution,
            ModeInfo->VerticalResolution,
            ModeInfo->PixelFormat
        );

        if (ModeInfo != NULL) {
            gBS->FreePool(ModeInfo);
            ModeInfo = NULL;
        }
    }

    // 暂停让用户查看枚举结果
    Print(L"\nPress Enter to continue...");
    SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
    while (TRUE) {
        SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Index);
        EFI_INPUT_KEY Key;
        if (!EFI_ERROR(SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &Key))) {
            if (Key.UnicodeChar == L'\r') {
                break;
            }
        }
    }

    // 3. 设置显示模式
    Status = Gop->SetMode(Gop, SelectedMode);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to set mode %d: %r\n", SelectedMode, Status);
        return Status;
    }
    Horizontal = Gop->Mode->Info->HorizontalResolution;
    Vertical = Gop->Mode->Info->VerticalResolution;
    Print(L"Selected mode %d with resolution %d x %d\n", SelectedMode, Horizontal, Vertical);
    Print(L"Mode %d set successfully!\n", SelectedMode);

    // 4. 当前显示模式信息
    Print(L"\n=== Current Mode Information ===\n");
    Print(L"Current Mode Number: %d\n", Gop->Mode->Mode);
    Print(L"FrameBuffer Base:   0x%016lx\n", Gop->Mode->FrameBufferBase);
    {
        UINT32 SizeMB = (UINT32)(Gop->Mode->FrameBufferSize / (1024 * 1024));
        UINT32 SizeKB = (UINT32)((Gop->Mode->FrameBufferSize % (1024 * 1024)) / 1024);
        Print(L"FrameBuffer Size:   %d bytes (%d.%02d MB)\n",
                Gop->Mode->FrameBufferSize,
                SizeMB,
                (SizeKB * 100) / 1024);
    }
    Print(L"Horizontal Resolution: %d\n", Gop->Mode->Info->HorizontalResolution);
    Print(L"Vertical Resolution:   %d\n", Gop->Mode->Info->VerticalResolution);
    Print(L"Pixel Format:          %d\n", Gop->Mode->Info->PixelFormat);

    // 解释像素格式
    switch (Gop->Mode->Info->PixelFormat) {
        case PixelRedGreenBlueReserved8BitPerColor:
            Print(L"  -> Pixel Format: RGB (8:8:8, with reserved byte)\n");
            break;
        case PixelBlueGreenRedReserved8BitPerColor:
            Print(L"  -> Pixel Format: BGR (most common on PC)\n");
            break;
        case PixelBitMask:
            Print(L"  -> Pixel Format: Custom bitmask\n");
            break;
        case PixelBltOnly:
            Print(L"  -> Pixel Format: Blt only (no direct framebuffer access)\n");
            break;
        default:
            Print(L"  -> Pixel Format: Unknown\n");
    }

    // 5. 根据像素格式确定颜色通道排列
    SwapRedBlue = (Gop->Mode->Info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor);

    // 4. 等待 Enter 键
    Print(L"Press Enter to draw...\n");
    SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
    while (TRUE) {
        SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Index);
        EFI_INPUT_KEY Key;
        if (!EFI_ERROR(SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &Key))) {
            if (Key.UnicodeChar == L'\r') {
                break;
            }
        }
    }

    // 5. 绘制渐变背景与中心十字
    if (Horizontal > 0 && Vertical > 0) {
        UINT32 *FrameBuffer = (UINT32*)Gop->Mode->FrameBufferBase;
        UINT32 PixelsPerScanLine = Gop->Mode->Info->PixelsPerScanLine;
        UINT32 x, y;
        UINT32 cx = Horizontal / 2;
        UINT32 cy = Vertical / 2;

        for (y = 0; y < Vertical; y++) {
            for (x = 0; x < Horizontal; x++) {
                UINT8 r = (UINT8)(x * 255 / Horizontal);
                UINT8 g = (UINT8)(y * 255 / Vertical);
                UINT8 b = 0x80;
                UINT32 color;

                if (SwapRedBlue) {
                    color = (b << 16) | (g << 8) | r;
                } else {
                    color = (r << 16) | (g << 8) | b;
                }
                FrameBuffer[y * PixelsPerScanLine + x] = color;
            }
        }

        for (x = 0; x < Horizontal; x++) {
            FrameBuffer[cy * PixelsPerScanLine + x] = 0x00FFFFFF;
        }
        for (y = 0; y < Vertical; y++) {
            FrameBuffer[y * PixelsPerScanLine + cx] = 0x00FFFFFF;
        }
    }

    // 6. 等待按键退出
    Print(L"Press any key to exit...\n");
    SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
    SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Index);

    // 7. 清屏为黑色
    if (Horizontal > 0 && Vertical > 0) {
        UINT32 *FrameBuffer = (UINT32*)Gop->Mode->FrameBufferBase;
        UINT32 PixelsPerScanLine = Gop->Mode->Info->PixelsPerScanLine;
        UINT32 x, y;
        for (y = 0; y < Vertical; y++) {
            for (x = 0; x < Horizontal; x++) {
                FrameBuffer[y * PixelsPerScanLine + x] = 0x00000000;
            }
        }
    }

    return EFI_SUCCESS;
}

方式2

bash 复制代码
/**
 * @brief 显示系统启动时的 LOGO 图 方式2:使用 Blt接口绘制
 * 
 */
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/GraphicsOutput.h>
 
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
    EFI_STATUS                      Status;
    EFI_GRAPHICS_OUTPUT_PROTOCOL    *Gop;
    UINT32                          MaxMode;
    UINT32                          ModeNumber;
    UINT32                          SelectedMode = 0;
    UINTN                           SizeOfInfo;
    UINT32                          Horizontal = 0;
    UINT32                          Vertical = 0;
    UINTN                           Index;
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo = NULL;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *BltBuffer = NULL;   // Blt 像素缓冲(B/G/R/A 结构体,无需管字节序)

    // 1. 获取 GOP Protocol
    Status = gBS->LocateProtocol(
                    &gEfiGraphicsOutputProtocolGuid,
                    NULL,
                    (VOID**)&Gop
                    );
    if (EFI_ERROR(Status)) {
        Print(L"Failed to locate GOP\n");
        return Status;
    }
    Print(L"GOP protocol found successfully!\n");

    // 2. 枚举所有显示模式
    Print(L"=== Enumerating All Display Modes ===\n");
    MaxMode = Gop->Mode->MaxMode;
    Print(L"MaxMode = %d\n\n", MaxMode);
    
    for (ModeNumber = 0; ModeNumber < MaxMode; ModeNumber++) {
        Status = Gop->QueryMode(Gop, ModeNumber, &SizeOfInfo, &ModeInfo);
        if (EFI_ERROR(Status)) {
            Print(L"QueryMode failed for mode %d: %r\n", ModeNumber, Status);
            continue;
        }

        Print(L"Mode %2d: %4d x %4d, PixelFormat = %d\n",
            ModeNumber,
            ModeInfo->HorizontalResolution,
            ModeInfo->VerticalResolution,
            ModeInfo->PixelFormat
        );

        if (ModeInfo != NULL) {
            gBS->FreePool(ModeInfo);
            ModeInfo = NULL;
        }
    }

    // 暂停让用户查看枚举结果
    Print(L"\nPress Enter to continue...");
    SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
    while (TRUE) {
        SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Index);
        EFI_INPUT_KEY Key;
        if (!EFI_ERROR(SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &Key))) {
            if (Key.UnicodeChar == L'\r') {
                break;
            }
        }
    }

    // 3. 设置显示模式
    Status = Gop->SetMode(Gop, SelectedMode);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to set mode %d: %r\n", SelectedMode, Status);
        return Status;
    }
    Horizontal = Gop->Mode->Info->HorizontalResolution;
    Vertical = Gop->Mode->Info->VerticalResolution;
    Print(L"Selected mode %d with resolution %d x %d\n", SelectedMode, Horizontal, Vertical);
    Print(L"Mode %d set successfully!\n", SelectedMode);

    // 4. 当前显示模式信息
    Print(L"\n=== Current Mode Information ===\n");
    Print(L"Current Mode Number: %d\n", Gop->Mode->Mode);
    Print(L"FrameBuffer Base:   0x%016lx\n", Gop->Mode->FrameBufferBase);
    {
        UINT32 SizeMB = (UINT32)(Gop->Mode->FrameBufferSize / (1024 * 1024));
        UINT32 SizeKB = (UINT32)((Gop->Mode->FrameBufferSize % (1024 * 1024)) / 1024);
        Print(L"FrameBuffer Size:   %d bytes (%d.%02d MB)\n",
                Gop->Mode->FrameBufferSize,
                SizeMB,
                (SizeKB * 100) / 1024);
    }
    Print(L"Horizontal Resolution: %d\n", Gop->Mode->Info->HorizontalResolution);
    Print(L"Vertical Resolution:   %d\n", Gop->Mode->Info->VerticalResolution);
    Print(L"Pixel Format:          %d\n", Gop->Mode->Info->PixelFormat);

    // 解释像素格式
    switch (Gop->Mode->Info->PixelFormat) {
        case PixelRedGreenBlueReserved8BitPerColor:
            Print(L"  -> Pixel Format: RGB (8:8:8, with reserved byte)\n");
            break;
        case PixelBlueGreenRedReserved8BitPerColor:
            Print(L"  -> Pixel Format: BGR (most common on PC)\n");
            break;
        case PixelBitMask:
            Print(L"  -> Pixel Format: Custom bitmask\n");
            break;
        case PixelBltOnly:
            Print(L"  -> Pixel Format: Blt only (no direct framebuffer access)\n");
            break;
        default:
            Print(L"  -> Pixel Format: Unknown\n");
    }

    // 等待 Enter 键后开始绘制
    Print(L"Press Enter to draw...\n");
    SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
    while (TRUE) {
        SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Index);
        EFI_INPUT_KEY Key;
        if (!EFI_ERROR(SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &Key))) {
            if (Key.UnicodeChar == L'\r') {
                break;
            }
        }
    }

    // 5. 使用 Blt 接口绘制渐变背景与中心十字
    if (Horizontal > 0 && Vertical > 0) {
        UINTN  BltBufSize = (UINTN)Horizontal * Vertical * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
        UINT32 x, y;
        UINT32 cx = Horizontal / 2;
        UINT32 cy = Vertical / 2;
        EFI_GRAPHICS_OUTPUT_BLT_PIXEL White = {0xFF, 0xFF, 0xFF, 0x00};

        // 5.1 在 CPU 内存中构建渐变 BltBuffer(结构体直接写 B/G/R,不关心硬件字节序)
        Status = gBS->AllocatePool(EfiBootServicesData, BltBufSize, (VOID**)&BltBuffer);
        if (EFI_ERROR(Status)) {
            Print(L"AllocatePool for BltBuffer failed: %r\n", Status);
        } else {
            for (y = 0; y < Vertical; y++) {
                for (x = 0; x < Horizontal; x++) {
                    UINTN idx = (UINTN)y * Horizontal + x;
                    BltBuffer[idx].Red      = (UINT8)(x * 255 / Horizontal);
                    BltBuffer[idx].Green    = (UINT8)(y * 255 / Vertical);
                    BltBuffer[idx].Blue     = 0x80;
                    BltBuffer[idx].Reserved = 0x00;
                }
            }
            // 5.2 把 BltBuffer 一次性 Blt 到屏幕 (0,0)
            Status = Gop->Blt(Gop, BltBuffer, EfiBltBufferToVideo,
                              0, 0,              // SourceX, SourceY
                              0, 0,              // DestX, DestY
                              Horizontal, Vertical,
                              0);                // Delta=0 -> 使用 Width*sizeof(PIXEL)
            if (EFI_ERROR(Status)) {
                Print(L"Blt EfiBltBufferToVideo failed: %r\n", Status);
            }
            gBS->FreePool(BltBuffer);
            BltBuffer = NULL;
        }

        // 5.3 用 EfiBltVideoFill 画白色中心十字(硬件加速,不需要构建缓冲)
        //    横线:从 (0, cy) 开始,宽=Horizontal,高=1
        Gop->Blt(Gop, &White, EfiBltVideoFill,
                 0, 0, 0, cy, Horizontal, 1, 0);
        //    竖线:从 (cx, 0) 开始,宽=1,高=Vertical
        Gop->Blt(Gop, &White, EfiBltVideoFill,
                 0, 0, cx, 0, 1, Vertical, 0);
    }

    // 6. 等待按键退出
    Print(L"Press any key to exit...\n");
    SystemTable->ConIn->Reset(SystemTable->ConIn, FALSE);
    SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &Index);

    // 7. 用 EfiBltVideoFill 清屏为黑色(1 次调用 = 整屏填充)
    if (Horizontal > 0 && Vertical > 0) {
        EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black = {0x00, 0x00, 0x00, 0x00};
        Gop->Blt(Gop, &Black, EfiBltVideoFill,
                 0, 0, 0, 0, Horizontal, Vertical, 0);
    }

    return EFI_SUCCESS;
}
.inf:
bash 复制代码
[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = ShowLogo
  FILE_GUID                      = 5B1A8D6E-7C3F-4E2A-9D01-3F1B2A4C5D6E
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]
  ShowLogo.c

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  UefiBootServicesTableLib

[Protocols]
  gEfiGraphicsOutputProtocolGuid
相关推荐
可乐鸡翅yeah_6 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
酣大智8 小时前
H3C MSR3600 交换机配置 SSH
网络·ssh·智能路由器
沐浴露z8 小时前
Agent 响应延迟过高?从工具治理角度提供两条思路
前端·网络
马六六i9 小时前
市面上正规的IP驱动产业新场景新工具有哪些
大数据·网络·python·tcp/ip
可乐ea11 小时前
多模型编排的三层:框架、模型路由与提供商路由
前端·网络·人工智能·ai智能体·多智能体协作·多模型编排·大模型路由
万联WANFLOW11 小时前
全球广告网络直通美国,TikTok抢占旺季全域流量新高地
网络·业界资讯
HackTwoHub11 小时前
Clown SRC skill第二代2.x最新版企业安全研究 Skill|证据优先的 SRC 与白盒审计工作流正式更新
网络·安全·web安全·网络安全·系统安全·密码学·安全架构
2601_9621773011 小时前
2026年AI API Gateway怎么选?我整理了6种方案的费用、稳定性和适用场景
网络·人工智能·深度学习·gateway
白帽攻防录11 小时前
SRC 挖洞:Certighost AD CS 域控制器伪造深度复盘,CVE-2026-54121 一个普通域用户怎么拿走整个企业域
网络·安全·web安全·网络安全·域安全
青春不朽51212 小时前
requests 超时、重试、SSL 报错?9 个高频问题与一整张排错对照表
网络·网络协议·ssl