当前位置: 首页 > news >正文

Linux学习笔记--中断子系统

struct gpio_key {int gpio;           // GPIO 引脚号int irq;            // 中断号  enum of_gpio_flags flag; // GPIO 标志(如上下拉)
};

中断处理函数

static irqreturn_t gpio_key_irq_100ask(int irq, void *dev_id)
{// 当按键触发中断时,打印按键状态printk("key %d val %d\n", irq, gpio_get_value(gpio_key->gpio));return IRQ_HANDLED;
}

probe 函数

count = of_gpio_count(node);  // 获取GPIO数量
gpio_keys = kzalloc(count * sizeof(struct gpio_key), GFP_KERNEL);for (i = 0; i < count; i++) {gpio = of_get_gpio_flags(node, i, &flags);  // 获取GPIO号和标志irq = gpio_to_irq(gpio);                    // GPIO转中断号// 注册中断处理函数,双边沿触发(按下和释放都触发)request_irq(irq, gpio_key_irq_100ask, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys[i]);
}

获取 GPIO 数量

count = of_gpio_count(node);

作用:从设备树节点中统计 GPIO 属性的数量。

设备树

gpio_keys {compatible = "100ask,gpio_key";gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>,    /* 第0个GPIO */<&gpio0 13 GPIO_ACTIVE_HIGH>,    /* 第1个GPIO */ <&gpio0 14 GPIO_ACTIVE_LOW>;     /* 第2个GPIO */
};

of_gpio_count() 会返回 3,因为有3个GPIO定义。

分配内存

gpio_keys = kzalloc(count * sizeof(struct gpio_key), GFP_KERNEL);

作用:为所有 GPIO 按键分配连续的内存空间。

内存布局

gpio_keys指针↓
[struct gpio_key]  ← gpio_keys[0] (第1个按键)
[struct gpio_key]  ← gpio_keys[1] (第2个按键)  
[struct gpio_key]  ← gpio_keys[2] (第3个按键)...
  • kzalloc() 分配并清零内存

  • GFP_KERNEL 表示在内核空间分配内存

循环处理每个 GPIO

for (i = 0; i < count; i++) {gpio = of_get_gpio_flags(node, i, &flags);irq = gpio_to_irq(gpio);// 保存信息和注册中断
}

获取 GPIO 信息和标志

gpio = of_get_gpio_flags(node, i, &flags);

参数

  • node:设备树节点

  • i:GPIO 的索引(0, 1, 2...)

  • &flags:输出参数,获取 GPIO 标志

返回值:GPIO 编号,用于后续的 GPIO 操作

GPIO 标志可能包括:

  • GPIO_ACTIVE_HIGH:高电平有效

  • GPIO_ACTIVE_LOW:低电平有效

  • GPIO_PULL_UP:上拉

  • GPIO_PULL_DOWN:下拉

GPIO 转中断号

irq = gpio_to_irq(gpio);

作用:将 GPIO 引脚号转换为对应的中断号。

原理:在 Linux 中,每个 GPIO 都可以作为中断源,内核提供了 GPIO 号到中断号的映射。

注册中断处理函数

request_irq(irq, gpio_key_irq_100ask, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys[i]);

参数详解

参数说明
irq中断号
gpio_key_irq_100ask中断处理函数指针
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING双边沿触发 - 上升沿和下降沿都触发中断
"100ask_gpio_key"中断名称(用于 /proc/interrupts)
&gpio_keys[i]传递给中断处理函数的设备ID

中断触发方式说明

双边沿触发的作用

  • IRQF_TRIGGER_RISING:电压从低到高变化时触发(按键按下)

  • IRQF_TRIGGER_FALLING:电压从高到低变化时触发(按键释放)

这样无论是按下还是释放按键都会触发中断,可以检测完整的按键动作。

完整的数据流

  1. 设备树定义 → 2. 获取GPIO数量 → 3. 分配内存 → 4. 逐个配置GPIO → 5. 注册中断

实际执行示例

假设设备树定义如下:

gpio_keys {compatible = "100ask,gpio_key";gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>,<&gpio0 13 GPIO_ACTIVE_LOW>;
};

执行过程:

  1. count = 2 (2个GPIO)

  2. 分配 2 * sizeof(struct gpio_key) 的内存

  3. 循环2次:

    • i=0: GPIO12 → 中断号A → 注册中断A

    • i=1: GPIO13 → 中断号B → 注册中断B

这样当任意按键按下或释放时,都会调用 gpio_key_irq_100ask 函数,并通过 dev_id 参数知道是哪个按键触发的。

中断控制器

中断控制器芯片定义

static struct irq_chip virtual_intc_irq_chip = {.name = "100ask_virtual_intc",.irq_ack = virtual_intc_irq_ack,.irq_mask = virtual_intc_irq_mask,.irq_mask_ack = virtual_intc_irq_mask_ack,.irq_unmask = virtual_intc_irq_unmask,.irq_eoi = virtual_intc_irq_eoi,
};

中断域操作

static const struct irq_domain_ops virtual_intc_domain_ops = {.xlate = irq_domain_xlate_onetwocell,  // 解析设备树.map = virtual_intc_irq_map,           // 建立硬件中断到虚拟中断的映射
};

核心函数解析

1. 中断映射函数

static int virtual_intc_irq_map(struct irq_domain *h, unsigned int virq,irq_hw_number_t hw)
{irq_set_chip_data(virq, h->host_data);irq_set_chip_and_handler(virq, &virtual_intc_irq_chip, handle_edge_irq);return 0;
}
  • 建立硬件中断号到Linux虚拟中断号的映射

  • 设置中断芯片和处理函数(边沿触发)

2. 中断处理函数

static void virtual_intc_irq_handler(struct irq_desc *desc)
{struct irq_chip *chip = irq_desc_get_chip(desc);chained_irq_enter(chip, desc);  // 进入链式中断处理,屏蔽中断/* 分辨是哪个硬件中断 */int hwirq = virtual_intc_get_hwirq();  // 随机获取0-3的硬件中断号/* 调用对应的中断处理函数 */generic_handle_irq(irq_find_mapping(virtual_intc_domain, hwirq));chained_irq_exit(chip, desc);  // 退出链式中断处理,重新使能中断
}

3. 探测函数

static int virtual_intc_probe(struct platform_device *pdev)
{// 1. 获取父中断控制器的中断号int irq_to_parent = platform_get_irq(pdev, 0);// 2. 设置链式中断处理函数irq_set_chained_handler_and_data(irq_to_parent, virtual_intc_irq_handler, NULL);// 3. 分配4个连续的中断描述符int irq_base = irq_alloc_descs(-1, 0, 4, numa_node_id());// 4. 创建中断域virtual_intc_domain = irq_domain_add_legacy(np, 4, irq_base, 0,&virtual_intc_domain_ops, NULL);return 0;
}
1. 获取父中断控制器的中断号
int irq_to_parent = platform_get_irq(pdev, 0);

作用

  • 从设备树中获取虚拟中断控制器连接到父中断控制器(如GIC)的中断号

  • platform_get_irq(pdev, 0) 获取设备树中定义的第一个中断号

  • 这个中断号是虚拟中断控制器向父中断控制器"汇报"中断时使用的中断线

设备树示例

virtual_intc: virtual-interrupt-controller {compatible = "100ask,virtual_intc";interrupt-parent = <&gic>;  // 父控制器是GICinterrupts = <0 100 4>;     // 这是platform_get_irq(pdev, 0)获取的中断// 含义:SPI类型,中断号100,高电平触发
};
2. 设置链式中断处理函数
irq_set_chained_handler_and_data(irq_to_parent, virtual_intc_irq_handler, NULL);

作用

  • 建立中断处理链:当父中断控制器收到irq_to_parent中断时,会调用virtual_intc_irq_handler

  • 参数说明:

    • irq_to_parent:父中断控制器的中断号

    • virtual_intc_irq_handler:链式中断处理函数

    • NULL:传递给处理函数的私有数据(这里不需要)

中断处理流程

父中断控制器(GIC)收到中断↓
调用 irq_desc[irq_to_parent].handle_irq↓
执行 virtual_intc_irq_handler (因为设置了链式处理)↓
虚拟中断控制器处理具体的中断源
3. 分配中断描述符
int irq_base = irq_alloc_descs(-1, 0, 4, numa_node_id());

作用

  • 为虚拟中断控制器的4个硬件中断源分配连续的Linux虚拟中断号

参数详解

  • -1:起始中断号,-1表示由内核自动分配

  • 0:要分配的硬件中断号的起始值(这里从0开始)

  • 4:要分配的中断数量(虚拟中断控制器有4个中断源)

  • numa_node_id():当前NUMA节点ID

分配结果示例

假设 irq_base = 100
那么分配的中断号范围是:100, 101, 102, 103
对应硬件中断号:0, 1, 2, 3
4. 创建中断域
virtual_intc_domain = irq_domain_add_legacy(np, 4, irq_base, 0,&virtual_intc_domain_ops, NULL);

作用

  • 创建并注册一个中断域,建立硬件中断号与Linux虚拟中断号的映射关系

参数详解

  • np:设备节点指针

  • 4:中断域包含的中断数量

  • irq_base:分配的起始虚拟中断号

  • 0:硬件中断号的起始值

  • &virtual_intc_domain_ops:中断域操作函数集

  • NULL:宿主数据

建立的映射关系

硬件中断号  →  Linux虚拟中断号0      →      irq_base + 01      →      irq_base + 1  2      →      irq_base + 23      →      irq_base + 3
完整的中断处理流程
// 当硬件中断发生时:
1. 父中断控制器(GIC)收到中断信号
2. 调用 virtual_intc_irq_handler
3. virtual_intc_irq_handler 中:- 调用 virtual_intc_get_hwirq() 获取具体硬件中断号(0-3)- 通过 irq_find_mapping(virtual_intc_domain, hwirq) 找到对应的虚拟中断号- 调用 generic_handle_irq() 执行该虚拟中断号的处理函数
4. 最终调用到设备驱动中注册的中断处理函数

这个probe函数完成了虚拟中断控制器的完整初始化:

  1. 建立与父控制器的连接 - 获取父中断号

  2. 注册中断处理机制 - 设置链式中断处理函数

  3. 分配系统资源 - 分配虚拟中断号范围

  4. 建立映射关系 - 创建中断域,连接硬件中断号与虚拟中断号

这样,当虚拟中断控制器产生中断时,就能正确地通过父中断控制器传递到Linux内核的中断处理子系统,最终调用到相应设备驱动的中断处理函数。

工作原理

  1. 层次结构:这个虚拟中断控制器作为子中断控制器连接到父中断控制器(如GIC)

  2. 中断流程

    • 父中断控制器收到中断

    • 调用virtual_intc_irq_handler

    • 随机选择一个硬件中断号(0-3)

    • 通过中断域找到对应的虚拟中断号

    • 调用该虚拟中断号的处理函数

  3. 中断管理

    • 使用chained_irq_enter/exit来管理中断屏蔽状态

    • 支持中断的确认、屏蔽、取消屏蔽等操作

GIC中的重要函数和结构体

沿着中断的处理流程,GIC涉及这4个重要部分:

  • CPU从异常向量表中调用handle_arch_irq,这个函数指针是有GIC驱动设置的

    • GIC才知道怎么判断发生的是哪个GIC中断

  • 从GIC获得hwirq后,要转换为virq:需要有GIC Domain

  • 调用irq_desc[virq].handle_irq函数:这也应该由GIC驱动提供

  • 处理中断时,要屏蔽中断、清除中断等:这些函数保存在irq_chip里,由GIC驱动提供

从硬件上看,GIC的功能是什么?

  • 可以使能、屏蔽中断

  • 发生中断时,可以从GIC里判断是哪个中断

在内核里,使用gic_chip_data结构体表示GIC,gic_chip_data里有什么?

  • irq_chip:中断使能、屏蔽、清除,放在irq_chip中的各个函数里实现

irq_domain

    • 申请中断时

      • 在设备树里指定hwirq、flag,可以使用irq_domain的函数来解析设备树

      • 根据hwirq可以分配virq,把(hwirq, virq)存入irq_domain中

    • 发生中断时,从GIC读出hwirq,可以通过irq_domain找到virq,从而找到处理函数

所以,GIC用gic_chip_data来表示,gic_chip_data中重要的成员是:irq_chip、irq_domain。

在Linux内核中,gic_chip_data 结构体用于描述通用中断控制器(GIC)的硬件信息和状态,它是GIC驱动的核心数据结构

成员名功能描述
versionGIC控制器的硬件版本号
irq_base该GIC控制器所管理的虚拟中断号(virq)的起始编号
irq_nr该GIC控制器管理的中断数量
dist_base分发器(Distributor)寄存器区的内核虚拟地址
cpu_baseCPU接口(CPU Interface)寄存器区的内核虚拟地址
irq_chip指向irq_chip结构体的指针,包含对GIC的底层硬件操作函数
irq_domain指向irq_domain结构体的指针,负责硬件中断号(hwirq)虚拟中断号(virq)映射

在表格列出的成员中,有几个扮演着尤为重要的角色:

  • 寄存器基地址 (dist_base 与 cpu_base)
    GIC硬件主要由两大模块组成:分发器(Distributor) 和 CPU接口(CPU Interface)dist_base 和 cpu_base 分别是这两个模块寄存器在内核地址空间中的映射地址。驱动通过操作这些地址的寄存器,来完成中断的使能、配置优先级以及应答等具体硬件操作。

  • 中断控制核心 (irq_chip 与 irq_domain)
    这是gic_chip_data与Linux内核中断子系统交互的关键。

    • irq_chip:它包含了一组函数指针,定义了如何操作GIC控制器本身,例如屏蔽一个中断(irq_mask)、应答一个中断(irq_ack)、设置中断触发类型(irq_set_type)等。你可以把它理解为GIC的"设备驱动程序"。

    • irq_domain:它的核心作用是建立硬件中断号(hwirq) 和Linux内核内部使用的虚拟中断号(virq) 之间的映射关系。当设备树被解析时,正是通过irq_domain来找到或分配对应的虚拟中断号,从而让驱动程序可以使用request_irq来注册中断处理函数。

 GIC驱动的初始化

了解gic_chip_data是如何被创建和初始化的,有助于更深入地理解其作用。这个过程主要由 gic_of_init 驱动初始化函数完成,一般包括以下步骤:

  1. 分配结构体:内核为gic_chip_data分配内存空间。

  2. 映射寄存器:解析设备树,将GIC的分发器CPU接口的物理地址映射到内核虚拟地址空间,并赋值给dist_basecpu_base成员。

  3. 初始化irq_chip:准备好一个irq_chip结构体实例,填充其各类操作函数(如mask、unmask、ack等),并将gic_chip_data中的irq_chip指针指向它。

  4. 创建irq_domain:创建一个irq_domain,并设置好其操作函数集(irq_domain_ops),特别是用于翻译设备树中断信息的xlate函数和建立映射的map函数。然后将其赋值给gic_chip_datairq_domain成员。

中断域操作结构体

static const struct irq_domain_ops virtual_intc_domain_ops = {.translate = virtual_intc_domain_translate,  // 翻译设备树中断说明符.alloc = virtual_intc_domain_alloc,          // 分配和设置中断描述符
};

中断芯片操作

static struct irq_chip virtual_intc_chip = {.name = "virtual_intc",.irq_mask = virtual_intc_irq_mask,.irq_unmask = virtual_intc_irq_unmask,.irq_eoi = virtual_intc_irq_eoi,
};

设备树翻译函数

static int virtual_intc_domain_translate(struct irq_domain *d,struct irq_fwspec *fwspec,unsigned long *hwirq,unsigned int *type)
{if (is_of_node(fwspec->fwnode)) {if (fwspec->param_count != 2)return -EINVAL;*hwirq = fwspec->param[0];  // 硬件中断号*type = fwspec->param[1];   // 中断触发类型return 0;}return -EINVAL;
}

作用:从设备树的中断说明符中提取硬件中断号和触发类型。

设备树示例

device_node {interrupts = <0 4>;  // hwirq=0, type=4 (IRQ_TYPE_LEVEL_HIGH)
};

中断分配函数

static int virtual_intc_domain_alloc(struct irq_domain *domain,unsigned int virq, unsigned int nr_irqs, void *data)
{struct irq_fwspec *fwspec = data;struct irq_fwspec parent_fwspec;irq_hw_number_t hwirq;int i;/* 1. 设置当前中断域的中断描述符 */hwirq = fwspec->param[0];for (i = 0; i < nr_irqs; i++)irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,&virtual_intc_chip, NULL);/* 2. 设置父中断域的中断 */parent_fwspec.fwnode = domain->parent->fwnode;parent_fwspec.param_count = 3;parent_fwspec.param[0] = 0;                     // GIC_SPI类型parent_fwspec.param[1] = hwirq + upper_hwirq_base; // 父中断号parent_fwspec.param[2] = fwspec->param[1];      // 触发类型return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec);
}

作用

  • 步骤1:为每个虚拟中断号设置硬件中断号和中断芯片

  • 步骤2:向父中断控制器申请对应的中断资源

中断芯片操作函数

static void virtual_intc_irq_unmask(struct irq_data *d)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);irq_chip_unmask_parent(d);  // 调用父控制器的unmask
}static void virtual_intc_irq_mask(struct irq_data *d)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);irq_chip_mask_parent(d);    // 调用父控制器的mask
}static void virtual_intc_irq_eoi(struct irq_data *d)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);irq_chip_eoi_parent(d);     // 调用父控制器的EOI
}

特点:所有操作都委托给父中断控制器,体现了层次化设计

探测函数

static int virtual_intc_probe(struct platform_device *pdev)
{struct irq_domain *parent_domain;struct device_node *parent;int err;/* 1. 读取设备树中的upper_hwirq_base属性 */err = of_property_read_u32(pdev->dev.of_node, "upper_hwirq_base", &upper_hwirq_base);/* 2. 找到父中断控制器和父中断域 */parent = of_irq_find_parent(pdev->dev.of_node);parent_domain = irq_find_host(parent);/* 3. 创建层次化中断域 */virtual_intc_domain = irq_domain_add_hierarchy(parent_domain, 0, 4,pdev->dev.of_node, &virtual_intc_domain_ops, NULL);return 0;
}

层次化中断映射机制

映射关系:

设备驱动
↓ 使用虚拟中断号
虚拟中断控制器 (virtual_intc)
↓ 硬件中断号 0-3
父中断控制器 (如GIC)
↓ 硬件中断号 (upper_hwirq_base + 0) 到 (upper_hwirq_base + 3)
硬件中断线

设备树配置示例:

virtual_intc: virtual-interrupt-controller {compatible = "100ask,virtual_intc";interrupt-parent = <&gic>;           // 父控制器是GICupper_hwirq_base = <100>;            // 在GIC中的起始中断号#interrupt-cells = <2>;              // 硬件中断号 + 触发类型
};my_device {compatible = "100ask,my_device";interrupt-parent = <&virtual_intc>;  // 使用虚拟中断控制器interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; // 硬件中断号1,高电平触发
};

中断处理流程

当硬件中断发生时:

  1. GIC收到中断:中断号 = upper_hwirq_base + hwirq

  2. GIC调用虚拟中断控制器的处理函数(自动设置)

  3. 虚拟中断控制器处理

    • 调用virtual_intc_irq_mask(委托给GIC)

    • 调用设备驱动注册的中断处理函数

    • 调用virtual_intc_irq_eoi(委托给GIC)

与传统方法的区别

方面传统方法 (irq_domain_add_legacy)层次化方法 (irq_domain_add_hierarchy)
中断映射手动建立映射自动通过设备树建立
父中断处理需要手动设置链式处理函数自动委托给父控制器
代码复杂度较高,需要管理链式中断较低,内核自动处理层次关系
适用场景传统、简单的中断控制器现代、复杂的中断控制器层次结构

Linux内核中层次化中断控制器的现代实现方式:

  1. 自动层次管理:通过irq_domain_add_hierarchy自动建立父子关系

  2. 操作委托:所有中断操作自动委托给父中断控制器

  3. 设备树驱动:完全通过设备树配置中断映射

  4. 简化开发:无需手动处理链式中断和中断屏蔽逻辑

这种方式更适合复杂的SoC设计,其中多个中断控制器可能形成层次结构。

在层次化中断控制器架构中调用父级的chip函数是非常重要的设计,主要原因如下:

1. 硬件操作的必要性

实际硬件控制

  • 虚拟中断控制器只是软件抽象,没有实际的硬件寄存器

  • 父中断控制器(如GIC) 才是真正控制物理中断线的硬件

  • 所有对中断的使能、禁用、确认等操作最终都需要作用于物理硬件

// 这些操作最终都要落实到GIC的硬件寄存器
static void virtual_intc_irq_mask(struct irq_data *d)
{printk("虚拟控制器执行mask\n");irq_chip_mask_parent(d);  // 实际调用GIC的mask函数来操作硬件
}

中断状态管理

状态一致性

父中断控制器需要维护准确的中断状态:

操作父控制器需要知道的原因
Mask记录哪些中断被禁用,避免错误处理
Unmask知道哪些中断已启用,可以正常响应
EOI确认中断处理完成,可以接受新中断

中断流控(Flow Control)

电平触发中断的特别需求

对于电平触发中断,处理流程必须严格:

中断发生↓
GIC检测到高电平,触发中断↓
调用虚拟控制器,虚拟控制器mask中断↓
执行设备驱动中断处理函数↓
设备清除中断源(电平变低)↓
虚拟控制器unmask中断↓
GIC可以再次检测中断

如果不调用父级的mask

  • 电平保持高电平期间,GIC会持续触发中断

  • 导致中断风暴,系统崩溃

层次化中断处理流程

完整的中断处理链条

// 当中断发生时:
1. GIC硬件检测到中断
2. GIC调用 irq_desc[parent_irq].handle_irq
3. 自动调用虚拟控制器的处理函数(内核自动设置)
4. 虚拟控制器处理:- 调用 irq_chip_mask_parent()  // 屏蔽GIC中的中断- 调用设备驱动的中断处理函数- 调用 irq_chip_eoi_parent()   // 通知GIC中断处理完成

具体场景分析

场景1:中断屏蔽

// 设备驱动调用 disable_irq(irq)
disable_irq(irq)↓
调用 virtual_intc_irq_mask(irq_data)↓
调用 irq_chip_mask_parent(irq_data)  // 调用GIC的mask函数↓
GIC操作硬件寄存器,真正屏蔽中断线

场景2:中断处理完成

// 中断处理完成后
中断处理函数返回↓
调用 virtual_intc_irq_eoi(irq_data)↓
调用 irq_chip_eoi_parent(irq_data)  // 调用GIC的EOI函数↓
GIC硬件确认中断处理完成,可以接受新中断

软件架构优势

责任分离

组件职责
虚拟中断控制器中断映射、多路复用、软件逻辑
父中断控制器硬件操作、状态管理、物理控制

GIC的特殊要求

一些中断控制器(如GIC)有严格的编程模型要求:

// GICv2的典型操作序列
gic_mask_irq(irq);      // 屏蔽中断
handle_irq_event();     // 处理中断
gic_eoi_irq(irq);       // 结束中断// 如果跳过任何步骤,GIC可能进入错误状态

调用父级chip函数的主要原因:

  1. 硬件操作:只有父控制器能操作物理硬件寄存器

  2. 状态一致性:父控制器需要准确的中断状态信息

  3. 流控要求:电平触发中断需要严格的mask/unmask序列

  4. 架构设计:层次化中断控制器的标准模式

  5. 代码质量:复用稳定代码,避免重复实现

这种设计确保了虚拟中断控制器能够正确集成到Linux内核的中断子系统中,同时保持与真实硬件的正确交互。

http://www.dtcms.com/a/546522.html

相关文章:

  • Selenium常用方法
  • 哈尔滨网站建设推广服务人力资源网站
  • screen命令指南
  • SAP-ABAP:穿越时空的ABAP基石:深入理解WRITE语句的奥秘与技巧实例详解
  • 做网站产品图片素材前端后端分别是什么意思
  • 做网站销售的昆明百度小程序
  • LeeCode 137. 只出现一次的数字II
  • AOI设备在消费电子领域的检测应用
  • 网站制作 成都土巴兔官网
  • 如何 做网站跳转建设网站企业网上银行
  • 基于需求驱动的自动驾驶感知任务数据集缺口识别与缓解方法
  • 上海文明城市建设网站如何做网站商铺
  • 怎么自己给自己的网站做推广做旅游网站毕设任务书
  • 丹阳网站建设价位网站建设的中期目标
  • html 网站链接cms编码是什么
  • 【原理揭秘】Nginx 匹配规则优先级详解
  • DeepSeek正在探索一种可能显著提升AI“记忆力”的新方法:用图像而非传统的文本token来存储信息
  • 网站数据库怎么备份网站跟换域名
  • 深入理解 Rust 的 VecDeque:环形缓冲区的高效设计与实践
  • wordpress只能本地访问网站优化公司多少钱
  • 网站二级域名周村区建设网站
  • 广告设计公司网站源码登陆网站空间
  • 网站开发用什么编程网站建设吸引人的话语
  • 郑州网站搜索排名网站怎么做悬浮图片放大
  • 仓颉言 Stack 栈的实现细节:从底层设计到性能优化
  • 黑五手表网站东莞网络推广网站
  • 杭州公积金网站查询系统网站建设用什么软件
  • 人人设计网官方网站来一个地址你们知道的
  • 迪杰斯特拉(dijkstra)算法
  • 交三百能在网站上找兼职做的it运维工作总结