AMD KFD userptr内存分配技术分析

1. 概述

本文档详细分析AMD GPU驱动中KFD (Kernel Fusion Driver) 子系统对用户态指针 (userptr) 内存的处理机制。当应用程序通过 KFD_IOC_ALLOC_MEM_FLAGS_USERPTR 标志分配内存时,驱动需要将用户空间已分配的内存映射到GPU地址空间,使GPU能够直接访问用户态内存。

1.1 核心函数

  • 入口函数: kfd_ioctl_alloc_memory_of_gpu() (kfd_chardev.c)
  • 核心实现: amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu() (amdgpu_amdkfd_gpuvm.c)
  • 页面初始化: init_user_pages() (amdgpu_amdkfd_gpuvm.c)

2. 用户态接口处理流程

文章:AMD rocr-libhsakmt分析系列6-1: userptr的register实现机制详解(上篇)分析了libhsakmt中在svm的支持下如何使用userptr,本文是KFD内核的实现分析,由于对内核的理解不是很深入,如果有错误,请读友们拍砖。

2.1 IOCTL 入口处理

kfd_ioctl_alloc_memory_of_gpu() 函数中,当检测到 KFD_IOC_ALLOC_MEM_FLAGS_USERPTR 标志时:

if (flags & KFD_IOC_ALLOC_MEM_FLAGS_USERPTR) {
    /* 检查 userptr 是否对应于其他设备的本地内存 */
    vma = find_vma(current->mm, args->mmap_offset);
    if (vma && (vma->vm_flags & VM_IO)) {
        unsigned long pfn;
        
        follow_pfn(vma, args->mmap_offset, &pfn);
        /* 转换为 doorbell 类型 */
        flags |= KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL;
        flags &= ~KFD_IOC_ALLOC_MEM_FLAGS_USERPTR;
        offset = (pfn << PAGE_SHIFT);
    } else {
        /* 验证地址对齐 */
        if (offset & (PAGE_SIZE - 1)) {
            pr_debug("Unaligned userptr address:%llx\n", offset);
            return -EINVAL;
        }
        cpuva = offset;
    }
}

关键点:

  1. 设备内存检测: 通过 find_vma() 检查用户地址是否映射到IO内存区域(VM_IO标志)
  2. 特殊处理: 如果是设备本地内存,转换为doorbell类型,获取物理页帧号(PFN)
  3. 地址验证: 普通userptr必须页对齐(PAGE_SIZE),否则返回 -EINVAL
  4. CPU虚拟地址保存: 将用户提供的地址保存到 cpuva 变量

3. GPU内存对象分配

3.1 域和标志设置

amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu() 中:

else if (flags & ALLOC_MEM_FLAGS_USERPTR) {
    domain = AMDGPU_GEM_DOMAIN_GTT;          // GPU访问域设为GTT
    alloc_domain = AMDGPU_GEM_DOMAIN_CPU;    // 实际分配域为CPU
    
    //userptr 放在offset变量中,见libhsakmt中的分析
    if (!offset || !*offset)
        return -EINVAL;
    user_addr = untagged_addr(*offset);       // 去除地址标签位
}

技术要点:

  • 双域设计:
    • domain: GTT域,表示GPU通过GTT (Graphics Translation Table) 访问
    • alloc_domain: CPU域,表示内存实际在系统RAM中
  • 地址处理: untagged_addr() 移除ARMv8.5-A的MTE (Memory Tagging Extension) 标签位

3.2 BO (Buffer Object) 创建

memset(&bp, 0, sizeof(bp));
bp.size = size;
bp.byte_align = 1;
bp.domain = alloc_domain; // CPU域
bp.flags = alloc_flags;
bp.type = bo_type; // ttm_bo_type_device
bp.resv = NULL;

ret = amdgpu_bo_create(adev, &bp, &bo);

if (user_addr)
    bo->flags |= AMDGPU_AMDKFD_USERPTR_BO;

创建的BO特性:

  • 分配在系统内存: 通过 AMDGPU_GEM_DOMAIN_CPU
  • 无sg表: 对于userptr,sg字段稍后由HMM (Heterogeneous Memory Management) 处理
  • BO标记: 设置 AMDGPU_AMDKFD_USERPTR_BO 标志

3.3 内存对象结构初始化

kgd_mem就是KFD驱动的bo,在amdgpu_bo的基础上添加了一些AI多卡应用需要的信息。该类型的理解请参考专栏:AMD KFD BO设计深度剖析——解锁GPU存储核心技术

*mem = kzalloc(sizeof(struct kgd_mem), GFP_KERNEL);
INIT_LIST_HEAD(&(*mem)->bo_va_list);
mutex_init(&(*mem)->lock);
(*mem)->alloc_flags = flags;
(*mem)->va = va;
(*mem)->domain = domain;              // GTT域
(*mem)->mapped_to_gpu_memory = 0;
(*mem)->process_info = avm->process_info;

3.4 添加到BO列表

add_kgd_mem_to_kfd_bo_list(*mem, avm->process_info, user_addr);
static void add_kgd_mem_to_kfd_bo_list(struct kgd_mem *mem,
                struct amdkfd_process_info *process_info,
                bool userptr)
{
    struct ttm_validate_buffer *entry = &mem->validate_list;
    struct amdgpu_bo *bo = mem->bo;

    INIT_LIST_HEAD(&entry->head);
    entry->num_shared = 1;
    entry->bo = &bo->tbo;
    mutex_lock(&process_info->lock);
    if (userptr)
        list_add_tail(&entry->head, &process_info->userptr_valid_list);
    else
        list_add_tail(&entry->head, &process_info->kfd_bo_list);
    mutex_unlock(&process_info->lock);
}

关键设计:

  • 分离管理: userptr BOs添加到 userptr_valid_list,普通BOs添加到 kfd_bo_list
  • 验证缓冲区: 通过 ttm_validate_buffer 结构管理,用于批量预留和验证

4. 用户页面初始化 (核心机制)

4.1 初始化流程概述

init_user_pages() 函数是userptr机制的核心,完成四个关键步骤:

步骤函数作用关键操作
1amdgpu_ttm_tt_set_userptr()保存用户地址到TTM设置 gtt->userptr,引用 usertask
2amdgpu_mn_register()注册MMU通知器插入interval tree,监控地址范围
3amdgpu_ttm_tt_get_user_pages()获取用户页面HMM fault,转换PFN到page数组
4ttm_bo_validate()验证BO在GTT域验证,建立GPU映射

4.2 关键代码段

步骤1: TTM Userptr设置

int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr, uint32_t flags)
{
    struct amdgpu_ttm_tt *gtt = (void *)ttm;
    gtt->userptr = addr;                      // 保存用户虚拟地址
    gtt->usertask = current->group_leader;    // 保存进程任务结构
    get_task_struct(gtt->usertask);           // 增加引用计数
    return 0;
}

步骤2: MMU Notifier注册

int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)
{
    // 关键点:使用区间树管理地址范围
    while ((it = interval_tree_iter_first(&amn->objects, addr, end))) {
        // 查找重叠区间并合并
        interval_tree_remove(&node->it, &amn->objects);
        addr = min(it->start, addr);
        end = max(it->last, end);
    }
    
    // 插入新的监控区间
    node->it.start = addr;
    node->it.last = end;
    interval_tree_insert(&node->it, &amn->objects);
    return 0;
}

关键技术:

  • 区间树: 使用红黑树interval tree高效管理地址范围,O(log n)复杂度
  • 地址合并: 自动合并重叠的监控区间,避免冗余
  • MMU回调: 当用户空间修改页表时(munmap、mprotect等),内核回调驱动

步骤3: HMM页面获取

int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo, struct page **pages)
{
    // 1. 验证VMA
    vma = find_vma(mm, start);
    if (unlikely(!vma || start < vma->vm_start))
        return -EFAULT;
    
    // 2. 设置HMM范围
    range->start = start;
    range->end = start + ttm->num_pages * PAGE_SIZE;
    range->default_flags = range->flags[HMM_PFN_VALID];
    range->default_flags |= is_readonly ? 0 : range->flags[HMM_PFN_WRITE];
    
    // 3. 注册并触发页面故障
    hmm_range_register(range, mirror);
    hmm_range_wait_until_valid(range, HMM_RANGE_DEFAULT_TIMEOUT);
    hmm_range_fault(range, 0);  // 确保页面在内存中
    
    // 4. 转换PFN为page结构
    for (i = 0; i < ttm->num_pages; i++)
        pages[i] = hmm_device_entry_to_page(range, pfns[i]);
    
    return 0;
}

HMM机制关键点:

  • HMM Range注册: 向HMM子系统注册监控范围
  • 页面故障处理: hmm_range_fault() 触发缺页异常,确保页面在物理内存中
  • PFN转换: 将物理页帧号(PFN)转换为内核 struct page* 指针
  • 读写权限: 根据BO的只读属性设置 HMM_PFN_WRITE 标志

步骤4: BO验证

amdgpu_bo_reserve(bo, true);                        // 预留BO
amdgpu_bo_placement_from_domain(bo, mem->domain);   // GTT域放置策略
ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);    // TTM验证
amdgpu_bo_unreserve(bo);                            // 释放预留

作用: 确保BO在GTT域验证成功,建立GPU可访问的内存映射


5. GPU映射处理

5.1 映射流程概述

amdgpu_amdkfd_gpuvm_map_memory_to_gpu() 负责将userptr BO映射到GPU虚拟地址空间。

步骤操作关键检查作用
1检测invalid状态atomic_read(&mem->invalid)判断userptr是否失效
2检查系统域bo->tbo.mem.mem_type == TTM_PL_SYSTEM确认BO是否已迁移到GTT
3建立VM映射map_bo_to_gpuvm()在GPU虚拟内存中建立映射
4更新页目录vm_update_pds()更新GPU页表结构

5.2 关键代码

Invalid检测:

// 使用mmap_sem保护,确保MMU notifier不会并发运行
if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm)) {
    down_write(&current->mm->mmap_sem);
    is_invalid_userptr = atomic_read(&mem->invalid);
    up_write(&current->mm->mmap_sem);
}

// Userptr在系统域视为invalid,延迟映射
if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) &&
    bo->tbo.mem.mem_type == TTM_PL_SYSTEM)
    is_invalid_userptr = true;

映射和同步:

map_bo_to_gpuvm(adev, entry, ctx.sync, is_invalid_userptr);  // 建立GPUVM映射
vm_update_pds(vm, ctx.sync);                                  // 更新页目录

技术要点:

  • 延迟映射: invalid或在系统域的userptr跳过映射,等待restore worker处理
  • mmap_sem保护: 确保与MMU notifier互斥,避免竞态
  • 同步机制: 通过 amdgpu_sync 确保GPU操作完成

6. 失效和恢复机制

6.1 失效触发流程

当用户空间修改内存映射时(munmap、mremap、mprotect等),触发MMU Notifier回调:

步骤操作函数/机制作用
1标记失效atomic_set(&mem->invalid, 1)设置失效标志
2移动列表userptr_valid_listuserptr_inval_list更新BO状态
3调度恢复schedule_delayed_work(restore_userptr_work)启动恢复工作队列

6.2 恢复工作队列

核心流程:

static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
{
    // 1. 获取进程引用
    usertask = get_pid_task(process_info->pid, PIDTYPE_PID);
    mm = get_task_mm(usertask);
    
    mutex_lock(&process_info->lock);
    
    // 2. 更新失效页面
    if (update_invalid_user_pages(process_info, mm))
        goto unlock_out;
    
    // 3. 验证失效页面
    if (!list_empty(&process_info->userptr_inval_list)) {
        if (validate_invalid_user_pages(process_info))
            goto unlock_out;
    }
    
    // 4. 原子更新并恢复
    if (atomic_cmpxchg(&process_info->evicted_bos, evicted_bos, 0) == evicted_bos)
        kgd2kfd_resume_mm(mm);
    
unlock_out:
    mutex_unlock(&process_info->lock);
    mmput(mm);
    put_task_struct(usertask);
    
    // 失败则重新调度
    if (evicted_bos)
        schedule_delayed_work(&process_info->restore_userptr_work,
            msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS));
}

6.3 更新失效页面

关键操作:

static int update_invalid_user_pages(struct amdkfd_process_info *process_info,
                                     struct mm_struct *mm)
{
    // 1. 迁移失效BO到CPU域
    list_for_each_entry_safe(mem, tmp_mem, &process_info->userptr_valid_list, ...) {
        if (!atomic_read(&mem->invalid))
            continue;
        
        amdgpu_bo_reserve(bo, true);
        amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU);
        ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
        amdgpu_bo_unreserve(bo);
        
        list_move_tail(&mem->validate_list.head, &process_info->userptr_inval_list);
    }
    
    // 2. 重新获取用户页面
    list_for_each_entry(mem, &process_info->userptr_inval_list, ...) {
        amdgpu_ttm_tt_get_user_pages(bo, bo->tbo.ttm->pages);
        amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm);
        atomic_cmpxchg(&mem->invalid, invalid, 0);  // 清除失效标志
    }
    
    return 0;
}

6.4 验证失效页面

核心流程:

static int validate_invalid_user_pages(struct amdkfd_process_info *process_info)
{
    // 1. 预留所有BO和页表
    ttm_eu_reserve_buffers(&ticket, &resv_list, false, &duplicates);
    
    // 2. 验证BO并更新GPUVM
    list_for_each_entry_safe(mem, tmp_mem, &process_info->userptr_inval_list, ...) {
        // 验证BO回GTT域
        if (bo->tbo.ttm->pages[0]) {
            amdgpu_bo_placement_from_domain(bo, mem->domain);
            ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
        }
        
        // 移回valid列表
        list_move_tail(&mem->validate_list.head, &process_info->userptr_valid_list);
        
        // 更新GPU页表
        list_for_each_entry(bo_va_entry, &mem->bo_va_list, bo_list) {
            if (bo_va_entry->is_mapped)
                update_gpuvm_pte(..., bo_va_entry, &sync);
        }
    }
    
    // 3. 更新页目录
    process_update_pds(process_info, &sync);
    
    ttm_eu_backoff_reservation(&ticket, &resv_list);
    return ret;
}

6.5 技术要点

机制实现方式优势
延迟恢复AMDGPU_USERPTR_RESTORE_DELAY_MS (1ms)聚合多次失效事件,减少恢复开销
原子操作atomic_cmpxchg(&mem->invalid, ...)确保并发安全,无锁编程
批量处理一次处理所有inval_list中的BOs减少锁竞争,提高效率
状态迁移CPU域 ← invalid ← GTT域完整的生命周期管理

7. 技术亮点总结

7.1 HMM (Heterogeneous Memory Management)

关键点实现机制优势
统一地址空间CPU和GPU共享相同的虚拟地址简化编程模型,无需地址转换
自动迁移页面在CPU/GPU之间自动迁移透明的数据移动,降低开发复杂度
透明性应用层无需显式数据传输提高开发效率,减少错误

7.2 MMU Notifier机制

关键点实现机制优势
实时同步用户空间内存变化实时通知驱动保证内存一致性
区间树管理使用红黑树interval tree管理地址范围O(log n)时间复杂度,高效查找
并发保护mmap_sem确保与页表修改的同步避免竞态条件

7.3 失效恢复机制

关键点实现机制优势
延迟恢复delayed_work聚合多次失效事件减少系统开销,提高效率
原子操作atomic_cmpxchg确保并发安全无锁编程,提高性能
批量处理一次恢复所有失效的userptr BOs减少锁竞争,提高吞吐量

7.4 双域设计

关键点实现机制优势
domainGTT域(GPU访问路径)明确GPU访问方式
alloc_domainCPU域(物理分配位置)内存实际在系统RAM中
灵活性支持内存在不同域之间迁移适应不同访问模式

8. 性能优化策略

优化策略实现方式核心函数/机制性能收益
页面预取HMM机制支持页面预取hmm_range_fault()减少页面故障延迟,提前准备数据
批量操作批量预留多个BOsttm_eu_reserve_buffers()减少锁竞争和上下文切换,提高吞吐量
批量验证process_validate_vms()一次性处理多个VM,降低系统调用开销
延迟更新延迟恢复工作队列AMDGPU_USERPTR_RESTORE_DELAY_MS (1ms)聚合多次失效事件,减少恢复次数
延迟调度schedule_delayed_work()避免频繁的上下文切换
零拷贝传输GPU直接访问用户内存HMM + userptr机制消除CPU-GPU数据拷贝,节省带宽和时间
统一虚拟地址共享地址空间避免地址转换开销
无锁设计原子操作atomic_read(), atomic_cmpxchg()减少锁竞争,提高并发性能
读写信号量down_read(), down_write()允许多读单写,提高并发度

9. 潜在问题和注意事项

问题类别具体问题影响缓解措施
内存固定开销Userptr页面需要固定在内存中增加内存压力,页面无法被换出限制userptr总量,使用内存限制机制
大量userptr分配可能导致系统内存不足amdgpu_amdkfd_reserve_mem_limit() 限制
长期占用物理内存影响其他进程可用内存及时释放不用的userptr
页面故障延迟首次访问触发页面故障GPU访问延迟增加使用页面预取优化
失效后恢复hmm_range_fault() 等待时间延迟工作队列批量处理
页表更新需要等待GPU完成异步同步机制 (amdgpu_sync)
并发复杂性MMU notifier与GPU操作并发需要复杂的同步机制使用mmap_sem + process_info->lock
多VM并发访问可能出现死锁严格的锁获取顺序
原子操作竞争atomic_cmpxchg可能失败需重试循环重试机制
进程生命周期进程异常退出MMU notifier需要正确清理amdgpu_mn_unregister() 清理
task_struct引用计数usertask 管理不当导致泄露get_task_struct() / put_task_struct()
mm_struct有效性进程退出后mm可能无效get_task_mm() / mmput() 保护
页面迁移CPU/GPU域切换TTM验证开销减少不必要的域切换
并发迁移可能导致数据不一致使用eviction fence同步

10. 应用场景

应用领域具体场景技术优势典型应用
零拷贝数据传输大数据处理GPU直接访问用户缓冲区,无需拷贝数据库查询加速、日志分析
视频处理避免CPU-GPU内存拷贝实时视频编解码、图像处理
科学计算减少数据传输开销气象模拟、分子动力学
HSA (Heterogeneous System Architecture)统一内存模型CPU和GPU共享地址空间异构计算平台
共享数据结构直接访问复杂数据结构图数据库、指针追踪算法
无缝协作CPU和GPU交替访问同一数据流水线式处理
机器学习大模型训练减少数据传输开销Transformer、LLM训练
推理加速直接访问输入数据实时推理服务
数据增强CPU预处理,GPU直接消费图像增强、数据预处理
图形渲染纹理数据用户空间准备纹理,GPU直接读取游戏引擎、3D渲染
顶点数据避免顶点缓冲拷贝CAD软件、建模工具
高性能计算MPI通信减少进程间数据拷贝并行计算框架
流式处理连续数据流零拷贝传输实时数据分析
内存映射文件大文件处理文件内容直接映射给GPU文件索引、搜索引擎
持久化内存GPU直接访问持久化数据数据库系统

11. 代码流程图

用户调用 KFD_IOC_ALLOC_MEM_FLAGS_USERPTR
    |
    v
kfd_ioctl_alloc_memory_of_gpu()
    |
    +-- 检查VMA是否为IO内存 (find_vma)
    |       |
    |       +-- 是 -> 转换为DOORBELL
    |       +-- 否 -> 验证地址对齐
    |
    v
amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu()
    |
    +-- 设置域: domain=GTT, alloc_domain=CPU
    +-- 创建BO (amdgpu_bo_create)
    +-- 分配kgd_mem结构
    +-- 添加到userptr_valid_list
    |
    v
init_user_pages()
    |
    +-- amdgpu_ttm_tt_set_userptr()
    |       |
    |       +-- 保存user_addr到gtt->userptr
    |       +-- 引用usertask
    |
    +-- amdgpu_mn_register()
    |       |
    |       +-- 创建HMM mirror
    |       +-- 插入interval_tree
    |       +-- 注册MMU notifier回调
    |
    +-- amdgpu_ttm_tt_get_user_pages()
    |       |
    |       +-- find_vma验证地址
    |       +-- hmm_range_register()
    |       +-- hmm_range_fault() 触发页面故障
    |       +-- 转换PFN到struct page*
    |
    +-- ttm_bo_validate()
            |
            +-- 在GTT域验证BO
            +-- 建立GPU映射

映射到GPU (用户调用map操作)
    |
    v
amdgpu_amdkfd_gpuvm_map_memory_to_gpu()
    |
    +-- 检查is_invalid_userptr
    +-- add_bo_to_vm()
    +-- map_bo_to_gpuvm()
    +-- vm_update_pds() 更新页目录

MMU Notifier回调 (用户munmap/mprotect等)
    |
    v
amdgpu_mn_invalidate_range_start()
    |
    +-- atomic_set(&mem->invalid, 1)
    +-- 移动到userptr_inval_list
    +-- schedule_delayed_work(restore_userptr_work)

恢复工作队列
    |
    v
amdgpu_amdkfd_restore_userptr_worker()
    |
    +-- update_invalid_user_pages()
    |       |
    |       +-- 迁移到CPU域
    |       +-- amdgpu_ttm_tt_get_user_pages() 重新获取页面
    |       +-- atomic_cmpxchg清除invalid标志
    |
    +-- validate_invalid_user_pages()
            |
            +-- ttm_bo_validate() 验证回GTT域
            +-- update_gpuvm_pte() 更新页表
            +-- 移动回userptr_valid_list

12. 总结

KFD的userptr机制通过HMM、MMU Notifier和TTM的深度集成,实现了CPU和GPU之间的零拷贝内存共享。关键技术包括:

  1. HMM统一内存管理: 透明的页面迁移和故障处理
  2. MMU Notifier实时监控: 自动跟踪用户空间内存变化
  3. 双域设计: 灵活的内存域管理
  4. 失效恢复机制: 自动恢复失效的userptr映射
  5. 批量优化: 减少锁竞争和系统调用开销

amdgpu的userptr bo的实现,还是依托于drm框架下的ttm,以及hmm、mn机制,想深入理解的话,请查看专栏:linux drm子系统。
这套机制为HSA和高性能计算提供了坚实的基础,使得GPU能够高效、安全地访问用户态内存。看起来复杂的很,我也是在反复调试的过程中总结了下流程,但是否有更好的实现技术或思路,我无法给出定论。既然AMD的大佬们都使用这种方案,那说明该方案有合理性和普适性,欢迎读友们讨论。

内容概要:本文档介绍了基于3D FDTD(时域有限差分)方法在MATLAB平台上对微带线馈电的矩形天线进行仿真分析技术方案,重点在于模拟超MATLAB基于3D FDTD的微带线馈矩形天线分析[用于模拟超宽带脉冲通过线馈矩形天线的传播,以计算微带结构的回波损耗参数]宽带脉冲信号通过天线结构的传播过程,并计算微带结构的回波损耗参数(S11),以评估天线的匹配性能和辐射特性。该方法通过建立三维电磁场模型,精确求解麦克斯韦方程组,适用于高频电磁仿真,能够有效分析天线在宽频带内的响应特性。文档还提及该资源属于一个涵盖多个科研方向的综合性MATLAB仿真资源包,涉及通信、信号处理、电力系统、机器学习等多个领域。; 适合人群:具备电磁场与微波技术基础知识,熟悉MATLAB编程及数值仿真的高校研究生、科研人员及通信工程领域技术人员。; 使用场景及目标:① 掌握3D FDTD方法在天线仿真中的具体实现流程;② 分析微带天线的回波损耗特性,优化天线设计参数以提升宽带匹配性能;③ 学习复杂电磁问题的数值建模与仿真技巧,拓展在射频与无线通信领域的研究能力。; 阅读建议:建议读者结合电磁理论基础,仔细理解FDTD算法的离散化过程和边界条件设置,运行并调试提供的MATLAB代码,通过调整天线几何尺寸和材料参数观察回波损耗曲线的变化,从而深入掌握仿真原理与工程应用方法。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值