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

【设备树笔记整理6】中断系统中的设备树

1 中断概念的引入与处理流程

1.1 中断处理框图

1.2 中断程序的使用

主函数()
while(1)
{do_routine_task();
}中断处理函数()  
{handle_interrupt_task();
}

如何调用中断处理函数?

1.3 ARM对异常(中断)的处理过程

(1)初始化

① 设置中断源,让它可以产生中断

② 设置中断控制器(设置屏蔽,设置优先级)

③ 设置CPU总开关(使能中断)

(2)执行常规程序(主函数)

(3)产生中断:按下按键 —> 中断控制器 —> CPU

(4)CPU每执行完一条指令,都会检查有无中断,异常发生

(5)发现有异常/中断发生,开始处理:对于不同的异常,跳去不同的地址执行程序。

这些地址上,只是一条跳转指令,跳去执行某个函数。

(6)这些函数执行以下工作:

2 Linux对中断处理的框架及代码流程简述  

2.1 中断处理中汇编相关的部分

2.1.1 异常向量入口: arch\arm\kernel\entry-armv.S

    .section .vectors, "ax", %progbits
.L__vectors_start:W(b)    vector_rstW(b)    vector_undW(ldr)  pc, .L__vectors_start + 0x1000W(b)    vector_pabtW(b)    vector_dabtW(b)    vector_addrexcptnW(b)    vector_irq                       // => 中断向量入口W(b)    vector_fiq

2.1.2 中断向量: vector_irq

/** Interrupt dispatcher*/vector_stub irq, IRQ_MODE, 4   // 相当于 vector_irq: ..., // 它会根据SPSR寄存器的值,// 判断被中断时CPU是处于USR状态还是SVC状态, // 然后调用下面的__irq_usr或__irq_svc.long   __irq_usr               @  0  (USR_26 / USR_32).long   __irq_invalid           @  1  (FIQ_26 / FIQ_32).long   __irq_invalid           @  2  (IRQ_26 / IRQ_32).long   __irq_svc               @  3  (SVC_26 / SVC_32).long   __irq_invalid           @  4.long   __irq_invalid           @  5.long   __irq_invalid           @  6.long   __irq_invalid           @  7.long   __irq_invalid           @  8.long   __irq_invalid           @  9.long   __irq_invalid           @  a.long   __irq_invalid           @  b.long   __irq_invalid           @  c.long   __irq_invalid           @  d.long   __irq_invalid           @  e.long   __irq_invalid           @  f

2.1.3 __irq_usr/__irq_svc

        这2个函数的处理过程类似:

  • 保存现场
  • 调用 irq_handler
  • 恢复现场

2.1.4 irq_handler: 将会调用C函数 handle_arch_irq 

    .macro  irq_handler
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLERldr r1, =handle_arch_irqmov r0, spbadr    lr, 9997fldr pc, [r1]
#elsearch_irq_handler_default
#endif
9997:.endm

2.1.5 handle_arch_irq的处理过程

  • 读取寄存器获得中断信息: hwirq
  • 把hwirq转换为virq
  • 调用 irq_desc[virq].handle_irq

[注意] 对于S3C2440,s3c24xx_handle_irq 是用于处理中断的C语言入口函数 

2.2 中断处理流程总结

(1)中断处理流程:
假设中断结构如下:
sub int controller ---> int controller ---> cpu

(2)发生中断时,
cpu跳到"vector_irq", 保存现场, 调用C函数handle_arch_irq

(3)handle_arch_irq:

① 读 int controller, 得到hwirq
② 根据hwirq得到virq
③ 调用 irq_desc[virq].handle_irq

(4)如果该中断没有子中断, irq_desc[virq].handle_irq的操作:

① 取出irq_desc[virq].action链表中的每一个handler, 执行它

② 使用irq_desc[virq].irq_data.chip的函数清中断

(5)如果该中断是由子中断产生, irq_desc[virq].handle_irq的操作:

① 读 sub int controller, 得到hwirq'

② 根据hwirq'得到virq

③ 调用 irq_desc[virq].handle_irq

2.3 图示

(1)CPU中断号(硬件中断号)

(2)硬件中断号转换到虚拟中断号的过程

(3)中断函数调用过程

3 中断号的演变与irq_domain 

        以前中断号(virq)跟硬件密切相关,现在的趋势是中断号跟硬件无关,仅仅是一个标号而已。 

3.1 以前的中断号映射方法

        以前,对于每一个硬件中断(hwirq)都预先确定它的中断号(virq),这些中断号一般都写在一个头文件里,比如arch\arm\mach-s3c24xx\include\mach\irqs.h,使用时:

(1)执行 request_irq(virq, my_handler) :

        内核根据virq可以知道对应的硬件中断,然后去设置、使能中断等。

(2)发生硬件中断时:

        内核读取硬件信息,确定hwirq,反算出virq,然后调用 irq_desc[virq].handle_irq, 最终会用到my_handler。

怎么根据hwirq计算出virq?   

        硬件上有多个intc(中断控制器),对于同一个hwirq数值,会对应不同的virq。所以在讲hwirq时,应该强调"是哪一个intc的hwirq",在描述hwirq转换为virq时,引入一个概念:irq_domain:域,在这个域里hwirq转换为某一个virq。 

3.2 新的中断号映射方法

(1)当中断控制器越来越多、当中断越来越多,上述方法(virq和hwirq固定绑定)有缺陷:

  • 增加工作量, 你需要给每一个中断确定它的中断号, 写出对应的宏, 可能有成百上千个
  • 你要确保每一个硬件中断对应的中断号互不重复

(2)有什么方法改进

① hwirq跟virq之间不再绑定

② 要使用某个hwirq时,先在irq_desc数组中找到一个空闲项, 它的位置就是virq,再在irq_desc[virq]中放置处理函数。

(3)新中断体系中, 怎么使用中断:

① 以前是request_irq发起,现在是先在设备树文件中声明想使用哪一个中断(哪一个中断控制器下的哪一个中断)

② 内核解析设备树时,会根据"中断控制器"确定irq_domain,根据"哪一个中断"确定hwirq,然后在irq_desc数组中找出一个空闲项,它的位置就是virq。并且把virq和hwirq的关系保存在irq_domain中:irq_domain.linear_revmap[hwirq] = virq;

③ 驱动程序 request_irq(virq, my_handler)

④ 发生硬件中断时,内核读取硬件信息,确定hwirq,确定 virq = irq_domain.linear_revmap[hwirq];然后调用 irq_desc[virq].handle_irq,最终会用到my_handler

(4)假设要使用子中断控制器(subintc)的n号中断, 它发生时会导致父中断控制器(intc)的m号中断:

① 设备树表明要使用<subintc n>,subintc表示要使用<intc m>

② 解析设备树时,会为<subintc n>找到空闲项 irq_desc[virq'],sub irq_domain.linear_revmap[n] = virq';会为<intc m>   找到空闲项 irq_desc[virq],irq_domain.linear_revmap[m] = virq;并且设置它的handle_irq为某个分析函数demux_func。

③ 驱动程序 request_irq(virq', my_handler)

④ 发生硬件中断时,内核读取intc硬件信息,确定hwirq = m,确定 virq = irq_domain.linear_revmap[m];然后调用 irq_desc[m].handle_irq, 即demux_func

⑤ demux_func:读取sub intc硬件信息,确定hwirq = n,确定 virq' =  sub irq_domain.linear_revmap[n];然后调用 irq_desc[n].handle_irq, 即my_handler

补充:图示

(1)触发INT4_7中断中的EINT4子中断

 (2)使用子中断EINT4的过程

 (3)注册的中断处理函数(保存在irq_desc数组中)

 (4)S3C2440中的子中断

4 示例_在S3C2440上使用设备树描述中断体验

4.1 笔记 

        所用文件在: doc_and_sources_for_device_tree\source_and_images\第5,6课的源码及映像文件(使用了完全版的设备树)\内核补丁及设备树。

先解压原始内核(source_and_images\kernel):

tar xzf linux-4.19-rc3.tar.gz

打上补丁:

cd linux-4.19-rc3
patch -p1 < ../linux-4.19-rc3_device_tree_for_irq_jz2440.patch

在内核目录下执行:

export PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bincp config_ok  .config
make uImage   // 生成 arch/arm/boot/uImage
make dtbs     // 生成 arch/arm/boot/dts/jz2440_irq.dtb

老内核:

/ # cat /proc/interruptsCPU029:      17593       s3c  13 Edge      samsung_time_irq42:          0       s3c  26 Edge      ohci_hcd:usb143:          0       s3c  27 Edge      s3c2440-i2c.074:         86  s3c-level   0 Edge      s3c2440-uart75:        561  s3c-level   1 Edge      s3c2440-uart83:          0  s3c-level   9 Edge      ts_pen84:          0  s3c-level  10 Edge      adc87:          0  s3c-level  13 Edge      s3c2410-wdt

新内核:

nfs 30000000 192.168.1.124:/work/nfs_root/uImage; 
nfs 32000000 192.168.1.124:/work/nfs_root/jz2440_irq.dtb; 
bootm 30000000 - 32000000
/ # cat /proc/interruptsCPU08:          0       s3c   8 Edge      s3c2410-rtc tick13:        936       s3c  13 Edge      samsung_time_irq30:          0       s3c  30 Edge      s3c2410-rtc alarm32:         15  s3c-level  32 Level     50000000.serial33:         60  s3c-level  33 Level     50000000.serial59:          0  s3c-level  59 Edge      53000000.watchdog

(1)某个设备要使用中断, 需要在设备树中描述中断, 如何?

        它要用哪一个中断?这个中断连接到哪一个中断控制器去?即: 使用哪一个中断控制器的哪一个中断:至少有有2个属性:

interrupts        // 表示要使用哪一个中断, 中断的触发类型等
interrupt-parent  // 这个中断要接到哪一个设备去? 即父中断控制器是谁

(2)上述的interrupts属性用多少个u32来表示?

        这应该由它的父中断控制器来描述,在父中断控制器中, 至少有2个属性:

interrupt-controller;   // 表示自己是一个中断控制器
#interrupt-cells        // 表示自己的子设备里应该有几个U32的数据来描述中断

4.2 图示

5 示例_使用设备树描述按键中断 

5.1 笔记

        在设备树的设备节点中描述“中断的硬件信息”,表明使用了”哪一个中断控制器里的哪一个中断,及中断触发方式“,设备节点会被转换为 platform_device,“中断的硬件信息”会转换为"中断号",保存在platform_device的"中断资源"里,驱动程序从platform_device的"中断资源"取出中断号,就可以request_irq了。

实验:

(1)把"002th_buttons_drv/jz2440_irq.dts" 放入内核 arch/arm/boot/dts目录,在内核根目录下执行:

make dtbs   // 得到 arch/arm/boot/dts/jz2440_irq.dtb

使用上节视频的uImage或这个jz2440_irq.dtb启动内核。

(2)编译、测试驱动:

① 把 002th_buttons_drv 上传到ubuntu

② 编译驱动:

export PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bincd 002th_buttons_drv
make   // 得到 buttons.ko

③ 编译测试程序:

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/4.3.2/bincd 002th_buttons_drvarm-linux-gcc -o buttons_test  buttons_test.c

④ 测试:

insmod buttons.ko
./buttons_test &
然后按键

5.2 图示 

(1)图1 

(2)图2

 (3)图3

 (4)图4

6 内核对设备树中断信息的处理过程

6.1 笔记

6.1.1 第一部分

        从硬件结构上看,处理过程分上下两个层面:中断控制器,使用中断的设备;从软件结构上看,处理过程分左右两个部分:在设备树中描述信息,在驱动中处理设备树。

(1)中断控制器:这又分为root irq controller,gpf 和 gpg irq controller

① root irq controller

  • 在设备树中的描述
  • 在内核中的驱动 

② 对于S3C2440, 还有: gpf/gpg irq controller

  • 在设备树中的描述(在pinctrl节点里)
  • 在内核中的驱动 (在pinctrl驱动中)

(2)设备的中断

  • 在设备节点中描述(表明使用"哪一个中断控制器里的哪一个中断, 及中断触发方式")
  • 在内核中的驱动 (在platform_driver.probe中获得IRQ资源, 即中断号)

6.1.2 irq_domain是核心

(1)每一个中断控制器都有一个irq_domain

(2)对设备中断信息的解析, 

① 需要调用 irq_domain->ops->xlate (即从设备树中获得hwirq,type)

② 获取未使用的virq,保存:irq_domain->linear_revmap[hwirq] = virq;

③ 在hwirq和virq之间建立联系:要调用 irq_domain->ops->map,比如根据hwirq的属性设置virq的中断处理函数(是一个分发函数还是可以直接处理中断)

irq_desc[virq].handle_irq = 常规函数;

如果这个hwirq有上一级中断,假设它的中断号为virq',还要设置: 

irq_desc[virq'].handle_irq = 中断分发函数;

6.1.3 s3c2440设备树中断相关代码调用关系

(1)上述处理过程如何触发?

① 内核启动时初始化中断的入口:

start_kernel // init/main.cinit_IRQ();if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)irqchip_init();   // 一般使用它elsemachine_desc->init_irq();

② 设备树中的中断控制器的处理入口:irqchip_init // drivers/irqchip/irqchip.c

    of_irq_init(__irqchip_of_table);  // 对设备树文件中每一个中断控制器节点, 调用对应的处理函数为每一个符合的"interrupt-controller"节点,分配一个of_intc_desc结构体, desc->irq_init_cb = match->data; // = IRQCHIP_DECLARE中传入的函数并调用处理函数(先调用root irq controller对应的函数, 再调用子控制器的函数, 再调用更下一级控制器的函数...)

(2)root irq controller 的驱动调用过程:

① 为root irq controller定义处理函数:

IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);  //drivers/irqchip/irq-s3c24xx.c

其中:

#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
#define _OF_DECLARE(table, name, compat, fn, fn_type)           \static const struct of_device_id __of_table_##name      \__used __section(__##table##_of_table)          \= { .compatible = compat,              \.data = (fn == (fn_type)NULL) ? fn : fn  }

展开为:

    static const struct of_device_id __of_table_s3c2410_irq     \__used __section("__irqchip_of_table")          \= { .compatible = "samsung,s3c2410-irq",               \.data = s3c2410_init_intc_of  }

它定义了一个of_device_id结构体, 段属性为"__irqchip_of_table", 在编译内核时这些段被放在__irqchip_of_table地址处。
即__irqchip_of_table起始地址处,
放置了一个或多个 of_device_id, 它含有compatible成员;
设备树中的设备节点含有compatible属性,
如果双方的compatible相同, 并且设备节点含有"interrupt-controller"属性,
则调用of_device_id中的函数来处理该设备节点。

所以: IRQCHIP_DECLARE 是用来声明设备树中的中断控制器的处理函数。

② root irq controller处理函数的执行过程:

s3c2410_init_intc_of  // drivers/irqchip/irq-s3c24xx.c// 初始化中断控制器: intc, subintcs3c_init_intc_of(np, interrupt_parent, s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));// 为中断控制器创建irq_domaindomain = irq_domain_add_linear(np, num_ctrl * 32,&s3c24xx_irq_ops_of, NULL);intc->domain = domain;// 设置handle_arch_irq, 即中断处理的C语言总入口函数set_handle_irq(s3c24xx_handle_irq);

 

(3) pinctrl系统中gpf/gpg irq controller的驱动调用过程:

pinctrl系统的驱动程序:

① 源代码: drivers/pinctrl/samsung/pinctrl-samsung.c

static struct platform_driver samsung_pinctrl_driver = {.probe      = samsung_pinctrl_probe,.driver = {.name   = "samsung-pinctrl",.of_match_table = samsung_pinctrl_dt_match, // 含有 { .compatible = "samsung,s3c2440-pinctrl", .data = &s3c2440_of_data },.suppress_bind_attrs = true,.pm = &samsung_pinctrl_pm_ops,},
};

② 设备树中:

pinctrl@56000000 {reg = <0x56000000 0x1000>;compatible = "samsung,s3c2440-pinctrl";  // 据此找到驱动

③ 驱动中的操作:

samsung_pinctrl_probe  // drivers/pinctrl/samsung/pinctrl-samsung.c

最终会调用到 s3c24xx_eint_init // drivers/pinctrl/samsung/pinctrl-s3c24xx.c

        // eint0,1,2,3的处理函数在处理root irq controller时已经设置; // 设置eint4_7, eint8_23的处理函数(它们是分发函数)for (i = 0; i < NUM_EINT_IRQ; ++i) {unsigned int irq;if (handlers[i]) /* add by weidongshan@qq.com, 不再设置eint0,1,2,3的处理函数 */{irq = irq_of_parse_and_map(eint_np, i);if (!irq) {dev_err(dev, "failed to get wakeup EINT IRQ %d\n", i);return -ENXIO;}eint_data->parents[i] = irq;irq_set_chained_handler_and_data(irq, handlers[i], eint_data);}}// 为GPF、GPG设置irq_domainfor (i = 0; i < d->nr_banks; ++i, ++bank) {ops = (bank->eint_offset == 0) ? &s3c24xx_gpf_irq_ops: &s3c24xx_gpg_irq_ops;bank->irq_domain = irq_domain_add_linear(bank->of_node, bank->nr_pins, ops, ddata);}

(4) 使用中断的驱动调用过程:

① 在设备节点中描述(表明使用"哪一个中断控制器里的哪一个中断,及中断触发方式”)比如:

    buttons {compatible = "jz2440_button";eint-pins  = <&gpf 0 0>, <&gpf 2 0>, <&gpg 3 0>, <&gpg 11 0>;interrupts-extended = <&intc 0 0 0 3>,<&intc 0 0 2 3>,<&gpg 3 3>,<&gpg 11 3>;};

② 设备节点会被转换为 platform_device

        "中断的硬件信息" 会转换为"中断号",保存在platform_device的"中断资源"里。device_node转换为platform_device, 讲解了设备树中设备节点转换为 platform_device 的过程,这里我们只关心里面对中断信息的处理:of_device_alloc (drivers/of/platform.c)

    dev = platform_device_alloc("", PLATFORM_DEVID_NONE);  // 分配 platform_devicenum_irq = of_irq_count(np);  // 计算中断数of_irq_to_resource_table(np, res, num_irq) // drivers/of/irq.c, 根据设备节点中的中断信息, 构造中断资源of_irq_to_resourceint irq = of_irq_get(dev, index);  // 获得virq, 中断号rc = of_irq_parse_one(dev, index, &oirq); // drivers/of/irq.c, 解析设备树中的中断信息, 保存在of_phandle_args结构体中domain = irq_find_host(oirq.np);   // 查找irq_domain, 每一个中断控制器都对应一个irq_domainirq_create_of_mapping(&oirq);             // kernel/irq/irqdomain.c, 创建virq和中断信息的映射irq_create_fwspec_mapping(&fwspec);irq_create_fwspec_mapping(&fwspec);irq_domain_translate(domain, fwspec, &hwirq, &type) // 调用irq_domain->ops->xlate, 把设备节点里的中断信息解析为hwirq, typevirq = irq_find_mapping(domain, hwirq); // 看看这个hwirq是否已经映射, 如果virq非0就直接返回virq = irq_create_mapping(domain, hwirq); // 否则创建映射virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), NULL);  // 返回未占用的virqirq_domain_associate(domain, virq, hwirq) // 调用irq_domain->ops->map(domain, virq, hwirq), 做必要的硬件设置

(5)驱动程序从platform_device的"中断资源"取出中断号, 就可以request_irq了

6.2 图示

(1)图1 

(2)图2 

(3)图3

后面稍微有点潦草了哈~~~ 

相关文章:

【设备树笔记整理6】中断系统中的设备树

1 中断概念的引入与处理流程 1.1 中断处理框图 1.2 中断程序的使用 主函数() while(1) {do_routine_task(); }中断处理函数() {handle_interrupt_task(); }如何调用中断处理函数&#xff1f; 1.3 ARM对异常(中断)的处理过程 &#xff08;1&#xff09;初始化 ① 设置中断…...

微信小程序下载后端返回的文件流

downtest() {let temp {"title": ["排名", "车号", "车队", "车手", "领航", "赛段成绩", "距首车成绩", "距前车差距", "发车时间", "冲刺时间", "赛段…...

Autoware.universe部署04:universe传感器ROS2驱动

文章目录 一、激光雷达驱动二、IMU驱动2.1 上位机配置4.2 IMU校准4.3 安装ROS驱动 三、CAN驱动四、相机驱动4.1 安装驱动4.2 修改相机参数 五、GNSS驱动 本文介绍了 Autoware.universe 各个传感器ROS2驱动&#xff0c;本系列其他文章&#xff1a; Autoware.universe部署01&…...

Spring boot如何工作

越来越方便了 java技术生态发展近25年&#xff0c;框架也越来越方便使用了&#xff0c;简直so easy&#xff01;&#xff01;&#xff01;我就以Spring衍生出的Spring boot做演示&#xff0c;Spring boot会让你开发应用更快速。 快速启动spring boot 请参照官网 Spring | Quic…...

代码随想录打卡—day45—【DP】— 8.29 完全背包应用

1 70. 爬楼梯&#xff08;完全背包版&#xff09; 70. 爬楼梯 完全背包装满的选法排列的套路&#xff0c;AC代码&#xff1a; class Solution { public:/*完全背包的思路:1 2是两个物体 可以无限取*/int dp[50]; // 能爬到第i楼的选法的排列数/*dp[j] dp[j - i];dp[0] 1fo…...

2023.8.28日论文阅读

文章目录 NestFuse: An Infrared and Visible Image Fusion Architecture based on Nest Connection and Spatial/Channel Attention Models(2020的论文)本文方法 LRRNet: A Novel Representation Learning Guided Fusion Network for Infrared and Visible Images本文方法学习…...

HAproxy(四十七)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 1.1 简介 1.2 核心功能 1.3 关键特性 1.4 应用场景 二、安装 1.内核配置 2.编译安装 ​3. 建立配置文件 4. 添加为系统服务 5. 添加3和5运行级别下自启动…...

Java实战场景下的ElasticSearch

文章目录 前言一、环境准备二、RsetAPI操作索引库1.创建索引库2.判断索引库是否存在3.删除索引库 二、RsetAPI操作文档1.新增文档2.单条查询3.删除文档4.增量修改5.批量导入6.自定义响应解析方法 四、常用的查询方法1.MatchAll():查询所有2.matchQuery():单字段查询3.multiMatc…...

拓世科技集团 | “书剑人生”李步云学术思想研讨会暨李步云先生九十华诞志庆

2023年&#xff0c;中国改革开放迎来了45周年&#xff0c;改革春风浩荡&#xff0c;席卷神州大地&#xff0c;45年间&#xff0c;中国特色社会主义伟大事业大步迈入崭新境界&#xff0c;一路上结出了饶为丰硕的果实。中华民族在这45年间的砥砺前行&#xff0c;不仅使中国的经济…...

前端须知名词解释

目录 一、多维转一维 二、一维转多维 一维转多维——使用场景&#xff1a;分页 三、判断当前元素是否为数组 四、判断当前元素是否是空对象 五、数字分割符&#xff1a;提高数字可读性 六、模糊盒子&#xff08;怪异盒子&#xff09;与标准盒模型 七、css的filter属性 …...

React性能优化之memo缓存函数

React是一个非常流行的前端框架&#xff0c;但是在处理大型应用程序时&#xff0c;性能可能会成为一个问题。为了解决这个问题&#xff0c;React提供了一个称为memo的功能&#xff0c;它可以缓存函数并避免不必要的重新渲染。 memo是React中的一个高阶组件&#xff08;HOC&…...

2023年高教社杯 国赛数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…...

C# Emgu.CV 条码检测

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Emgu.CV; using Emgu.CV.Util; using static Emgu.C…...

VueRouter的基本使用

路由的基本使用 文章目录 路由的基本使用01-VueRouterVueRouter的使用 &#xff08; 5 2&#xff09;综合代码 拓展&#xff1a;组件存放问题 什么是路由呢&#xff1f; 在生活中的路由&#xff1a;设备和IP的映射关系 在Vue中&#xff1a;路径 和 组件 的 映射 关系。 01-Vu…...

网工笔记:快速认识7类逻辑接口

逻辑接口是指能够实现数据交换功能但物理上不存在、需要通过配置建立的接口。逻辑接口需要承担业务传输。 下面是我整理了7款常见的逻辑接口。 接口类型 描述 Eth-Trunk接口 具有二层特性和三层特性的逻辑接口&#xff0c;把多个以太网接口在逻辑上等同于一个逻辑接口&…...

MySQL中的free链表,flush链表,LRU链表

一、free链表 1、概述 free链表是一个双向链表数据结构&#xff0c;这个free链表里&#xff0c;每个节点就是一个空闲的缓存页的描述数据块的地址&#xff0c;也就是说&#xff0c;只要你一个缓存页是空闲的&#xff0c;那么他的描述数据块就会被放入这个free链表中。 刚开始数…...

mac使用VsCode远程连接服务器总是自动断开并要求输入密码的解决办法

在mac中使用vscode远程连接服务器&#xff0c;时常会出现自动断开并要求重新输入服务器密码的问题&#xff0c;接下来让我们来解决它&#xff1a; 1、首先&#xff0c;在本地创建公钥&#xff1a; ssh-keygen 这条命令执行之后&#xff0c;出现提示直接回车即可&#xff1b;直…...

Python爬虫分布式架构 - Redis/RabbitMQ工作流程介绍

在大规模数据采集和处理任务中&#xff0c;使用分布式架构可以提高效率和可扩展性。本文将介绍Python爬虫分布式架构中常用的消息队列工具Redis和RabbitMQ的工作流程&#xff0c;帮助你理解分布式爬虫的原理和应用。 为什么需要分布式架构&#xff1f; 在数据采集任务中&#…...

【ES】笔记-集合介绍与API

集合是一种不允许值重复的顺序数据结构。 通过集合我们可以进行并集、交集、差集等数学运算&#xff0c; 还会更深入的理解如何使用 ECMAScript 2015(ES2015)原生的 Set 类。 构建数据集合 集合是由一组无序且唯一(即不能重复)的项组成的。该数据结构使用了与有限集合相同的数…...

Spring Boot(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot 前后端分离)【五】

&#x1f600;前言 本篇博文是关于Spring Boot(Vue3ElementPlusAxiosMyBatisPlusSpring Boot 前后端分离)【五】&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章…...

二、Tomcat 安装集

一、Tomcat—Docker 1. 拉取镜像 # 1、拉取镜像&#xff08;tomcat版本8&#xff0c;jre版本8&#xff09;。 docker pull tomcat:8-jre82. 启动容器 # 2、启动一个tomcat容器。 docker run -id --name tomcat -p 8080:8080 镜像ID # 3、宿主机里新建/root/tomcat目录&#x…...

CentOS 上通过 NFS 挂载远程服务器硬盘

NFS&#xff08;Network File System&#xff09;是一种用于在不同的计算机系统之间共享文件和目录的协议。它允许一个计算机系统将其文件系统的一部分或全部内容暴露给其他计算机系统&#xff0c;使其能够像访问本地文件一样访问这些内容。在这篇博客中&#xff0c;我们将介绍…...

微信小程序中的 广播监听事件

定义 WxNotificationCenter.js 文件&#xff1b; /*** author: Di (微信小程序开发工程师)* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)* 垂直微信小程序开发交流社区* * github地址: https://github.com/icindy/WxNotificationCenter…...

Quickstart: MinIO for Linux

单节点部署教程 1.安装Minio服务端 //wget下载二进制文件 wget https://dl.min.io/server/minio/release/linux-amd64/minio //赋予权限 chmod x minio //将minio可执行文件移入usr/local/bin目录下&#xff0c;使得minio可以全局执行 sudo mv minio /usr/local/bin/ 2.启动Mi…...

Java中word转Pdf工具类

背景&#xff1a; 最近做的一个项目中&#xff0c;对于word转Pdf用的地方很多&#xff0c;特此记录 搭建总图&#xff1a; 代码部分&#xff1a; 1.需要的jar包&#xff1a; aspose-words-15.8.0-jdk16.jar 注&#xff1a;下载好这个jar包后&#xff0c;在项目的根目录新建一…...

【conda install】网络慢导致报错CondaHTTPError: HTTP 000 CONNECTION FAILED for url

⭐⭐问题&#xff1a; 部署安装环境经常会出现由于网络慢问题&#xff0c;导致conda安装不了库&#xff0c;报错如下&#xff1a; Solving environment: failedCondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/…...

2023-8-28 图中点的层次(树与图的广度优先遍历)

题目链接&#xff1a;图中点的层次 #include <iostream> #include <cstring> #include <algorithm>using namespace std;const int N 100010;int h[N], e[N], ne[N], idx; int n, m; int q[N], d[N];void add(int a, int b) {e[idx] b, ne[idx] h[a], h…...

设计模式(一)

1、适配器模式 &#xff08;1&#xff09;概述 适配器中有一个适配器包装类Adapter&#xff0c;其包装的对象为适配者Adaptee&#xff0c;适配器作用就是将客户端请求转化为调用适配者中的接口&#xff1b;当调用适配器中的方法时&#xff0c;适配器内部会调用适配者类的方法…...

Prometheus关于微服务的监控

在微服务架构下随着服务越来越多,定位问题也变得越来越复杂,因此监控服务的运行状态以及针对异常状态及时的发出告警也成为微服务治理不可或缺的一环。服务的监控主要有日志监控、调用链路监控、指标监控等几种类型方式,其中指标监控在整个微服务监控中比重最高,也是实际生…...

CSS实现白天/夜晚模式切换

目录 功能介绍 示例 原理 代码 优化 总结 功能介绍 在网页设计和用户体验中&#xff0c;模式切换功能是一种常见的需求。模式切换可以为用户提供不同的界面外观和布局方案&#xff0c;以适应其个人偏好或特定环境。在这篇博客中&#xff0c;我们将探索如何使用纯CSS实现一…...