ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入解析Intel IOMMU:从虚拟化I/O加速到设备直通实战

深入解析Intel IOMMU:从虚拟化I/O加速到设备直通实战 简介本资源是面向Linux内核开发者、虚拟化工程师及系统安全研究人员的Intel IOMMU底层机制学习材料聚焦I/O内存管理单元在硬件辅助虚拟化与DMA安全隔离中的核心实现。压缩包为RAR格式共2个文件1个C源码文件、1个头文件总大小32KB轻量精炼便于快速切入驱动层逻辑分析。其中C文件承载IOMMU初始化、寄存器操作、设备映射与故障处理等关键驱动逻辑头文件则定义数据结构、宏常量及函数接口构成完整可读的硬件交互抽象层。已有224人学习下载适合具备一定Linux设备驱动基础的中高级开发者用于深入理解Intel VT-d规范落地细节、调试DMA访问异常、优化虚拟机直通PCIe passthrough配置或为KVM/QEMU环境下的I/O安全加固提供源码级参考依据。1. 项目缘起从一次虚拟机启动失败说起那天下午我正打算在一台搭载了Intel酷睿i7处理器的台式机上用VMware Workstation启动一个用于测试的Linux虚拟机。环境是熟悉的系统是刚重装过的Windows 10BIOS里也确认开启了虚拟化技术Intel VT-x。然而点击“开启此虚拟机”后熟悉的启动画面没有出现取而代之的是一个刺眼的错误提示“此平台不支持虚拟化的 Intel VT-x/EPT”。我愣了一下这不对啊硬件明明支持BIOS也开了怎么VMware就“不认识”了呢重启进入BIOS再次确认VT-dIntel Virtualization Technology for Directed I/O的选项赫然在目并且处于“Enabled”状态。一个念头闪过会不会和这个“VT-d”有关为了快速恢复工作我尝试在VMware的虚拟机设置里将虚拟化引擎的“虚拟化Intel VT-x/EPT或AMD-V/RVI(V)”选项取消勾选。虚拟机果然能启动了但性能监控显示I/O操作尤其是磁盘和网络吞吐明显不如从前流畅有种“隔靴搔痒”的感觉。这让我意识到我可能无意中关闭了一个对虚拟机I/O性能至关重要的底层加速特性——而这正是Intel IOMMU技术所要解决的问题。这次经历促使我决定彻底搞懂Intel IOMMUInput-Output Memory Management Unit到底是什么它和VT-x、VT-d这些经常在BIOS里看到的名词有什么关系以及为什么它在现代虚拟化、直通Passthrough乃至高性能计算场景中变得不可或缺。本文就是这次探索的总结我会从一个实践者的角度拆解IOMMU的原理、作用、启用方法以及那些令人头疼的排错过程。2. 核心概念拆解VT-x, VT-d, IOMMU与DMAR在深入IOMMU之前我们必须先理清Intel虚拟化技术栈中几个容易混淆的核心概念。它们环环相扣共同构建了完整的硬件虚拟化支持。2.1 Intel VT-xCPU虚拟化的基石Intel VT-x是Intel的CPU虚拟化扩展技术。你可以把它理解为在CPU硬件层面新增的一套指令集和运行模式Root Mode和Non-Root Mode让一个物理CPU能够被安全、高效地分割成多个虚拟CPUvCPU供多个虚拟机使用。在没有VT-x的时代虚拟机监控器VMM如VMware、Hyper-V需要通过复杂的软件技巧来“模拟”CPU行为这被称为“全虚拟化”或“二进制翻译”开销巨大。VT-x的出现使得虚拟机能够直接、非特权地运行大部分指令只有在执行敏感指令如修改页表时才陷入VMM进行处理这被称为“陷入再模拟”性能得到了质的飞跃。我们平时在BIOS里开启的“Virtualization Technology”选项主要就是指VT-x。2.2 Intel VT-d 与 IOMMUI/O虚拟化的钥匙然而仅有CPU的虚拟化是不够的。虚拟机还需要访问磁盘、网卡等外部设备。如果所有设备访问都由VMM软件模拟I/O性能将成为无法逾越的瓶颈。这就是Intel VT-dVirtualization Technology for Directed I/O登场的原因。VT-d是一套完整的I/O虚拟化解决方案架构而IOMMU输入输出内存管理单元则是这套架构中最核心的硬件组件。你可以把IOMMU想象成专门为I/O设备服务的“内存管理单元MMU”。我们都知道CPU的MMU负责将进程的虚拟地址转换为物理地址并实施内存保护。同理IOMMU负责为DMA直接内存访问操作执行地址转换和保护。在没有IOMMU的系统上设备进行DMA操作时使用的是“物理地址”。在虚拟化环境中这会导致严重问题安全问题一个虚拟机可以指令其透传的设备通过DMA访问任意物理内存包括宿主机或其他虚拟机的内存这完全破坏了隔离性。功能问题虚拟机操作系统认为自己拥有从零开始的连续物理内存但实际分配到的可能是分散的、不连续的物理页。设备如果直接使用虚拟机认为的“物理地址”我们称之为“Guest Physical Address, GPA”去DMA必然会访问到错误的内存位置。IOMMU完美地解决了这两个问题地址转换它维护着类似于CPU页表的“I/O页表”IO Page Table。当设备发起一个DMA请求携带一个GPA时IOMMU会查表将其转换为宿主机真实的物理地址Host Physical Address, HPA确保数据被写入正确的位置。内存保护I/O页表同样可以设置访问权限读/写。VMM可以为每个虚拟机或每个设备分配独立的I/O页表从而严格限制每个设备只能访问被授权的那部分物理内存实现了设备DMA层面的隔离。所以VT-d是技术总称IOMMU是实现该技术的硬件单元。在Linux内核中用于支持IOMMU的驱动框架就叫做“VT-d驱动”。2.3 DMARACPI表中的“地图”操作系统如何知道平台上存在IOMMU硬件以及它的具体配置如寄存器地址、支持的设备范围呢答案藏在ACPI高级配置与电源接口表中。DMARDMA Remapping Reporting是Intel定义的一种ACPI表。在系统启动时固件UEFI/BIOS会将DMAR表加载到内存中。这张表就像一张“地图”详细描述了系统中是否存在IOMMU硬件。IOMMU硬件寄存器的基地址MMIO地址。系统的DMA地址宽度支持多大的物理地址空间。最重要的DRHDDMA Remapping Hardware Unit Definition结构。一个物理系统可能包含多个IOMMU硬件单元例如CPU内置一个芯片组内置一个。每个DRHD描述一个IOMMU单元并列出由该单元管理的所有PCI设备通过PCI Segment, Bus, Device, Function号标识。当Linux内核启动时它会解析DMAR表根据其中的信息来初始化和配置VT-d驱动。这也是为什么我们常在内核启动日志中看到类似DMAR-IR: IOAPIC id 8 under DRHD base 0xfed90000 IOMMU 0的信息。这条日志的意思是内核发现IOAPIC中断控制器ID 8 归属于物理地址为0xfed90000的那个IOMMU硬件单元管理。注意iommupt是Linux内核的一个启动参数。pt代表“passthrough”直通。这个参数指示内核对于没有被直接分配给虚拟机的设备即大多数在宿主机上使用的设备IOMMU使用一种最简化的“直通”模式进行映射而不是建立完整的隔离页表。这能减少这些设备DMA操作的开销是推荐在生产环境中使用的参数。而iommuon则会为所有设备强制创建隔离页表开销较大通常仅用于调试。3. 为什么需要IOMMU核心应用场景剖析理解了IOMMU是什么之后我们来看看它具体在哪些场景下发挥着不可替代的作用。这不仅仅是“提升性能”那么简单更是实现某些高级特性的前提。3.1 场景一PCIe设备直通Passthrough这是IOMMU最经典、最核心的应用。所谓“直通”就是将物理服务器上的一个PCIe设备如高性能网卡、GPU、NVMe SSD完全、独占地分配给一个虚拟机让虚拟机驱动程序直接与硬件交互绕过VMM的模拟层从而获得近乎原生的性能。没有IOMMU的直通是不可想象的。如前所述设备的DMA会破坏内存隔离。IOMMU通过为这个直通设备单独分配一个I/O页表将其DMA操作严格限制在该虚拟机被分配的内存范围内同时完成GPA到HPA的转换使得直通变得安全、可行。实操心得在配置直通时务必在BIOS中开启VT-d。在Linux宿主机上除了启用内核VT-d支持还需要使用vfio-pci驱动来接管目标设备替代原有的原生驱动因为VFIO框架完整利用了IOMMU提供的隔离能力。VMware ESXi的“PCI设备直通”和KVM/QEMU的“VFIO直通”都依赖于此。3.2 场景二提升虚拟化I/O性能如SR-IOV即使不进行独占式的直通IOMMU也能提升虚拟化I/O性能。例如支持SR-IOV单根I/O虚拟化的网卡。一张物理网卡可以虚拟出多个轻量级的“虚拟功能”VF每个VF可以直通给一个虚拟机。每个VF都有自己的PCI配置空间并通过IOMMU进行独立的地址转换和隔离。这样多个虚拟机可以高性能地共享同一块物理硬件IOMMU保证了它们之间的安全隔离。3.3 场景三增强系统安全性抵御DMA攻击即使在非虚拟化环境中IOMMU也有其安全价值。恶意软件或拥有物理访问权限的攻击者可能利用带有DMA能力的设备如Thunderbolt、FireWire接口的设备发起“DMA攻击”直接读取或篡改系统内存。启用IOMMU后操作系统可以为这些外部DMA设备配置严格的访问策略将未经授权的DMA请求拦截下来从而抵御此类攻击。Linux内核的“IOMMU API”和“VFIO”框架也使得用户态程序可以安全地直接控制设备这在云计算和容器场景下很有用。3.4 场景四解决老旧操作系统或驱动的兼容性问题一些老旧的32位操作系统或驱动程序可能无法处理高于4GB的物理内存地址。当这些系统或驱动尝试让设备向“高地址”内存进行DMA时设备可能会因为地址宽度限制而失败。IOMMU可以通过重映射将这些高物理地址转换为设备能识别的低地址从而解决兼容性问题。4. 实战在Linux系统中启用与验证IOMMU理论说再多不如动手试一下。我们以常见的Linux发行版如Ubuntu 22.04为例演示如何启用IOMMU并验证其状态。4.1 第一步确认硬件与BIOS支持这是所有工作的前提。确认CPU支持执行cat /proc/cpuinfo | grep vmxIntel或svmAMD。有输出即代表CPU支持虚拟化扩展。对于IOMMUIntel CPU需支持VT-dAMD CPU需支持AMD-Vi。通常消费级平台如酷睿i7都支持但务必确认。进入BIOS/UEFI设置开机按特定键Del, F2, F10等进入。在“Advanced”或“Chipset”菜单下找到Intel Virtualization Technology(VT-x)必须开启。Intel VT for Directed I/O(VT-d) 或AMD IOMMU必须开启。 不同主板名称可能略有差异如“VT-d”、“Virtualization Technology for Directed I/O”。保存并退出。4.2 第二步配置Linux内核启动参数这是最关键的一步目的是告诉Linux内核启用IOMMU驱动。编辑GRUB配置文件。对于Ubuntusudo nano /etc/default/grub找到GRUB_CMDLINE_LINUX_DEFAULT这一行。它可能看起来像这样GRUB_CMDLINE_LINUX_DEFAULTquiet splash根据你的CPU厂商添加IOMMU启动参数对于Intel CPU在引号内添加intel_iommuon iommuptintel_iommuon强制启用Intel VT-d驱动。iommupt为直通设备启用IOMMU对非直通设备使用直通模式推荐。对于AMD CPU添加amd_iommuon iommupt修改后可能像这样GRUB_CMDLINE_LINUX_DEFAULTquiet splash intel_iommuon iommupt保存文件然后更新GRUB配置sudo update-grub重启系统。4.3 第三步验证IOMMU是否成功启用重启后通过以下命令检查检查内核启动日志sudo dmesg | grep -iE “DMAR|IOMMU”你应该能看到类似下面的输出这表明内核成功检测并初始化了IOMMU[ 0.000000] DMAR: IOMMU enabled [ 0.000000] DMAR: Host address width 39 [ 0.000000] DMAR: DRHD base: 0x000000fed90000 flags: 0x0 [ 0.000000] DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap 1c0000c40660462 ecap 19e2ff0505e [ 0.000000] DMAR: DRHD base: 0x000000fed91000 flags: 0x1 [ 0.000000] DMAR: dmar1: reg_base_addr fed91000 ver 1:0 cap d2008c40660462 ecap f050da [ 0.000000] DMAR: RMRR base: 0x0000009dc00000 end: 0x0000009dcfffff [ 0.000000] DMAR-IR: IOAPIC id 8 under DRHD base 0xfed91000 IOMMU 1 [ 0.000000] DMAR-IR: IOAPIC id 9 under DRHD base 0xfed91000 IOMMU 1 [ 0.000000] DMAR-IR: HPET id 0 under DRHD base 0xfed91000 IOMMU 1 [ 0.000000] DMAR-IR: x2apic is disabled because BIOS doesn‘t support interrupt remapping with x2apic and ‘intremapno_x2apic_optout’ not present.注意看是否有DMAR: IOMMU enabled这一行。如果有DMAR: BIOS has allocated no shadow GTT; disabling IOMMU for graphics之类的警告可能意味着集成显卡的IOMMU支持有问题但不一定影响其他设备。检查内核参数cat /proc/cmdline确认输出中包含你添加的intel_iommuon和iommupt参数。检查IOMMU分组这是为设备直通做准备的关键步骤。IOMMU以“组”为单位管理设备。一个组内的设备共享同一个IOMMU页表因此必须作为一个整体直通给虚拟机。sudo sh -c “for d in /sys/kernel/iommu_groups/*/devices/*; do n${d#*/iommu_groups/*}; n${n%%/*}; printf ‘IOMMU Group %s ‘ ‘$n’; lspci -nns ${d##*/}; done”这个命令会列出所有IOMMU组及其包含的PCI设备。你需要找到你想直通的设备比如独显并确认它在一个独立的组里或者和少量你愿意一起直通的设备同组。如果它和一个你无法直通的关键设备如主板芯片组同组那么直通将无法进行。4.4 排错为什么IOMMU启用失败如果你在dmesg中看不到IOMMU enabled或者虚拟机软件仍然报VT-x/EPT错误可以按以下思路排查BIOS设置未生效这是最常见的原因。再次进入BIOS确认VT-d选项确实已保存并启用。有些主板有“高级CPU设置”或“芯片组北桥设置”VT-d选项可能藏得比较深。尝试加载BIOS默认设置然后只开启VT-x和VT-d再试。内核参数错误或未生效检查cat /proc/cmdline确认参数已加载。确保没有拼写错误如intel_iommuon不是intel_iomuon。更新GRUB后必须重启。硬件不支持非常老的CPU或主板可能不支持VT-d。可以查阅Intel ARKark.intel.com网站搜索你的CPU型号在“高级技术”中查看是否支持“Intel® Virtualization Technology for Directed I/O (VT-d)”。内核未编译VT-d支持主流发行版的内核默认都包含VT-d驱动。但如果你使用的是自定义内核需要确认CONFIG_INTEL_IOMMUy或m已设置。可以通过zcat /proc/config.gz | grep CONFIG_INTEL_IOMMU来检查如果该文件存在。ACPI DMAR表问题极少数情况下主板的BIOS/UEFI固件可能存在bug提供的DMAR表不正确或缺失。这会导致内核无法发现IOMMU硬件。可以尝试升级主板BIOS到最新版本。在内核参数中添加intel_iommuigfx_off有时可以绕过某些集成显卡相关的问题。5. 进阶话题IOMMU与虚拟化软件的协同IOMMU的最终价值需要在虚拟化平台上体现。我们来看看它如何与主流虚拟化软件协同工作。5.1 与KVM/QEMU的配合VFIO直通在Linux KVM虚拟化方案中实现设备直通的标准方式是使用VFIOVirtual Function I/O框架。VFIO是一个安全、强大的用户态设备驱动框架它完全依赖于IOMMU提供的隔离能力。配置流程简述启用IOMMU如前所述。将目标设备从宿主机内核驱动如nouveau,nvidia,igb中解绑然后绑定到vfio-pci驱动上。这通常通过修改initramfs或使用driverctl工具实现。在QEMU启动命令中使用-device vfio-pci,hostxx:xx.x参数将设备传递给虚拟机。关键点QEMU进程会通过VFIO框架为这个直通设备在IOMMU中申请并设置好专属的I/O页表。虚拟机内的驱动程序直接与硬件通信其发起的DMA请求由IOMMU硬件自动完成地址转换和安全检查性能损失极小。5.2 与VMware Workstation/ESXi的配合对于VMware系列产品IOMMUVT-d的支持主要用于以下两点虚拟化性能增强在VMware Workstation/Fusion中即使不进行PCI直通开启VT-d也能让虚拟化引擎使用“EPT”扩展页表等硬件加速特性提升内存虚拟化效率。这就是文章开头那个错误提示的根源——当宿主机BIOS开启了VT-d但操作系统内核未正确启用IOMMU驱动时VMware检测到状态不一致可能出于兼容性考虑而禁用EPT导致报错。PCI设备直通在VMware ESXi中要使用“PCI设备直通”功能必须在主机BIOS中开启VT-d并且ESXi系统会自动检测和使用IOMMU。解决开头的报错那个“此平台不支持虚拟化的 Intel VT-x/EPT”错误根本原因就是宿主机Windows/Linux层面没有正确启用IOMMU驱动导致VMware无法使用需要IOMMU支持的硬件加速特性。正确的解决步骤是进入宿主机BIOS确认VT-d已开启。对于Linux宿主机按照第4章添加内核参数启用IOMMU。对于Windows宿主机确保使用的是支持IOMMU的Windows版本如Pro/Enterprise并且相关驱动正常。有时需要以管理员身份运行bcdedit /set hypervisorlaunchtype auto并重启。完成以上步骤后VMware的“虚拟化Intel VT-x/EPT”选项就能正常勾选了。5.3 中断重映射Interrupt Remapping这是一个高级但至关重要的特性通常由IOMMU硬件一并提供即VT-d规范的一部分。在直通场景下设备的中断MSI/MSI-X需要直接投递给指定的虚拟机。中断重映射确保了设备中断的安全、正确路由。如果IOMMU支持但中断重映射未启用或被禁用你在直通某些设备特别是较新的网卡和显卡时可能会在虚拟机启动时遇到系统崩溃或设备无法工作的情况。在内核参数中intel_iommuon默认会尝试启用中断重映射。如果遇到问题可以尝试显式指定intremapon或intremapno_x2apic_optout针对一些老BIOS的兼容性问题。6. 性能考量与常见误区启用IOMMU并非没有代价理解其性能影响和常见误区有助于做出正确决策。6.1 性能开销在哪里IOMMU会引入少量的性能开销主要来自两个方面地址转换延迟设备每次DMA操作都需要经过IOMMU的页表查询TLB缓存可以极大缓解此开销。I/O页表管理开销当虚拟机内存发生变化如内存气球膨胀/收缩、内存热插拔时VMM需要更新I/O页表这会带来软件开销。然而在绝大多数现代硬件上IOMMU硬件单元的设计非常高效其TLB容量大、命中率高。对于网络、存储等高吞吐、低延迟的I/O密集型负载直通带来的性能收益绕过软件模拟远远大于IOMMU引入的微小开销。因此在虚拟化环境中启用IOMMU并配合设备直通是性能净收益的。6.2 常见误区澄清误区一开了VT-x就必须开VT-d/IOMMU。澄清不一定。VT-x是CPU虚拟化VT-d/IOMMU是I/O虚拟化和隔离。如果你只运行不需要高性能I/O或设备直通的虚拟机可以只开VT-x。但为了安全性和未来扩展性建议一并开启。误区二IOMMU会显著降低游戏显卡在虚拟机里的性能。澄清恰恰相反。对于GPU直通如“显卡穿透”到Windows虚拟机玩游戏IOMMU是必需且有益的。没有IOMMUGPU直通根本无法实现或极不安全。IOMMU的硬件转换开销与GPU渲染的巨大计算量相比微乎其微不会成为性能瓶颈。瓶颈通常出现在CPU模拟、内存带宽或驱动优化上。误区三iommupt参数会降低安全性。澄清iommupt直通模式仅针对宿主机自身使用的、未分配给虚拟机的设备。对于这些设备内核为其建立1:1的直通映射减少了页表管理开销。这并不影响分配给虚拟机的设备的安全性因为虚拟机的设备会使用完整的、隔离的I/O页表。这是一种合理的性能优化不会牺牲核心安全属性。误区四所有PCI设备都能被单独直通。澄清否。IOMMU以“组”为单位进行隔离。一个IOMMU组内的所有设备必须一起直通给同一个虚拟机。如果一块独立显卡和一个USB控制器在同一个IOMMU组里你想直通显卡就必须把那个USB控制器也一起直通出去。这是由硬件拓扑PCIe Switch、Root Port如何连接到IOMMU决定的软件无法改变。7. 故障诊断与日志分析当IOMMU或直通相关功能出现问题时系统日志是首要的排查工具。这里列举几个典型场景。7.1 场景直通设备导致虚拟机启动失败或宿主机崩溃排查步骤检查内核启动日志sudo dmesg | grep -iE “DMAR|IOMMU|VFIO”。关注是否有错误ERROR或警告WARN。DMAR: [Firmware Bug]表明ACPI DMAR表可能存在固件bug。DMAR: DRHD: handling fault status reg 3或DMAR: [DMA Write] Request device … fault reason …这表明有设备进行了非法的DMA访问被IOMMU拦截了。这常常是因为直通设备的驱动在虚拟机内尝试访问未被授权内存。可能需要检查虚拟机内的驱动版本或尝试在QEMU参数中添加,x-vgaon针对老VGA设备或调整PCIe ACSAccess Control Services设置。检查虚拟机日志如果是KVM查看QEMU的启动输出或日志文件/var/log/libvirt/qemu/。如果是VMware查看虚拟机日志文件.vmx同目录下的.log文件。尝试禁用中断重映射在某些老硬件或特定设备组合下中断重映射可能有问题。可以尝试在宿主机内核参数中将intel_iommuon替换为intel_iommuon intremapoff仅用于测试会降低安全性。如果问题消失说明是中断重映射兼容性问题可能需要更新BIOS或寻找其他解决方案。7.2 场景IOMMU分组不理想关键设备无法单独直通现象使用lspci和检查IOMMU组脚本发现你想直通的独立显卡和主板芯片组的SATA控制器或USB主控制器在同一个IOMMU组里。分析与解决原因这是由主板PCIe总线硬件拓扑决定的。通常所有连接到同一个PCIe Root Port下的设备会共享一个IOMMU组。解决方案更换PCIe插槽将显卡插到另一个CPU直连的PCIe x16插槽上可能使其进入一个独立的IOMMU组。查阅主板手册了解不同插槽的拓扑关系。使用PCIe ACS补丁这是一个内核补丁可以强制让IOMMU在更细粒度上隔离设备破坏原有的硬件分组。警告这需要自行编译内核可能带来系统不稳定且不符合硬件设计规范存在潜在风险。仅作为最后手段。接受并直通整个组如果同组的其他设备如一个USB控制器你也可以接受直通给同一个虚拟机那么就将整个组直通出去。对于游戏虚拟机直通一个USB控制器用于连接键鼠也是常见做法。7.3 场景启用IOMMU后系统启动变慢或出现奇怪问题排查检查DMAR表初始化日志dmesg中DMAR相关的初始化信息如果非常多、非常慢可能是系统PCI设备很多或者固件提供的RMRRReserved Memory Regions区域异常。RMRR用于一些需要特殊内存区域的设备如集成显卡。尝试禁用集成显卡的IOMMU如果问题可能与集成显卡有关可以尝试添加内核参数intel_iommuigfx_off。这个参数会禁止对集成显卡进行DMA重映射有时能解决启动问题或图形异常。简化参数只使用intel_iommuon不加iommupt观察是否问题依旧。或者尝试iommuforce强制为所有设备启用IOMMU即使它们声称不支持这有助于判断是否是个别设备驱动兼容性问题。经过这样一番从理论到实践、从启用验证到排错进阶的梳理再回头看文章开头那个VMware报错其本质就非常清晰了它是一个由宿主机IOMMU软件栈未正确初始化导致虚拟化软件无法使用硬件辅助I/O虚拟化功能的连锁反应。解决它不仅是为了消除一个错误提示更是为了打开一扇通往更高性能、更安全虚拟化应用的大门。无论是搭建一个高性能的云游戏虚拟机还是构建一个需要直通网卡的网络测试环境对Intel IOMMU的深入理解和正确配置都是不可或缺的基础技能。本文还有配套的精品资源点击获取
返回列表