news 2026/6/17 10:37:06

USDPAA用户空间数据路径加速:门户配置与DMA内存管理实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
USDPAA用户空间数据路径加速:门户配置与DMA内存管理实战

1. 项目概述:用户空间数据路径加速的挑战与机遇

在嵌入式网络处理领域,尤其是像NXP QorIQ LS系列这样的多核通信处理器上,数据平面的性能直接决定了整个系统的吞吐量和延迟。传统的内核网络协议栈虽然功能完善,但其复杂的处理流程和频繁的内核态/用户态切换,在处理高速数据流时往往会成为性能瓶颈。想象一下,一个10Gbps的网络接口,每秒钟要处理近1500万个64字节的小包,如果每个数据包都要经过内核协议栈的层层处理,CPU很快就会不堪重负。

这时,DMA(直接内存访问)用户空间数据路径技术就成为了破局的关键。DMA允许像网络控制器、加解密引擎这样的硬件外设,绕过CPU直接与内存进行数据读写,这本身就是一次巨大的性能解放。而用户空间数据路径,则是更进一步,让应用程序能够直接在用户态操作这些硬件资源,几乎完全消除了内核介入的开销。USDPAA(用户空间数据路径加速架构)就是NXP为自家DPAA(数据路径加速架构)硬件提供的一套用户空间驱动框架,它让开发者能够像调用本地库函数一样,直接操纵QMan(队列管理器)和BMan(缓冲区管理器)这样的核心加速引擎。

但这条路并不好走。把原本由内核严密管控的硬件资源直接暴露给用户空间,会带来一系列棘手问题:内存如何管理才能被DMA设备安全高效地访问?多个应用或线程如何安全地共享或独占硬件门户?系统资源如何通过设备树进行静态分配和动态绑定?这些正是我们在配置QMan/BMan门户和管理DMA内存时需要深入理解的核心理念。本文将结合我在多个基于LS1046A等平台的项目实战经验,为你拆解USDPAA中门户配置与DMA内存管理的每一个技术细节与实操要点。

2. 核心思路拆解:内核与用户空间的资源博弈

要理解USDPAA的设计,首先要跳出传统Linux驱动模型的思维定式。在标准模型中,硬件资源由内核统一管理,用户程序通过系统调用间接使用。而USDPAA的目标是让高性能应用(如数据包转发、防火墙、负载均衡)能“直达”硬件,这就需要一套全新的资源划分与管理哲学。

2.1 门户(Portal)的本质与两种生命周期

门户是DPAA架构中软件与硬件加速器(QMan/BMan)交互的窗口。你可以把它理解为一个专用的硬件队列和寄存器集合,应用程序通过它向硬件提交命令或读取结果。

内核空间门户的特点是持久化。它们在系统启动时由内核驱动初始化,并一直服务于内核子系统(如网络驱动、加密驱动)。内核中的其他模块可以默认这些门户始终可用且状态稳定。这种设计保证了系统服务的可靠性,但缺乏灵活性。

用户空间门户则遵循线程专有模型。一个门户从初始化、服务到销毁,其生命周期完全与绑定它的那个用户空间线程同步。线程运行时,门户为其独占服务;线程结束,门户资源便被释放。这种设计带来了极高的灵活性和性能,因为应用程序可以根据负载动态创建和销毁处理线程,并为其分配专属硬件资源,避免了资源锁竞争。但这也意味着应用程序必须自己负责门户的整个生命周期管理,包括错误处理。

2.2 设备树:静态资源分配的蓝图

在嵌入式Linux中,设备树(Device Tree)是描述硬件资源的权威配置文件。对于QMan/BMan这类硬件资源,内核需要在启动时就知道哪些归内核管,哪些可以分给用户空间。这就是设备树中fsl,usdpaa-portal属性的作用。

查看一个实际的设备树片段,区别一目了然:

// 内核门户:无 fsl,usdpaa-portal 属性 qportal0: qman-portal@0 { compatible = "fsl,qman-portal"; reg = <0x0 0x4000>; interrupts = <104 0x2>; fsl,qman-channel-id = <0x0>; }; // 用户空间门户:明确标记 fsl,usdpaa-portal qportal1: qman-portal@4000 { compatible = "fsl,qman-portal"; fsl,usdpaa-portal; // 关键属性 reg = <0x4000 0x4000>; interrupts = <106 0x2>; fsl,qman-channel-id = <0x1>; };

设备树只是完成了资源的“划分”,并没有规定用户空间程序具体怎么用。内核在启动时,会为所有标记了fsl,usdpaa-portal的门户在/dev目录下创建对应的字符设备节点。应用程序通过标准的文件操作(open)或USDPAA提供的专用API来“认领”并初始化这些门户。

2.3 性能关键:CPU亲和性与缓存优化

设备树节点中的cpu-handle = <&cpu1>;属性暗示了一个重要的性能优化点:门户与CPU核心的亲和性(Affinity)。DPAA硬件有一个称为“Stashing”的特性,它可以将数据直接推送到特定CPU核心的缓存中。如果一个线程在它所绑定的门户所属的CPU核心上运行,那么硬件访问的数据很可能还在该核心的本地缓存里,速度极快。

如果线程跑在了别的核心上会怎样?功能上完全正常,因为多核缓存一致性协议(如MESI)会保证数据正确性。但性能会受损:每次硬件访问可能都需要从另一个核心的缓存里“偷”数据,或者从更慢的主存中读取,这会显著增加延迟并引发核心间的缓存流量争用。因此,在编写USDPAA应用时,一个最佳实践是:使用pthread_setaffinity_np()将线程绑定到其门户所在的CPU核心上。实测中,对于处理小包的高吞吐场景,维持缓存亲和性可能带来超过10%的性能提升。

注意:早期的USDPAA实现中,门户与CPU的绑定是静态的(通过设备树)。但文档也提到,后续版本可能会提供更灵活的绑定方式。在现有项目中,我们仍需遵循这一约束。

3. DMA内存管理:为硬件特供的“内存专区”

DMA是性能的基石,但也是最容易踩坑的地方。普通应用程序通过malloc()分配的内存,对DMA设备来说可能是“不可见”或“难以高效访问”的。原因在于以下几个硬件强约束:

  1. 物理连续性:像FMan(帧管理器)、SEC(安全引擎)这类DPAA外设,它们通过自己的DMA引擎访问内存,不经过CPU的MMU进行虚拟地址转换。它们直接使用物理地址,并且通常要求这些物理地址是连续的。malloc分配的内存,在虚拟地址空间是连续的,但其背后的物理页很可能是离散的,这不符合硬件要求。
  2. 地址可访问性:有些SoC存在多个内存控制器或地址域,外设可能无法访问所有物理内存区域。必须从特定的、外设可寻址的物理内存区间分配。
  3. 不可交换性:Linux内核可能会将长时间不用的内存页交换到磁盘。如果一块内存正在被DMA设备读写时被换出,将导致数据损坏或设备错误。因此DMA内存必须被“钉”在物理内存中。
  4. 高效的地址转换:用户空间程序使用虚拟地址,而硬件使用物理地址。两者之间需要快速转换。对于每秒处理千万级数据包的应用,转换开销必须极低。

3.1 USDPAA的解决方案:预留大页内存与TLB1映射

USDPAA采用了一种“预留制”方案来解决上述问题,其核心思想是:在系统启动的早期,趁内核内存管理系统还没完全“上锁”,就提前划走一大块物理连续内存,专供DMA使用。

内核配置与驱动: 在Linux内核的配置菜单中,你需要开启CONFIG_FSL_USDPAA_SHMEM选项(路径:Device Drivers->Misc devices->NXP USDPAA shared memory driver)。这个选项决定了预留内存的大小,默认是64MB。���于高性能转发应用,我建议根据实际缓冲区数量和数据包大小进行评估,适当调大此值,例如256MB或512MB,避免运行时内存不足。

这个内核驱动会做两件关键事:

  1. 暴露设备接口:创建一个/dev/fsl_usdpaa_shmem字符设备。用户空间程序通过mmap()这个设备,就能将那块预留的、物理连续的物理内存映射到自己的虚拟地址空间。
  2. 安装TLB1钩子:这是性能优化的精髓。Power架构的MMU有TLB0(页表缓存,映射4KB小页)和TLB1(可变大页映射)。驱动会在内存管理代码中植入一个钩子,当应用首次访问这块DMA内存区域触发页错误时,内核不会像通常那样为其建立一个个4KB的TLB0映射,而是直接建立一个覆盖整个预留区域的、单一的大TLB1映射。这意味着,只要应用访问过这块区域中的任何一个地址,整个区域(比如64MB)的映射就一次性建立好了,后续访问零页错误开销。对于追求亚微秒级延迟的数据平面应用,消除页错误中断是至关重要的。

3.2 用户空间API:分配、释放与地址转换

USDPAA在用户空间提供了简洁的API来操作这块“DMA内存专区”,头文件是<usdpaa/dma_mem.h>

初始化与设置: 在任何DMA内存分配之前,必须先调用dma_mem_setup()。这个函数内部会处理打开/dev/fsl_usdpaa_shmem设备、执行mmap以及处理必要的对齐(由于TLB1映射有对齐要求,应用需要提议一个对齐的虚拟地址,而不是让内核随意分配)。

核心操作函数

  • void *dma_mem_memalign(size_t alignment, size_t size): 从DMA内存池中分配一块对齐的内存。alignment通常需要匹配缓存行大小(如64字节)或硬件要求的特定对齐值。
  • void dma_mem_free(void *addr): 释放之前分配的内存。
  • void *dma_mem_ptov(phys_addr_t phys): 将物理地址转换为当前进程虚拟地址空间中的指针。这是与QMan/BMan API交互的必备操作,因为硬件操作通常使用物理地址。
  • phys_addr_t dma_mem_vtop(void *addr): 将虚拟地址转换回物理地址,用于配置DMA设备的目的地址等场景。

一个典型的使用流程

#include <usdpaa/dma_mem.h> #include <usdpaa/fsl_usd.h> // 1. 初始化DMA内存系统 if (dma_mem_setup() != 0) { perror("Failed to setup DMA memory"); exit(1); } // 2. 分配一块用于存放数据包的DMA缓冲区(缓存行对齐) #define BUF_SIZE 2048 #define CACHELINE_SIZE 64 void *dma_buffer = dma_mem_memalign(CACHELINE_SIZE, BUF_SIZE); if (!dma_buffer) { perror("Failed to allocate DMA buffer"); exit(1); } // 3. 获取该缓冲区的物理地址,用于传递给硬件(例如,放入帧描述符) phys_addr_t buffer_phys = dma_mem_vtop(dma_buffer); // 4. 当硬件完成DMA写入后,在用户空间直接通过 dma_buffer 指针访问数据 process_packet(dma_buffer); // 5. 使用完毕后释放 dma_mem_free(dma_buffer);

3.3 当前方案的局限与未来演进

USDPAA当前的DMA内存管理方案简单有效,但存在一个明显限制:这块预留的物理连续内存,同时只能被一个用户空间进程安全地映射。因为它是通过一个全局的/dev节点mmap的。这限制了多个USDPAA应用实例并行运行的能力。

文档中也明确指出,未来的版本很可能转向基于HugeTLB(大页)的机制。HugeTLB是Linux标准的大内存页支持,能够提供类似TLB1映射的性能优势,同时具备更好的共享性和管理灵活性。在现有项目开发中,我们需要意识到这个限制,通常采用单进程多线程的模型来利用多核,而不是多进程模型。

4. 门户配置与应用程序初始化实战

理解了原理,我们来看如何在实际代码中初始化和使用QMan/BMan门户。整个过程是分层递进的。

4.1 初始化顺序:依赖关系不能乱

USDPAA的初始化有严格的顺序要求,就像搭积木,底层没搭好,上层就会塌。正确的顺序是:

  1. USDPAA “of” 驱动初始化:这是最底层,负责从设备树中解析资源信息。必须最先完成。
  2. DMA内存初始化:调用dma_mem_setup(),为后续所有缓冲区分配准备好“弹药库”。
  3. QMan/BMan线程初始化:在每个要使用门户的线程中,调用qman_thread_init()bman_thread_init()。这个调用会完成门户的硬件初始化,并使该线程获得对门户的访问权。
  4. 网络配置获取(如果涉及网络):通过usdpaa_netcfg_acquire()读取FMC策略文件和配置文件,获取帧队列、缓冲区池等网络相关资源的配置。

错误的初始化顺序是导致程序崩溃或硬件无响应的常见原因。务必确保在调用任何QMan/BMan API之前,前两步已经成功完成。

4.2 线程初始化与门户绑定

下面是一个典型的USDPAA工作线程的初始化代码片段:

#include <usdpaa/fsl_usd.h> #include <pthread.h> #include <sched.h> void* data_plane_thread(void *arg) { int thread_id = *(int*)arg; struct qman_portal *portal; // 步骤1: 初始化当前线程的QMan门户 // 这个API会查找设备树中可用的、标记为usdpaa的门户,并将其绑定到当前线程 if (qman_thread_init() != 0) { fprintf(stderr, "Thread %d: QMan portal init failed\n", thread_id); return NULL; } // 步骤2: (强烈推荐)设置CPU亲和性,让线程运行在门户绑定的核心上 cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(thread_id, &cpuset); // 假设thread_id对应核心编号 if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) != 0) { perror("pthread_setaffinity_np failed"); // 亲和性设置失败不一定是致命错误,但会影响性能 } // 步骤3: 获取当前线程的门户上下文,用于后续API调用 portal = qman_get_affine_portal(thread_id); if (!portal) { fprintf(stderr, "Failed to get affine portal for core %d\n", thread_id); qman_thread_finish(); return NULL; } // ... 这里是线程的主循环,使用portal进行入队/出队操作 ... // 步骤4: 线程退出前,清理门户资源 qman_thread_finish(); return NULL; }

qman_thread_init()这个调用是“懒加载”的。它并不会在系统启动时初始化所有门户,而是等到第一个线程调用它时,才去初始化并绑定一个空闲的门户给该线程。这符合用户空间门户“按需创建”的生命周期模型。

4.3 原始门户API:为高级场景预留的后门

除了标准的线程初始化API,USDPAA还提供了一组“原始门户API”(Raw Portal APIs)。这组API的用途比较特殊:它允许一个USDPAA进程代表另一个处理器(例如,另一个ARM核心,甚至是一个DSP协处理器)来分配QMan/BMan门户。

struct usdpaa_raw_portal portal; int ret; // 分配一个未配置的原始QMan门户 ret = qman_allocate_raw_portal(&portal); if (ret) { // 错误处理 } // 此时,portal结构体中包含了门户的物理地址、大小等信息。 // 分配者需要自行通过其他IPC机制将这些信息传递给目标处理器, // 并由目标处理器完成门户的最终配置和初始化。 // 使用完毕后释放 ret = qman_free_raw_portal(&portal);

什么情况下会用这个?在非对称多处理(AMP)系统中,一个核心运行Linux,另一个核心运行裸机或RTOS。Linux端的USDPAA应用可以帮裸机核心预留好硬件门户资源,然后通过共享内存将门户配置信息传递过去。对于绝大多数运行对称多处理(SMP)Linux的应用来说,用不到这个API,使用标准的qman_thread_init即可。

5. 网络配置与FMan集成:打通数据平面

USDPAA应用如果涉及网络数据包处理,就必然要和FMan(帧管理器)打交道。FMan负责MAC层帧的接收、发送、分类、哈希等。QMan作为队列管理器,连接着FMan和软件(内核或用户空间)。

5.1 四种典型的交互用例

文档中清晰地阐述了四种场景,理解它们对设计系统至关重要:

  1. FMan -> 内核网络栈:最传统的方式。FMan将数据包送入内核驱动的队列,由内核协议栈处理。这是控制平面和管理流量的典型路径。
  2. FMan -> USDPAA应用:纯用户空间数据平面。FMan直接将数据包送入USDPAA应用持有的队列,实现旁路内核的极速转发。我们的“反射器”(Reflector)示例就是这种模式。
  3. FMan -> 内核 与 USDPAA:混合模式。FMan可以根据帧内容(如VLAN ID、目的IP哈希)将流量分发到不同的队列,一部分给内核处理(如SSH、HTTP管理流量),另一部分给USDPAA应用进行快速转发(如数据平面流量)。这通过FMan的帧分类器实现。
  4. 内核 -> USDPAA(通过QMan或TUN/TAP):数据从内核协议栈发出,经由QMan队列或标准的Linux TUN/TAP虚拟设备,送给USDPAA应用处理。这可以用于实现用户空间的VPN、隧道封装等。

5.2 设备树决定资源归属

一个网络接口(如一个MAC)最终是给内核用还是给USDPAA用,是由设备树中的以太网节点属性决定的。关键区别在于compatible属性:

// 给USDPAA用的接口:使用 "fsl,dpa-ethernet-init" ethernet@0 { compatible = "fsl,dpa-ethernet-init"; // 关键标识 fsl,fman-mac = <&enet0>; // ... 其他属性如buffer pools, channels ... }; // 给内核网络栈用的接口:使用 "fsl,dpa-ethernet" ethernet@1 { compatible = "fsl,dpa-ethernet"; // 关键标识 fsl,fman-mac = <&enet1>; };

内核在启动时,会根据compatible属性选择不同的驱动和初始化方式。标记为fsl,dpa-ethernet-init的接口,内核的以太网驱动会为其配置FMan,但不会创建标准的Linux网络设备(如eth0),而是将控制权留给用户空间(USDPAA)。

5.3 FMC配置:定义复杂的流量策略

对于用例2和3,我们需要告诉FMan如何分发数据包。这是通过一个运行在用户空间的工具fmc(Frame Manager Configuration)和两个XML文件(策略文件和配置文件)完成的。

  • 策略文件(Policy XML):定义高级的流量分类规则。例如,“所有目的端口为80的TCP流量发送到队列A”,“所有带VLAN标签100的流量发送到队列B”。
  • 配置文件(Configuration XML):定义具体的硬件资源映射。例如,将“队列A”绑定到QMan的哪个具体通道(Channel)和帧队列(Frame Queue)上,而这个帧队列又由哪个USDPAA线程来消费。

USDPAA应用在启动时,会调用usdpaa_netcfg_acquire(policy_path, config_path),解析这些XML文件,并与设备树信息结合,最终获取到它需要操作的帧队列ID、缓冲区池ID等关键资源句柄。这使得网络数据路径的配置变得非常灵活,可以通过修改XML文件来改变流量策略,而无需重新编译应用。

6. 系统调优与实战避坑指南

理论最终要服务于实践。在这一部分,我将分享几个在真实项目中积累的关键调优点和常见问题排查方法。

6.1 CPU隔离:为数据平面线程创造“净土”

在高性能数据平面应用中,我们经常希望某个CPU核心完全只运行我们的USDPAA线程,不被内核调度器打扰,也不处理其他中断。这能带来最稳定、最低延迟的性能。Linux内核参数isolcpus就是干这个的。

操作方法: 在内核启动参数(通常在U-Boot的bootargs环境变量或GRUB配置中)添加isolcpus=1,2,3。例如:

setenv bootargs 'console=ttyS0,115200 root=/dev/ram0 isolcpus=1,2,3'

这告诉内核,在默认情况下,不要将任何用户空间进程调度到CPU 1、2、3上。只有显式设置了亲和性的线程(比如我们通过pthread_setaffinity_np绑定的USDPAA线程)才能在这些核心上运行。

配套操作:中断绑定: 仅仅隔离CPU还不够,我们还需要防止外部设备中断打断我们的数据平面核心。使用smp_affinity将设备中断绑定到其他核心。

# 查看所有中断号 cat /proc/interrupts # 假设网络中断号是 150,将其绑定到CPU 0 echo 1 > /proc/irq/150/smp_affinity # 1 代表CPU0 (二进制 0001) # 对于多队列网卡,可以分别绑定不同的队列到不同的非隔离核心

特别注意:门户本身会产生中断(用于通知软件有事件完成)。这个中断必须绑定到使用该门户的线程所在的核心,否则性能会严重下降。通常USDPAA驱动或初始化代码会处理好这一点,但手动检查一下/proc/interrupts中类似“qman-portal”的中断亲和性是个好习惯。

6.2 性能问题排查清单

当你的USDPAA应用性能不达预期时,可以按照以下清单排查:

问题现象可能原因排查方法
吞吐量远低于理论值1. CPU亲和性未设置,缓存失效严重。
2. 门户中断被其他核心处理。
3. DMA内存分配未对齐,导致缓存行分裂。
4. 数据平面线程被其他进程或内核任务(如ksoftirqd)抢占。
1. 检查taskset -p <pid>或代码中的pthread_setaffinity_np
2. 检查/proc/irq/<portal_irq>/smp_affinity
3. 确保dma_mem_memalign使用缓存行大小对齐。
4. 检查isolcpus是否生效,并用top -H -p <pid>观察线程状态。
数据包丢失1. 缓冲区池(BMan)耗尽。
2. 帧队列(QMan)拥塞,入队失败。
3. 应用处理速度跟不上线速。
1. 检查BMan池的统计信息(如有API),增大池大小。
2. 检查QMan入队错误码,优化处理逻辑或增加队列深度。
3. 使用性能分析工具(如perf)定位代码热点,考虑优化算法或使用多线程并行处理。
应用启动失败,提示门户初始化失败1. 设备树中未正确标记fsl,usdpaa-portal
2. 另一个进程已占用了所有可用门户。
3. USDPAA内核驱动未加载或DMA内存初始化失败。
1. 检查设备树源文件(.dts)和编译后的二进制(.dtb)。
2. 检查/dev下是否有fsl-usdpaa-*设备节点,并用lsof查看是否被占用。
3. 检查dmesg内核日志,查看fsl_usdpaa_shmem驱动加载和内存预留信息。
内存分配失败 (dma_mem_memalign返回NULL)1. DMA内存池耗尽。
2. 请求的内存大小或对齐值不合理。
3. 未先调用dma_mem_setup()
1. 增大内核配置CONFIG_FSL_USDPAA_SHMEM的值并重新编译内核。
2. 检查分配大小,确保对齐值是2的幂且不超过页大小。
3. 确保初始化顺序正确。

6.3 一个真实的调试案例:幽灵般的性能抖动

在一次压力测试中,我们发现一个USDPAA转发线程的延迟偶尔会出现几十微秒的尖峰,极不规则。排查了所有上述清单项均无果。最终使用perf进行采样分析,发现性能抖动时,总伴随着大量的sched_yield系统调用。

根源:在开发初期,为了“礼貌”地让出CPU,我们在处理循环中不小心加入了一个条件判断,当队列为空时调用了usleep(1)。但��高负载下,队列很少空,这个调用本应极少触发。然而,Linux的usleep在某些高精度定时器配置下,可能会退化为sched_yield。就是这个不经意的“让出”,导致线程被重新调度,虽然很快又回到原核心,但上下文切换和可能的缓存污染足以引起可观的延迟抖动。

解决:将usleep替换为纯忙等待或更高效的无锁检查(如__builtin_ia32_pause指令的封装),或者直接采用阻塞式从门户接收消息的API(如qman_portal_poll的超时等待模式),由硬件中断来唤醒线程,彻底消除了非确定性的主动调度。

这个案例告诉我们,在用户空间数据平面编程中,对任何可能引起阻塞或调度的系统调用都要保持高度警惕。性能分析工具是你的好朋友。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 10:25:16

智慧树刷课插件:让在线学习自动化的智能助手

智慧树刷课插件&#xff1a;让在线学习自动化的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件&#xff0c;自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 你是否曾经坐在电脑前&#xff0c;盯着智慧树平台上的视频课程…

作者头像 李华
网站建设 2026/6/17 10:23:48

终极指南:Rescuezilla - 免费图形化系统恢复工具完全掌握

终极指南&#xff1a;Rescuezilla - 免费图形化系统恢复工具完全掌握 【免费下载链接】rescuezilla The Swiss Army Knife of System Recovery 项目地址: https://gitcode.com/gh_mirrors/re/rescuezilla 你是否曾因系统崩溃而手足无措&#xff1f;是否在为硬盘升级时的…

作者头像 李华
网站建设 2026/6/17 10:22:27

重定向/管道符/通配符/转义字符/VI/VIM

数据流- 标准输入&#xff08;standard input&#xff0c;简称stdin&#xff09;&#xff1a;默认情况下&#xff0c;标准输入指从键盘获取的输入- 标准输出&#xff08;standard output&#xff0c;简称stdout&#xff09;&#xff1a;默认情况下&#xff0c;命令执行所回传正…

作者头像 李华
网站建设 2026/6/17 10:21:29

数组相关知识点(四)

一、字符指针变量在指针的类型中我们知道有一种指针类型为字符指针char*int main() {char ch w;char *pc &ch;*pc w;return 0; }二、数组指针变量2.1 数组指针变量整形指针变量&#xff1a; int * pint; 存放的是整形变量的地址&#xff0c;能够指向整形数据的指针。 浮…

作者头像 李华
网站建设 2026/6/17 10:15:08

计算机毕业设计之jsp大学生家教平台的设计与实现

随着计算机技术&#xff0c;网络技术的迅猛发展&#xff0c;Internet 的不断普及&#xff0c;网络在各个领域里发挥了越来越重要的作用。特别是随着近年人民生活水平不断提高&#xff0c;大学生家教平台的设计与实现给家教平台的业务带来了更大的发展机遇。在经济快速发展的带动…

作者头像 李华