Linux 系统中异常与中断

文章目录

  • 异常与中断的关系
  • 中断的处理流程
  • 异常向量表
  • Linux 系统对中断的处理
    • ARM 处理器程序运行的过程
    • 程序被中断时,怎么保存现场
    • Linux 系统对中断处理的演进
      • Linux 对中断的扩展:硬件中断、软件中断
        • 硬件中断
        • 软件中断
      • 中断处理原则:
      • 耗时中断的拆分:上半部分和下半部分
        • 中断下半部分的实现:tasklet
        • 中断下半部分的实现:工作队列
      • 耗时中断的新技术:threaded irq(线程化的中断)
  • Linux 中断系统中的重要数据结构
    • 概述
    • irq_desc数组
    • irqaction结构体
    • irq_domain结构体
    • irq_data结构体
    • irq_chip结构体
  • 在设备树中指定中断、在驱动中获得中断
    • 设备树里中断节点的语法
      • 设备树里的中断控制器
      • 设备树里使用中断
    • 设备树里中断节点的示例
    • 在代码中获得中断
      • 对于 platform_device
      • 对于 I2C 设备、SPI 设备
      • 调用 of_irq_get 获得中断号
      • 对于 GPIO

异常与中断的关系

在这里插入图片描述
异常有:

  • ①指令未定义
  • ② 指令、数据访问有问题
  • ③ SWI(软中断)
  • ④ 快中断
  • ⑤ 中断

中断也属于一种“异常”,导致中断发生的情况有很多,比如:

  • 按键
  • 定时器
  • ADC 转换完成
  • UART 发送完数据、收到数据

众多的“中断源”,汇集到“中断控制器”,由“中断控制器”选择优先级最高的中断并通知 CPU,中断控制器也可以屏蔽中断信号。

中断的处理流程

arm 对异常(中断)处理过程:

  • ① 初始化:
    • a) 设置中断源,让它可以产生中断
    • b) 设置中断控制器(可以屏蔽某个中断,优先级)
    • c) 设置 CPU 总开关(使能中断)
  • ② 执行其他程序:正常程序
  • ③ 产生中断:比如按下按键—>中断控制器—>CPU
  • ④ CPU 每执行完一条指令都会检查有无中断/异常产生
  • ⑤ CPU 发现有中断/异常产生,开始处理。
    • 对于不同的异常,跳去不同的地址执行程序。这地址上,只是一条跳转指令,跳去执行某个函数(地址),这个就是异常向量。③④⑤都是硬件做的。
  • ⑥ 这些函数做什么事情?软件做的:
    • a) 保存现场(各种寄存器)
    • b) 处理异常(中断):分辨中断源,再调用不同的处理函数
    • c) 恢复现场

异常向量表

u-boot 或是 Linux 内核,都有类似如下的代码:

_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq // 发生中断时,CPU 跳到这个地址执行该指令 ** 假设地址为 0x18**
ldr pc, _fiq

这就是异常向量表,每一条指令对应一种异常。

  • 发生复位时,CPU 就去 执行第 1 条指令:b reset。
  • 发生中断时,CPU 就去执行“ldr pc, _irq”这条指令。
  • 这些指令存放的位置是固定的,比如对于 ARM9 芯片中断向量的地址是0x18。当发生中断时,CPU 就强制跳去执行 0x18 处的代码。

在向量表里,一般都是放置一条跳转指令,发生该异常时,CPU 就会执行向量表中的跳转指令,去调用更复杂的函数。当然,向量表的位置并不总是从 0 地址开始,很多芯片可以设置某个vector base 寄存器,指定向量表在其他位置,比如设置 vector base 为0x80000000,指定为 DDR 的某个地址。但是表中的各个异常向量的偏移地址,是固定的:复位向量偏移地址是 0,中断是 0x18。

Linux 系统对中断的处理

ARM 处理器程序运行的过程

ARM 芯片属于精简指令集计算机(RISC:Reduced Instruction SetComputing),它所用的指令比较简单,有如下特点:

  • ① 对内存只有读、写指令
  • ② 对于数据的运算是在 CPU 内部实现
  • ③ 使用 RISC 指令的 CPU 复杂度小一点,易于设计

比如对于 a=a+b 这样的算式,需要经过下面 4 个步骤才可以实现,CPU 运行时,先去取得指令,再执行指令:

  • ① 把内存 a 的值读入 CPU 寄存器 R0
  • ② 把内存 b 的值读入 CPU 寄存器 R1
  • ③ 把 R0、R1 累加,存入 R0
  • ④ 把 R0 的值写入内存 a
    在这里插入图片描述

程序被中断时,怎么保存现场

如果要暂停一个程序,中断一个程序,就需要把这些寄存器的值保存下来:这就称为保存现场。

寄存器保存在内存中,这块内存就称之为栈。程序要继续执行,就先从栈中恢复那些 CPU 内部寄存器的值。如图所示:概括程序 A、B 的切换过程

在这里插入图片描述
这个场景并不局限于A、B 的切换过程,也可以中断,函数调用等
① 函数调用:

  • a) 在函数 A 里调用函数 B,实际就是中断函数 A 的执行。
  • b) 那么需要把函数 A 调用 B 之前瞬间的 CPU 寄存器的值,保存到栈里;
  • c) 再去执行函数 B;
  • d) 函数 B 返回之后,就从栈中恢复函数 A 对应的 CPU 寄存器值,继续执行。

② 中断处理

  • a) 进程 A 正在执行,这时候发生了中断。
  • b) CPU 强制跳到中断异常向量地址去执行,
  • c) 这时就需要保存进程 A 被中断瞬间的 CPU 寄存器值,
  • d) 可以保存在进程 A 的内核态栈,也可以保存在进程 A 的内核结构体中
  • e) 中断处理完毕,要继续运行进程 A 之前,恢复这些值。

③ 进程切换

  • a) 在所谓的多任务操作系统中,我们以为多个程序是同时运行的。
  • b) 如果我们能感知微秒、纳秒级的事件,可以发现操作系统时让这些程序依次执行一小段时间,进程 A 的时间用完了,就切换到进程 B。
  • c) 怎么切换?
  • d) 切换过程是发生在内核态里的,跟中断的处理类似。
  • e) 进程 A 的被切换瞬间的 CPU 寄存器值保存在某个地方;
  • f) 恢复进程 B 之前保存的 CPU 寄存器值,这样就可以运行进程 B 了。

进程的调度也是使用栈来保存、恢复现场;在linux中,线程才是调度的最小单位,这些线程共用打开的文件句柄、全局变量等等。而这些线程,之间是互相独立的,“同时运行”,也就是说:每一个线程,都有自己的栈,那么在同一个进程里面,所有的线程共享代码段和数据段,同时,他们还有各自的栈内存,保存CPU寄存器里面的值

在这里插入图片描述

Linux 系统对中断处理的演进

Linux 对中断的扩展:硬件中断、软件中断

硬件中断

对于按键中断等硬件产生的中断,称之为“硬件中断”(hard irq)。每个硬件中断都有对应的处理函数

可以认为对硬件中断的处理是用数组来实现的,数组里存放的是函数指针:
在这里插入图片描述
当硬件触发中断,系统会根据中断号调用中断处理函数

软件中断

相对的,可以人为地制造中断:软件中断(soft irq)
在这里插入图片描述

如何触发软中断?——由软件决定,对于 X 号软件中断,只需要把它的 flag 设置为 1 就表示发生了该中断

软件中断何时处理?——Linux 系统中,各种硬件中断频繁发生,例如定时器中断每 10ms 发生一次,那将软件中断设计在处理完硬件中断后,再去处理软件中断

有哪些软件中断?——查内核源码 include/linux/interrupt.h

enum 
{HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, TASKLET_SOFTIRQ SCHED_SOFTIRQ, 
#ifdef CONFIG_HIGH_RES_TIMERSHRTIMER_SOFTIRQ, 
#endif 
};

怎么触发软件中断?——最核心的函数是 raise_softirq,简单地理解就是设置 softirq_veq[nr]的标记位:

extern void raise_softirq(unsigned int nr) //用于引发一个软中断(类似普通中断)

怎么设置软件中断的处理函数:

extern void open_softirq(int nr, void (*action) (struct soft_action*));

中断处理原则:

  1. Linux系统中,中断不能嵌套,否则会导致栈不够用

  2. 中断处理要越快越好,即在注册中断 handler 函数中指定中断处理函数,代码尽可能高效

在 Linux 系统中使用中断是挺简单的,为某个中断 irq 注册中断处理函数handler,可以使用 request_irq 函数:
在这里插入图片描述

如果中断要做的事情就是很多,没办法加快,则需要将耗时的事情拆分

耗时中断的拆分:上半部分和下半部分

当一个中断要耗费很多时间来处理时,它的坏处是:在这段时间内,其他中断无法被处理。换句话说,在这段时间内,系统是关中断的

耗时的中断:中断分为上半部分/下半部分;在 handler 函数里只做紧急的事,然后就重新开中断,让系统得以正常运行;那些不紧急的事,以后再处理,处理时是开中断的。

在这里插入图片描述

中断下半部的实现有很多种方法,主要讲2种:

  • tasklet(小任务)
  • work queue(工作队列)。

中断下半部分的实现:tasklet

当下半部比较耗时但是能忍受,并且它的处理比较简单时,可以用tasklet 来处理下半部。tasklet 是使用软件中断来实现。

在这里插入图片描述

使用流程图简化一下:

在这里插入图片描述

  • 中断下半部执行时,有可能会被多次打断,有可能会再次发生同一个中断
    • 中断下半部执行时再次发生同一个中断:则同一个中断的上半部、下半部,在执行时是多对一的关系。
    • 中断下半部执行时如果被多次打断:如果在A中断的下半部出现中断,则会执行B中断的上半部分,然后再进行preemot_count判断时,直接进入完成处理;之后是恢复整个软件中断的处理:包含A中断和B中断的下半部分。【软件中断时定时处理的】

中断下半部分属于软件中断的一种,在枚举类型里面对应TASKLET_SOFTIRQ

在这里插入图片描述

中断下半部分的实现:工作队列

在中断下半部的执行过程中,虽然是开中断的,期间可以处理各类中断。但是毕竟整个中断的处理还没走完,这期间 APP 是无法执行的。在中断上半部唤醒内核线程。内核线程和APP 都一样竞争执行,APP 有机会执行,系统不会卡顿。

这个内核线程是系统帮我们创建的,一般是 kworker 线程,内核中有很多这样的线程:
在这里插入图片描述

kworker 线程要去“工作队列”(work queue)上取出一个一个“工作”(work 结构体),来执行它里面的函数。

使用:

①创建 work:
你得先写出一个函数,然后用这个函数填充一个 work 结构体。比如:

static DECLARE_WORK(aer_recover_work,aer_recover_work_fun);
//第一参数是work结构体

② 要执行这个函数时,把 work 提交给 work queue 就可以了:

schedule_work(&aer_recover_work);

上述函数会把 work 提供给系统默认的 work queue:system_wq,它是一个队列。

在中断上半部调用 schedule_work 函数,触发 work 的处理

③ 谁来执行 work 中的函数?
schedule_work 函数不仅仅是把 work 放入队列,还会把kworker 线程唤醒。此线程抢到时间运行时,它就会从队列中取出 work,执行里面的函数。

④ 谁把 work 提交给 work queue?
在中断场景中,可以在中断上半部调用 schedule_work 函数。

工作队列只是创建一个线程,对于多核CPU来说,可以处理多个线程,所以将中断下部分实现的技术换为threaded irq,这样每个中断下半部分为一个线程,在任意一个CPU中都可以执行

耗时中断的新技术:threaded irq(线程化的中断)

在这里插入图片描述

你可以只提供 thread_fn,系统会为这个函数创建一个内核线程。发生中断时,内核线程就会执行这个函数。

以前用 work 来线程化地处理中断,一个 worker 线程只能由一个 CPU 执行,多个中断的 work 都由同一个 worker线程来处理,在单 CPU 系统中也只能忍着了。但是在 SMP 系统中,明明有那么多 CPU 空着,你偏偏让多个中断挤在这个CPU 上?新技术threaded irq,为每一个中断都创建一个内核线程;多个中断的内核线程可以分配到多个 CPU 上执行,这提高了效率。

Linux 中断系统中的重要数据结构

概述

本节内容,可以从request_irq函数(头文件:include/linux/interrupt.h)一路分析得到。

在这里插入图片描述

注意:如果内核配置了CONFIG_SPARSE_IRQ,那么它就会用基数树(radixtree)来代替irq_desc数组。SPARSE的意思是“稀疏”,假设大小为1000的数组中只用到2个数组项,那不是浪费嘛?所以在中断比较“稀疏”的情况下可以用基数树来代替数组。

irq_desc数组

irq_desc结构体在include/linux/irqdesc.h中定义,主要内容如下图:
在这里插入图片描述
每一个irq_desc数组项中都有一个函数:handle_irq,还有一个action链表。要理解它们,需要先看中断结构图:硬件中断的信号从左到右,函数的处理从右到左(函数主要是读取寄存器确定中断的来源)。由图可知共享中断会出现两个中断号:A号中断和B号中断

在这里插入图片描述
在Linux内核中对于每一个硬件中断,都有一个数组成员项,所以下图有GIC结构体和GPIO结构体,每个结构体都有:handle_irq,还有一个action链表
在这里插入图片描述
首先GIC中断CPU时,CPU读取GIC状态得到中断A。

① GIC的处理函数:

  • irq_desc[A].handle_irq是XXX_gpio_irq_handler(XXX指厂家),假设A号中断对应的是GPIO中断,这个函数需要读取芯片的GPIO控制器,细分发生的是哪一个GPIO中断(假设是B),再去调用irq_desc[B]. handle_irq。

② 模块的中断处理函数:

  • 导致GPIO中断B发生的原因很多,可能是外部设备1,可能是外部设备n,可能只是某一个设备,也可能是多个设备。所以irq_desc[B].handle_irq会依次调用某个链表(action链表)里的函数【BSP开发人员会设置对应的处理函数,一般是handle_level_irq或handle_edge_irq,从名字上看是用来处理电平触发的中断、边沿触发的中断】,这些函数由外部设备提供。这些函数自行判断该中断是否自己产生,若是则处理。

③ 外部设备提供的处理函数:

  • 这里说的“外部设备”可能是芯片,也可能总是简单的按键。它们的处理函数由自己驱动程序提供,这是最熟悉这个设备的“人”:它知道如何判断设备是否发生了中断,如何处理中断。

irqaction结构体

irqaction结构体在include/linux/interrupt.h中定义,主要内容如下图:
在这里插入图片描述

当调用request_irq、request_threaded_irq注册中断处理函数时,内核就会构造一个irqaction结构体。在里面保存name、dev_id等,最重要的是handler、thread_fn、thread。

  • handler是中断处理的上半部函数,用来处理紧急的事情。
  • thread_fn对应一个内核线程thread,当handler执行完毕,Linux内核会唤醒对应的内核线程。在内核线程里,会调用thread_fn函数。

可以提供handler而不提供thread_fn,就退化为一般的request_irq函数。
可以不提供handler只提供thread_fn,完全由内核线程来处理中断。
也可以既提供handler也提供thread_fn,这就是中断上半部、下半部。

在调用reqeust_irq函数时可以传入dev_id作用有2个:

  • ① 中断处理函数执行时,可以使用dev_id
  • ② 卸载中断时要传入dev_id,这样才能在action链表中根据dev_id找到对应项所以在共享中断中必须提供dev_id,非共享中断可以不提供。

irq_domain结构体

irq_domain结构体在include/linux/irqdomain.h中定义,主要内容如下图:
在这里插入图片描述

在设备树中有这样的属性:

interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;

它表示要使用gpio1里的第5号中断,hwirq就是5。

但是我们在驱动中会使用request_irq(irq, handler)这样的函数来注册中断,irq是软件中断号,它应该从“gpio1的第5号中断”转换得来。

谁把hwirq转换为irq?由gpio1的相关数据结构,就是gpio1对应的irq_domain结构体。即每个一GPIO都有自己的irq_domain结构体

irq_domain结构体中有一个irq_domain_ops结构体,里面有各种操作函数,主要是:

  • ① xlate:用来解析设备树的中断属性,提取出hwirq、type等信息。
  • ② map:把hwirq转换为irq。即转为为虚拟中断号【软件中断号会保存在platform_diverce】

map函数会完成映射关系,会保存在unsigned int linear_revmap[];数组里面
例如:读取GPIO寄存器得到hwirq,根据hwirq得到之前映射的irq(软件中断号B)

irq_data结构体

irq_data结构体在include/linux/irq.h中定义,主要内容如下图:
在这里插入图片描述

它就是个中转站,里面有irq_chip指针 irq_domain指针,都是指向别的结构体。

irq是软件中断号,hwirq是硬件中断号。比如上面我们举的例子,在GPIO中断B是软件中断号,可以找到irq_desc[B]这个数组项;GPIO里的第x号中断,这就是hwirq。

谁来建立irq、hwirq之间的联系呢?由irq_domain来建立。irq_domain会把本地的hwirq映射为全局的irq,什么意思?比如GPIO控制器里有第1号中断,UART模块里也有第1号中断,这两个“第1号中断”是不一样的,它们属于不同的“域”──irq_domain。

irq_chip结构体

irq_chip结构体在include/linux/irq.h中定义,主要内容如下图:
在这里插入图片描述

这个结构体跟“chip”即芯片相关,里面各成员的作用在头文件中也列得很清楚,摘录部分如下:

@irq_startup: start up the interrupt (defaults to ->enable if NULL)
@irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
@irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
@irq_disable: disable the interrupt
@irq_ack: start of a new interrupt
@irq_mask: mask an interrupt source
@irq_mask_ack: ack and mask an interrupt source
@irq_unmask: unmask an interrupt source
@irq_eoi: end of interrupt

我们在request_irq后,并不需要手工去使能中断,原因就是系统调用对应的irq_chip里的函数帮我们使能了中断。

我们提供的中断处理函数中,也不需要执行主芯片相关的清中断操作,也是系统帮我们调用irq_chip中的相关函数。

但是对于外部设备相关的清中断操作,还是需要我们自己做的。
就像上面图里的“外部设备1“、“外部设备n”,外设备千变万化,内核里可没有对应的清除中断操作

在设备树中指定中断、在驱动中获得中断

可以参考文档:内核的Documentation\devicetree\bindings\interrupt-controller\interrupts.txt

设备树里中断节点的语法

设备树里的中断控制器

比如中断的硬件框图如下:设备树会将这个硬件图转化为节点
在这里插入图片描述

在硬件上,“中断控制器”只有 GIC 这一个,但是我们在软件上也可以把上图中的“GPIO”称为“中断控制器”。很多芯片有多个 GPIO 模块,比如 GPIO1、GPIO2 等等。所以软件上的“中断控制器”就有多个。各个中断控制器之间有父子关系。

假设 GPIO1 有 32 个中断源,但是它把其中的 16 个汇聚起来向 GIC 发出一个中断,把另外 16 个汇聚起来向 GIC 发出另一个中断。这就意味着 GPIO1 会用到 GIC 的两个中断,会涉及 GIC 里的 2 个 hwirq。这些层级关系、中断号(hwirq),都会在设备树中有所体现。

在设备树中,中断控制器节点中必须有一个属性:interrupt-controller,表明它是“中断控制器”。还必须有一个属性:#interrupt-cells,表明引用这个中断控制器的话需要多少个 cell。

#interrupt-cells 的值一般有如下取值:

  • #interrupt-cells=<1>
    别的节点要使用这个中断控制器时,只需要一个 cell 来表明使用“哪一个中断”。
  • #interrupt-cells=<2>
    别的节点要使用这个中断控制器时,需要一个 cell 来表明使用“哪一个中断”;还需要另一个 cell 来描述中断,一般是表明触发类型:
2 个  cell 的 的 bits[3:0] 用来表示中断触发类型(trigger type and level flags)1 = low-to-high edge triggered ,上升沿触发
2 = high-to-low edge triggered ,下降沿触发
4 = active high level-sensitive ,高电平触发
8 = active low level-sensitive , 低电平触发

如果中断控制器有级联关系,下级的中断控制器还需要表明它的“ interrupt-parent ” 是 谁 , 用 了 interrupt-parent ” 中的哪一个“interrupts”

设备树里使用中断

一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的?这 3个问题,在设备树里使用中断时,都要有所体现。

例如:

i2c@7000c000 {gpioext: gpio-adnp@41 {compatible = "ad,gpio-adnp";//用哪一个中断控制器里的中断?interrupt-parent = <&gpio>;//用哪一个中断?Interrupts 里要用几个 cell,由 interrupt-parent 对应的中断控制器决定interrupts = <160 1>;gpio-controller;#gpio-cells = <1>;interrupt-controller;#interrupt-cells = <2>;//指明子节点的指定中断控制器的话需要多少个 cell};
......
};

新的写法:interrupts-extended,指明了上述的三个问题

一个“interrupts-extended”属性就可以既指定“interrupt-parent”,也指定“interrupts”,比如:

interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;

设备树里中断节点的示例

从设备树文件里面将有关中断的部分内容抽取出来:
在这里插入图片描述
对于第一个节点,是GIC控制器,使用到该控制器需要三个cell,因为对于多CPU系统,GIC可以发出两种信号(SPI,PPI)而CPU之间可以通过SGI进行通信,所以GIC控制器的第一个cell必须要指定是哪种信号(SPI或PPI或SGI)

在这里插入图片描述

从设备树反推 IMX6ULL 的中断体系,如下,比之前的框图多了一个“GPC INTC”

在这里插入图片描述

在代码中获得中断

根据博文:哪些设备树节点会被转换为 platform_device提到,设备树中的节点有些能被转换为内核里的platform_device,有些不能
在这里插入图片描述

对于 platform_device

一个节点能被转换为 platform_device,如果它的设备树里指定了中断属性,那么可以从platform_device 中获得“中断资源”,函数如下,可以使用下列函数获得 IORESOURCE_IRQ 资源,即中断号:

/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type // 取哪类资源?IORESOURCE_MEM 、IORESOURCE_REG
* // IORESOURCE_IRQ 等
* @num: resource index // 这类资源中的哪一个?
*/
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type,unsigned int num);

对于 I2C 设备、SPI 设备

对于 I2C 设备节点,I2C 总线驱动在处理设备树里的 I2C 子节点时,也会处理其中的中断信息。一个 I2C 设备会被转换为一个 i2c_client 结构体,中断号会保存在 i2c_client 的 irq 成员里,代码如下(drivers/i2c/i2c-core.c):

在这里插入图片描述
对于 SPI 设备节点,SPI 总线驱动在处理设备树里的 SPI 子节点时,也会处理其中的中断信息。一个 SPI 设备会被转换为一个 spi_device 结构体,中断号会保存在 spi_device 的 irq 成员里,代码如下(drivers/spi/spi.c):
在这里插入图片描述

调用 of_irq_get 获得中断号

如果你的设备节点既不能转换为 platform_device,它也不是 I2C 设备,不是 SPI 设备,那么在驱动程序中可以自行调用 of_irq_get 函数去解析设备树,得到中断号。

对于 GPIO

参考:drivers/input/keyboard/gpio_keys.c;可以使用 gpio_to_irq 或 gpiod_to_irq 获得中断号。
举例,假设在设备树中有如下节点:

gpio-keys {compatible = "gpio-keys";pinctrl-names = "default";user {label = "User Button";gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;gpio-key,wakeup;linux,code = <KEY_1>;};
};

那么可以使用下面的函数获得引脚和 flag:

button->gpio = of_get_gpio_flags(pp, 0, &flags);
bdata->gpiod = gpio_to_desc(button->gpio);

再去使用 gpiod_to_irq 获得中断号:

irq = gpiod_to_irq(bdata->gpiod);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试题】万字总结MYSQL面试题

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 1、三大范式 2、DML 语句和 DDL 语句区别 3、主键和外键的区别 4、drop、delete、truncate 区别 5、基础架构 6、MyISAM 和 InnoDB 有什么区别&#xff1f; 7、推荐自增id作为…

【C++】STL---list基本用法介绍

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

Kotlin基础(七):数据类和封闭类

前言 本文主要讲解kotlin数据类&#xff08;DataClass&#xff09;和封闭类&#xff08;SealedClasses&#xff09;&#xff0c;包括使用数据类&#xff0c;对象复制&#xff0c;数据类成员的解构&#xff0c;使用封闭类&#xff0c;以及数据类和封闭类在Android开发中的应用。…

el-select和el-checkBox实现下拉菜单全选功能

el-select 和 el-checkbox 实现下拉菜单全选功能 示例代码&#xff1a; <el-selectpopper-class"select-container"v-model"ids"placeholder"请选择目标":multiple-limit"20"multiplefilterablecollapse-tagsclass"wd400&qu…

uniapp 小程序 查看评价

查看评价效果图&#xff1a; 评分组件在上一篇文章&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; <template><view class"view-comments"><view class"evaluate-box"><view class"ti…

使用Gin框架搭配WebSocket完成实时聊天

文章目录 前言实时聊天聊天功能测试发送信息 前言 在写项目的时候&#xff0c;需要完成实时聊天的功能&#xff0c;于是简单的学习下WebSocket&#xff0c;想知道WebSocket是什么的小伙伴可以去网上别的地方学习一下。 要实现实时聊天&#xff0c;网上的大部分内容都是Spring…

Rust操作MySQL

查询 本部分是对 「Rust入门系列」Rust 中使用 MySQL[1]的学习与记录 经常使用的时间处理库&#xff1a; chrono 流式查询使用&#xff1a; query_iter 输出到Vec使用&#xff1a; query 映射到结构体使用&#xff1a; query_map 获取单条数据使用&#xff1a; query_first 命名…

小程序体验版上线注意事项

1.接口域名必须是https&#xff0c;有ssh证书。不能用ip地址。 2.需要在微信公众平台进行配置 微信公众平台->开发-> 开发管理->开发设置 对服务器域名和业务域名进行配置对业务域名进行配置时&#xff0c;需要下载校验文件&#xff0c;放在域名根目录下

【Node.js 安装】Node.js安装与使用教程

Node.js 安装 Node.js 是什么那什么是运行时 如何安装 Node.jsNode 使用教程 Node.js 是什么 先说结论&#xff0c;Node.js 它是一套 JavaScript 运行环境&#xff0c;用来支持 JavaScript 代码的执行 JavaScript 诞生于 1995 年&#xff0c;几乎是和互联网同时出现&#xf…

十分钟掌握 Vim 编辑器核心功能

十分钟掌握 Vim 编辑器核心功能 文章目录 十分钟掌握 Vim 编辑器核心功能&#x1f468;‍&#x1f3eb;内容一&#xff1a;前言【Vim是什么】&#x1f468;‍&#x1f52c;内容二&#xff1a;Vim 常用模式&#x1f468;‍&#x1f680;内容三&#xff1a;基本操作&#x1f468;…

【C语言】表达式求值相关问题汇总—>隐式类型转换(整型提升)、算数转换与操作符优先级汇总(收藏查阅)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负。 目录 前言&#xff1a; 一、隐式类型转换 &#xff08;一&#xff09;整型提升的意义…

Dubbogo 详解

Dubbogo 详解 简介 dubbo功能很强大的微服务开发框架&#xff0c;支持多种通信协议&#xff0c;并具有流量治理的功能。 dubbo在有了大转变&#xff0c;拥抱了云原生&#xff0c;从哪些方面可以体现呢&#xff1f; 推出了自己的Trip协议修复了服务发现的级别&#xff0c;之…

华为云CodeArts产品体验的心得体会及想法

文章目录 前言CodeArts 的产品优势一站式软件开发生产线研发安全Built-In华为多年研发实践能力及规范外溢高质高效敏捷交付 功能特性说明体验感受问题描述完结 前言 华为云作为一家全球领先的云计算服务提供商&#xff0c;致力于为企业和个人用户提供高效、安全、可靠的云服务。…

算法与数据结构(二十一)前缀和数组差分数组

前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和。 1. 一维数组中的前缀和 先看一道例题&#xff0c;力扣第 303 题「区域和检索 - 数组不可变」&#xff0c;让你计算数组区间内元素的和&#xff0c;这是一道标准的前缀和问题&#xff1a; 题目要求你实现这样一个…

【C++初阶】---C++入门篇

文章目录 前言&#x1f31f;一、C历史介绍&#x1f31f;二、命名空间&#x1f30f;2.1.C与C对比&#x1f30f;2.2.命名空间的引入&#x1f30f;2.3.命名空间定义&#x1f30f;2.4.命名空间的使用&#x1f30f;2.5.对上述C与C对比中的第二个不同点的解释&#xff1a; &#x1f3…

TableGPT: Towards Unifying Tables, Nature Language and Commands into One GPT

论文标题&#xff1a;TableGPT: Towards Unifying Tables, Nature Language and Commands into One GPT 论文地址&#xff1a;https://github.com/ZJU-M3/TableGPT-techreport/blob/main/TableGPT_tech_report.pdf 发表机构&#xff1a;浙江大学 发表时间&#xff1a;2023 本文…

使用GGML和LangChain在CPU上运行量化的llama2

Meta AI 在本周二发布了最新一代开源大模型 Llama 2。对比于今年 2 月发布的 Llama 1&#xff0c;训练所用的 token 翻了一倍&#xff0c;已经达到了 2 万亿&#xff0c;对于使用大模型最重要的上下文长度限制&#xff0c;Llama 2 也翻了一倍。 在本文&#xff0c;我们将紧跟趋…

【NLP】使用 Keras 保存和加载深度学习模型

一、说明 训练深度学习模型是一个耗时的过程。您可以在训练期间和训练后保存模型进度。因此&#xff0c;您可以从上次中断的地方继续训练模型&#xff0c;并克服漫长的训练挑战。 在这篇博文中&#xff0c;我们将介绍如何保存模型并使用 Keras 逐步加载它。我们还将探索模型检查…

JavaSwing+MySQL的酒店管理系统

点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/88063706?spm1001.2014.3001.5503 JDK1.8、MySQL5.7 功能&#xff1a;散客开单&#xff1a;完成散客的开单&#xff0c;可一次最多开5间相同类型的房间。 2、团体开单&#xff1a;完成团体…

【论文笔记】KDD2019 | KGAT: Knowledge Graph Attention Network for Recommendation

Abstract 为了更好的推荐&#xff0c;不仅要对user-item交互进行建模&#xff0c;还要将关系信息考虑进来 传统方法因子分解机将每个交互都当作一个独立的实例&#xff0c;但是忽略了item之间的关系&#xff08;eg&#xff1a;一部电影的导演也是另一部电影的演员&#xff09…