18f458中断入口C语言_操作系统开发之——中断

aca9588b3264ab915b62595defa7f6b5.png

这里先提交一个代码的错误,之前运行过快,没看出刷屏的问题:

// kernel/console.c...void init_console(void) {...    // Before:    // console_fixed_height = ScreenHeight - 16;    //    console_fixed_height = (ScreenHeight / 16 - 1) * 16;...}...void console_roll(void) {...    // Before:    // if (console_y > console_fixed_height) {    //    if (console_y >= console_fixed_height) {...}

891ac9682112718c1ab2786c67cc1df4.png

Logo字符和信息是笔者自己加的

接下来,终于到了内核开发的核心部分:中断。

中断基本概念

中断,顾名思义就是中断当前任务并转去做其他事务:当你正在看一本书时,突然房间有个电话,你就先把书反扣在桌面上,然后去接电话,当你接完电话回来时,又把书扣回继续看

粗糙地用计算机术语描述就是:CPU在有序执行一段程序时,中断控制芯片突然传送了个信息请求CPU处理,这时CPU暂停当前执行的程序,然后将当前执行程序的各个寄存器的值和其他数据压入堆栈,转而去执行中断程序,然后回来将原来栈里的数据弹出,接着执行之前的程序。

没有中断,操作系统是没有灵魂的:你在键盘上敲一个键,显示一个图像,字符缓冲区输出一段文本,操作一个文件,鼠标的随便滑动,网络数据的传输,各种外设协同、驱动功能,进程调度等等都是建立在完善的中断系统之下完成的,相信读者此时已经了解到中断的重要性。失去了中断机制,操作系统就只是个单纯的“死循环”。

中断的分类

外部中断(硬件中断)

所谓外部中断,就是指CPU外部发生的中断,由硬件发起。常发生于输入输出设备、时钟,计时器,电源,网卡等部件和外设。外部中断有两根信号线:INTR(INTeRrupt)和 NMI(Non Maskable Interrupt)。INTR传来的中断比较无关紧要,CPU甚至可以不去处理,例如网卡和硬盘的中断请求,CPU可以晚一些执行,我们常称作可屏蔽中断;而NMI就很严重了,基本上都必须立马处理,比如内存读写出错,电源掉电等。

由于外设众多,执行中断程序的时候可能又会有另一个中断发生,因此中断程序一般都有个特点:执行要尽快,函数要可重入(常发生于多线程中的全局变量保护的问题)。

像这些可屏蔽中断,在Linux中,分为上半部分和下半部分:上半部分处理比较重要的,要快速完成的程序,下部分就是没那么重要的程序,一般在CPU空闲时或者合适的时期来处理,这里有个很生动的例子:

拿网卡举例子,网络中的数据通过网线到达网卡后,首先会被存储到网卡自己的缓冲区中,这个缓冲区容量不大(比起内存来说是非常小的),即使很大也有写满的那天,所以里面的数据必须立即被 CPU拿走,否则由于网卡缓冲区中无空余空间,后续到来的数据只能丢掉。鉴于这个刻不容缓的理由,网卡会立即发中断通知 CPU:“数据到了,赶紧取走”,这话说得无比坚定,丝毫没有商量的意思,CPU 立即放下手里的工作(其实并不是真地立即放下,怎么也得把当前正在执行的指令执行完,指令的执行必须是原子操作一气呵成,哪有执行一半指令的道理),马上执行网卡的中断处理程序,将网卡缓冲区中的数据拷贝到内核缓冲区中,至此,救火工作算是完成了,这就是所说的上半部。CPU 拿到网络数据后,处理数据的工作就不那么紧急了,它将在下半部中完成,这部分将在适当的时机被启动。

来源:《操作系统真象还原》

随着时代的发展,很多外设之间可以使用通道机制和DMA方式进行工作,大大得减轻了CPU的负担。之前的图形模式的Linear Frame Buffer就是其中一个例子。

内部中断(软件中断和异常)

中断源都是软件(可能有些人对软件的定义仍然是:Application。事实上,一系列按照特定顺序组织的计算机数据和指令的集合都是软件,你可以说操作系统是个很大的系统软件,也可以说BIOS是在ROM里躺着的软件,甚至一个dll、lib、so、数据、文档都可以叫做软件)发起的,常见于除数为0,运算溢出,指令的单步运行,程序运行至断点等等。至于其他,Intel官方以及列出个表了:

中断向量号助记符描述起源
0#DE除 0 异常DIV和IDIV指令
1#DB调试异常任何代码或数据引用
2/NMI 中断不可屏蔽的外部中断
3#BP断点异常INT 3指令
4#OF溢出INTO指令
5#BR对数组的引用超出边界BOUND指令
6#UD无效或未定义的操作码UD指令或保留的操作码
7#NM设备不可用(无数学协处理器)浮点或WAIT / FWAIT指令
8#DF双重故障(有错误代码)可以生成异常,NMI或INTR的任何指令
9#MF协处理器跨段操作浮点指令
10#TS无效TSS(有错误代码)任务切换或TSS访问
11#NP段不存在(有错误代码)正在加载段寄存器或访问系统段
12#SS栈错误(有错误代码)堆栈操作和SS寄存器加载
13#GP常规保护(有错误代码)任何内存引用和其他保护检查
14#PF页故障(有错误代码)任何内存引用
15保留
16#MF浮点处理单元错误浮点或WAIT / FWAIT指令
17#AC对齐检查存储器中的任何数据引用
18#MC机器检查错误代码(如果有)和来源取决于型号
19#XMSIMD(单指令多数据)浮点异常SIMD浮点指令
20#VE虚拟化EPT异常
21-31保留
32-255可屏蔽中断来自INTR引脚或INT n指令的外部中断

翻译过来非常糟糕,读者可在intel白皮书的6.4.1看到原文和更多解释。对于表中的中断向量号,0~19号中断被CPU占用,20-31号中断被Intel保留,32~255号属于用户可自定义中断。不过我们一般都会中断按照习惯指定固定的设备。比如32号是timer中断,33号是键盘中断等等。下面我们开始介绍点实质性的东西。

中断描述符表

中断描述符表(Interrupt Descriptor Table,IDT)是保护模式下用于存储中断处理程序入口的表,CPU接收到一个中断后,通过中断向量号在表中定位描述符,在该描述符中找到该中断处理程序的起始地址,接着执行该中断处理程序。中断描述符表里面可以包含以下任意一种门描述符:
  • 任务门描述符

  • 中断门描述符

  • 陷阱门描述符

  • 调用门描述符

他们的数据结构如下(每个门的上面是高32位,下面是低32位):

240237232b2c0a75c927dfdc7fcd3f62.png

调用门的结构是这样的:

bcae2fd968abcd182060b9eda678f870.png

上面的英文我们见过很多次了,笔者就不再翻译了,我们只需要中断门描述符,其他门描述符读者感兴趣可以自行研究。我们直接用C语言表示该数据结构:
// include/interrupt.h#ifndef _INTERRUPT_H#define _INTERRUPT_H#include typedef union Type_S {    struct {        uint16_t Reserved:5;        uint16_t SetZero:3;        uint16_t P:1;        uint16_t DPL:2;        uint16_t Type_Flag:5;   // |0 D 1 1 0| D: Size of gate: 1 = 32 bits; 0 = 16 bits    } __attribute__((packed));    uint16_t All;} Type_S;typedef struct IDT_S {    // High 32bits    uint16_t Offset0_15;    uint16_t Segment_Selector;    // Low 32bits    Type_S Type;    uint16_t Offset16_31;} __attribute__((packed)) IDT_S;typedef struct IDTR_S {    uint16_t Limite;    uint32_t Base;} __attribute__((packed)) IDTR_S;#define IDT_BASE 0x00000000#define IDT_SIZE 0xFF#define INT_GATE 0x8E00     // 1000 1110 0000 0000#endif // _INTERRUPT_H
Type_S其实写成uint16_t就可以了,只不过笔者“严格”遵守门描述符的数据结构而已,__attribute__((packed))的是GNU专有的语法,gcc编译器在编译结构体部分的时候会自动根据结构体的数据结构添加一些数据类型进行内存对齐,使其运行效率提高,但是底层的东西不能这么做,是什么就是什么,packed就是为了告诉编译器不要“自作聪明”。接下来我们还要有一个IDT描述符的初始化函数:
// kernel/interrupt.c#include #include #include static void init_IDT_Descriptor(uint16_t Segment_Selector, uint32_t Offset, uint16_t Type, IDT_S *IDT);IDT_S IDT[256];IDTR_S IDTR;static void init_IDT_Descriptor(uint16_t Segment_Selector, uint32_t Offset, uint16_t Type, IDT_S *IDT) {    IDT->Offset0_15 = Offset & 0xffff;    IDT->Segment_Selector = Segment_Selector;    IDT->Type.All = Type;    IDT->Offset16_31 = (Offset & 0xffff0000) >> 16;    return;}
说到string.h,我们需要自己实现,里面的模块以后会越用越多,全部实现的篇幅太长,这里只展示暂时用到的:
// libraries/string.c#include void* memcpy(void* dst, const void* src, uint8_t size) {    char *d;    const char *s;    if (dst == NULL || src == NULL) return NULL;    if ((char*)dst > ((char*)src + sizeof(src)) || ((char*)dst < (char*)src)) {        d = (char*)dst;        s = (char*)src;        while (size--) *d++ = *s++;    } else {        d = ((char*)dst + size - 1);        s = ((char*)src + size -1);        while (size --) *d-- = *s--;    }    return dst;}void* memset(void* dst, uint32_t val, uint32_t size) {    for (; 0 < size; size--) {        *(char*)dst = val;        dst++;    }    return dst;}uint32_t memcmp(void* buf1,void* buf2, uint32_t size) {    while (size --> 0) {        if (*(uint32_t*)buf1++ != *(uint32_t*)buf2++) {            return 0;        }    }    return 1;}void* memmove(void *dst, const void *src, size_t n) {    char *tmp;    const char *s;    if (dst <= src) {        tmp = dst;        s = src;        while (n--)            *tmp++ = *s++;    } else {        tmp = dst;        tmp += n;        s = src;        s += n;        while (n--)            *--tmp = *--s;    }    return dst;}

实现中断管理

接下来就是初始化整个IDT了,这部分内容比较多,但是都很简单。我们先捋一捋中断得整个流程,注意了,我们现在不考虑用户态和内核态(也就是特权级的问题):首先就是CPU(我们目前只讲单核处理器)接收到一个中断(向量号),这时保存现场,Intel官方是这么说的:

1. 将EFLAGS,CS和EIP寄存器的当前内容(按此顺序)压入堆栈。

2. 将错误代码(如果适用)压入堆栈。

3. 从中断门加载新代码段和新指令指针的段选择器(或陷阱门)分别进入CS和EIP寄存器。

4. 如果调用是通过中断门进行的,请清除EFLAGS寄存器中的IF标志。

5. 开始执行处理程序过程。

2b85d2776ac12312c5f2fad7b3793a2d.png

也就是说,我们要把当前所有寄存器和标准位保存起来,那么我们需要这个数据结构:

// include/interrupt.h...typedef struct Registers_S {    uint32_t ds;    uint32_t edi;    uint32_t esi;    uint32_t ebp;    uint32_t esp;    uint32_t ebx;    uint32_t edx;    uint32_t ecx;    uint32_t eax;    uint32_t Interrupt_Number; // 这里应该是Vector才对    uint32_t Error_Code;    uint32_t eip;    uint32_t cs;    uint32_t eflags;    uint32_t user_esp;    uint32_t user_ss;} Registers_S;...

前面我们说过各个中断号的归属,我们需要为每个中断号实现一个中断服务程序,以及中断注册函数,那么接下来我们的头文件就是这样:

// include/interrupt.h...extern void ISR0(void);extern void ISR1(void);extern void ISR2(void);extern void ISR3(void);extern void ISR4(void);extern void ISR5(void);extern void ISR6(void);extern void ISR7(void);extern void ISR8(void);extern void ISR9(void);extern void ISR10(void);extern void ISR11(void);extern void ISR12(void);extern void ISR13(void);extern void ISR14(void);extern void ISR15(void);extern void ISR16(void);extern void ISR17(void);extern void ISR18(void);extern void ISR19(void);extern void ISR20(void);extern void ISR21(void);extern void ISR22(void);extern void ISR23(void);extern void ISR24(void);extern void ISR25(void);extern void ISR26(void);extern void ISR27(void);extern void ISR28(void);extern void ISR29(void);extern void ISR30(void);extern void ISR31(void);void init_IDT(void);...

但是具体的函数都是保存现场,实现起来都基本是一样的(有错误号和无错误号差一个指令),一个个写太费事了,nasm为开发者提供了一个一劳永逸的办法:宏汇编。这可是个好东西:

; kernel/_Interrupt.asm[bits 32]extern ISR_Handler; nasm的宏定义; 有错误号使用空指令%define ERROR_CODE nop; 没有错误号就Push无效错误号%define NO_ERROR_CODE push 0; %macro 宏函数 参数个数%macro ISR_CODE 2; 参数1:%1,参数2:%2,...[global ISR%1]ISR%1:    cli                     ; 关闭中断    %2                      ; 估计情况决定是否放置Push无效错误号    push byte %1            ; Push中断向量号    pusha    mov ax,ds    push eax                ; 保存数据段描述符    mov ax,0x10    mov ds,ax    mov es,ax    mov fs,ax    mov gs,ax    mov ss,ax    push esp                ; Registers_S指针    call ISR_Handler        ; 调用相应中断处理函数这里可以使用,当前仅为测试                            ; ISR_Handler%1的方法实现对不同中断的不同处理                            ; 也可以使用[ISR_Handler + %1*4]函数指针数组的表示方法    add esp,4    pop ebx                 ; 恢复原来的数据段描述符    mov ds,bx    mov es,bx    mov fs,bx    mov gs,bx    mov ss,bx    popa    add esp,8           ; 跳过Error_Code    iret                ; 中断处理函数不能返回,需要使用iret或iretd打断;宏函数结束%endmacro;宏函数名 参数1,参数2,...ISR_CODE  0,NO_ERROR_CODEISR_CODE  1,NO_ERROR_CODEISR_CODE  2,NO_ERROR_CODEISR_CODE  3,NO_ERROR_CODEISR_CODE  4,NO_ERROR_CODEISR_CODE  5,NO_ERROR_CODEISR_CODE  6,NO_ERROR_CODEISR_CODE  7,NO_ERROR_CODEISR_CODE  8,ERROR_CODEISR_CODE  9,NO_ERROR_CODEISR_CODE 10,ERROR_CODEISR_CODE 11,ERROR_CODEISR_CODE 12,ERROR_CODEISR_CODE 13,ERROR_CODEISR_CODE 14,ERROR_CODEISR_CODE 15,NO_ERROR_CODEISR_CODE 16,NO_ERROR_CODEISR_CODE 17,ERROR_CODEISR_CODE 18,NO_ERROR_CODEISR_CODE 19,NO_ERROR_CODEISR_CODE 20,NO_ERROR_CODEISR_CODE 21,NO_ERROR_CODEISR_CODE 22,NO_ERROR_CODEISR_CODE 23,NO_ERROR_CODEISR_CODE 24,NO_ERROR_CODEISR_CODE 25,NO_ERROR_CODEISR_CODE 26,NO_ERROR_CODEISR_CODE 27,NO_ERROR_CODEISR_CODE 28,NO_ERROR_CODEISR_CODE 29,NO_ERROR_CODEISR_CODE 30,NO_ERROR_CODEISR_CODE 31,NO_ERROR_CODE

有了这些,C语言这边就小菜一碟了:

// kernel/interrupt.c#include #include #include IDT_S IDT[256];IDTR_S IDTR;static void init_IDT_Descriptor(uint16_t Segment_Selector, uint32_t Offset, uint16_t Type, IDT_S *IDT) {    IDT->Offset0_15 = Offset & 0xffff;    IDT->Segment_Selector = Segment_Selector;    IDT->Type.All = Type;    IDT->Offset16_31 = (Offset & 0xffff0000) >> 16;    return;}void ISR_Handler(Registers_S *Registers) {    printk(KERN_EMERG"InterruptNumber: %d\n", Registers->Interrupt_Number);}void init_IDT(void) {    // 这里不加括号编译器会警告!    IDTR.Limite = (sizeof(IDT_S) << 8) - 1;    IDTR.Base  = (uint32_t)&IDT;    memset((uint8_t*)&IDT, 0, sizeof(IDT_S) << 8);    init_IDT_Descriptor(0x08, (uint32_t)ISR0,  INT_GATE, &IDT[0]);    init_IDT_Descriptor(0x08, (uint32_t)ISR1,  INT_GATE, &IDT[1]);    init_IDT_Descriptor(0x08, (uint32_t)ISR2,  INT_GATE, &IDT[2]);    init_IDT_Descriptor(0x08, (uint32_t)ISR3,  INT_GATE, &IDT[3]);    init_IDT_Descriptor(0x08, (uint32_t)ISR4,  INT_GATE, &IDT[4]);    init_IDT_Descriptor(0x08, (uint32_t)ISR5,  INT_GATE, &IDT[5]);    init_IDT_Descriptor(0x08, (uint32_t)ISR6,  INT_GATE, &IDT[6]);    init_IDT_Descriptor(0x08, (uint32_t)ISR7,  INT_GATE, &IDT[7]);    init_IDT_Descriptor(0x08, (uint32_t)ISR8,  INT_GATE, &IDT[8]);    init_IDT_Descriptor(0x08, (uint32_t)ISR9,  INT_GATE, &IDT[9]);    init_IDT_Descriptor(0x08, (uint32_t)ISR10, INT_GATE, &IDT[10]);    init_IDT_Descriptor(0x08, (uint32_t)ISR11, INT_GATE, &IDT[11]);    init_IDT_Descriptor(0x08, (uint32_t)ISR12, INT_GATE, &IDT[12]);    init_IDT_Descriptor(0x08, (uint32_t)ISR13, INT_GATE, &IDT[13]);    init_IDT_Descriptor(0x08, (uint32_t)ISR14, INT_GATE, &IDT[14]);    init_IDT_Descriptor(0x08, (uint32_t)ISR15, INT_GATE, &IDT[15]);    init_IDT_Descriptor(0x08, (uint32_t)ISR16, INT_GATE, &IDT[16]);    init_IDT_Descriptor(0x08, (uint32_t)ISR17, INT_GATE, &IDT[17]);    init_IDT_Descriptor(0x08, (uint32_t)ISR18, INT_GATE, &IDT[18]);    init_IDT_Descriptor(0x08, (uint32_t)ISR19, INT_GATE, &IDT[19]);    init_IDT_Descriptor(0x08, (uint32_t)ISR20, INT_GATE, &IDT[20]);    init_IDT_Descriptor(0x08, (uint32_t)ISR21, INT_GATE, &IDT[21]);    init_IDT_Descriptor(0x08, (uint32_t)ISR22, INT_GATE, &IDT[22]);    init_IDT_Descriptor(0x08, (uint32_t)ISR23, INT_GATE, &IDT[23]);    init_IDT_Descriptor(0x08, (uint32_t)ISR24, INT_GATE, &IDT[24]);    init_IDT_Descriptor(0x08, (uint32_t)ISR25, INT_GATE, &IDT[25]);    init_IDT_Descriptor(0x08, (uint32_t)ISR26, INT_GATE, &IDT[26]);    init_IDT_Descriptor(0x08, (uint32_t)ISR27, INT_GATE, &IDT[27]);    init_IDT_Descriptor(0x08, (uint32_t)ISR28, INT_GATE, &IDT[28]);    init_IDT_Descriptor(0x08, (uint32_t)ISR29, INT_GATE, &IDT[29]);    init_IDT_Descriptor(0x08, (uint32_t)ISR30, INT_GATE, &IDT[30]);    init_IDT_Descriptor(0x08, (uint32_t)ISR31, INT_GATE, &IDT[31]);    //    // 加载IDTR    //    __asm__ ("lidtl (IDTR)");}
下面当然是测试一下了:

508cc8cba4892a822b49c4d6743c0a47.png

看,编译一气呵成!

中断函数注册

每个中断函数肯定不一样,我们就可以使用函数指针数组,这种情况下,有些函数可以先不用实现:

// include/interrupt.h...typedef void (*Interrupt_Handler)(Registers_S*);void RegisterInterrupt(uint8_t Number, Interrupt_Handler Handler);...
// kernel/interrupt.c...Interrupt_Handler InterruptHandlers[256] = {NULL};...// 这里可以动态注册中断函数void RegisterInterrupt(uint8_t Number, Interrupt_Handler Handler) {    InterruptHandlers[Number] = Handler;    return;}// 这里保存函数尚未实现时不会调用空函数void ISR_Handler(Registers_S *Registers) {    if (InterruptHandlers[Registers->Interrupt_Number] != NULL) {        InterruptHandlers[Registers->Interrupt_Number](Registers);    } else {        printk(KERN_EMERG"InterruptNumber: %d\n", Registers->Interrupt_Number);    }}...

运行结果还是一样的,今天就到这了!

关注"GuEes"公众号,了解更多消息

f488d807ffe158eb014ef826b8755202.gif

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

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

相关文章

可选版本 安装软件_【Linux软件】在Deepin系统下安装LibreOffice 6.1.4版本的方法

在Deepin系统下安装LibreOffice 6.4.1版本的方法使用的是Deepin系统&#xff0c;从深度应用商店看到有LibreOffice&#xff0c;安装了一下&#xff0c;发现ui是真的丑&#xff0c;就像是十年前的xp那样&#xff0c;记得前段时间在Windows用的时候UI基本重写了&#xff0c;发生了…

的表格点击全选_“逼死”强迫症的杂乱表格,原来3秒就能整理好!(必学)...

点击上方蓝字关注星标★不迷路本文作者&#xff1a;长小安视频策划&#xff1a;视频小分队本文编辑&#xff1a;竺兰哈喽大家好&#xff01;你们可爱的小安又来啦~相信在工作中经常接触表格的人&#xff0c;总会有这样的困扰&#xff1a;表格数据太多&#xff0c;行高列宽不合适…

linux 重置网卡配置_Linux不进入网卡配置文件更改静态ip

1、找到网卡配置文件名ls /etc/sysconfig/network-scripts/2、备份并查看原始配置文件(若原先有配置IP的&#xff0c;则按照第五点方式修改)3、修改随机自启和IP地址echo -e "IPADDR192.168.43.12 \nNETMASK255.255.255.0 \nGATEWAY192.168.43.1" >> /etc/sysc…

mysql从盘延迟_Mysql-主从延迟解决方法

Mysql 的主从延迟 指的是 主库受写入 后 到这个写入能体现在 从库上 的这段时间Mysql 的主从延迟 有两个原因&#xff1a;1. 写操作 已经在 主库中执行了&#xff0c;但是 binlog 还没有发送出去&#xff0c; 后者还在路上&#xff0c;没有被 从库收到2. 虽然 binlog 已经被 从…

php程序访问mysql数据实现查询_PHP+MySql实现后台数据的读取

我们使用的是PHP的php_mysqli扩展首先了解一些基础的用法1.连接数据库使用mysqli_connect()参数&#xff1a;①主机地址 ②MYSQL用户名 ③MYSQL密码 ④选择连接的数据库 ⑤端口号返回&#xff1a;如果连接成功返回资源类型的标识符号&#xff0c;如果失败返回false如果我们与My…

idea测试连接mysql报错08001_IDEA连接MySQL(版本8)数据库失败的解决方法(报错08001)...

在IDEA自带的数据库连接工具中&#xff0c;可以连接MySQL数据库&#xff0c;但是有的时候连接出现08001错误&#xff0c;连接不上数据库。1、一般配置如下所示Database处填写数据库名字&#xff1b;User处填写mysql设置好的用户名&#xff1b;密码同理填写设置好的&#xff1b;…

信息管理系统 github_Java+MySQL实现学生信息管理系统

基于Java swingMySQL实现学生信息管理系统&#xff1a;主要实现JDBC对学生信息进行增删改查&#xff0c;应付一般课设足矣&#xff0c;分享给大家。源码&#xff1a;https://github.com/ZhuangM/student.git1、 开发环境&#xff1a;jdk7MySQL5win7代码结构&#xff1a;model-d…

smith标准型_线性系统理论(八)多项式矩阵Smith-McMillan标准型计算方法

1 参考[1]Chenglin Li&#xff1a;线性系统理论&#xff08;七&#xff09;finite- and infinite-zeros​zhuanlan.zhihu.com多项式矩阵Smith-McMillan标准型确定方法分析2 单模矩阵法Chenglin Li&#xff1a;线性系统理论&#xff08;七&#xff09;finite- and infinite-zero…

mysql explain insert_简述Mysql Explain 命令

MySQL的EXPLAIN命令用于SQL语句的查询执行计划(QEP)。这条命令的输出结果能够让我们了解MySQL 优化器是如何执行SQL语句的。这条命令并没有提供任何调整建议&#xff0c;但它能够提供重要的信息帮助你做出调优决策。参考官方文档地址:为什么用explain . 如果你的页面返回结果很…

mysql数据库初识实训总结_MySQL(数据库)的初识

1.什么是数据库数据库(Database)是按照数据结构来组织、存储和管理数据的仓库2.什么是MySQLMySQL 是最流行的关系型数据库管理系统&#xff0c;在WEB应用方面 MySQL 是最好的RDBMS(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。3.关系数…

jdk1.8要安装什么mysql_Window下安装JDK1.8+Tomcat9.0.27+Mysql5.7.28的教程图解

JDK1.8安装下载打开链接: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html.下拉选择不同jdk版本&#xff0c;图示为window系统下载提示需登录账号可查看这个帖子Oracle账号.安装双击下载的安装包&#xff0c;按提示走就可以安装完成之后…

php mysql bootstart_PHP MySQL 创建数据库

PHP MySQL 创建数据库数据库存有一个或多个表。你需要 CREATE 权限来创建或删除 MySQL 数据库。使用 MySQLi 和 PDO 创建 MySQL 数据库CREATE DATABASE 语句用于在 MySQL 中创建数据库。在下面的实例中&#xff0c;创建了一个名为 "myDB" 的数据库&#xff1a;实例 (…

中班机器人歌曲_机器人幼儿园大班音乐教案

机器人幼儿园大班音乐教案作为一名无私奉献的老师&#xff0c;有必要进行细致的教案准备工作&#xff0c;教案是教学活动的总的组织纲领和行动方案。那么问题来了&#xff0c;教案应该怎么写&#xff1f;以下是小编为大家收集的机器人幼儿园大班音乐教案&#xff0c;供大家参考…

java中0x07_JAVA里0X00的表示

相信很多针对报文进行组织与拆解&#xff0c;在C、C里有memset的功能很容易完成字符串里填充0x00&#xff0c;在java里同样很容易做到&#xff0c;则是用\000,八进制来表示。测试代码如下&#xff1a;package j8583.example;import java.util.ArrayList;import java.util.Array…

微信第三方扫描登录 java源代码_微信开放平台基于网站应用授权登录源码(java)...

1. 第三方发起微信授权登录请求&#xff0c;微信用户允许授权第三方应用后&#xff0c;微信会拉起应用或重定向到第三方网站&#xff0c;并且带上授权临时票据code参数&#xff1b;2. 通过code参数加上AppID和AppSecret等&#xff0c;通过API换取access_token&#xff1b;3. 通…

java性能优化方案_Java性能优化要点

Java性能优化要点本文介绍如何通过以下几点从Java中挤压出性能&#xff0c;该大部分经验来自于Netty作者。JITJava即时编译器当Java执行runtime环境时&#xff0c;每遇到一个新的类&#xff0c;JIT编译器在此时就会针对这个类别进行编译(compile)被优化成相当精简的原生型指令码…

java注解 源码_详解Java注解教程及自定义注解

详解Java注解教程及自定义注解更新时间&#xff1a;2016-02-26 11:47:06 作者&#xff1a;佚名 我要评论(0)Java注解提供了关于代码的一些信息&#xff0c;但并不直接作用于它所注解的代码内容。在这个教程当中&#xff0c;我们将学习Java的注解&#xff0c;如何定制注解&…

win7卸载java_Win7彻底卸载Oracle 11g图文步骤(靠谱)

网上资料结合自己的操作整理出的一套靠谱的彻底卸载Oracle 11g的步骤&#xff01;(Win7)&#xff0c;具体内容详情如下所示&#xff1a;1&#xff1a;停掉所有Oracle相关的服务1.1打开服务方式如下&#xff1a;1.1.1&#xff1a;右击“计算机”–>管理–>服务和应用程序–…

vba与python相比2019_重大改变!Python 或将取代 VBA 成为 Excel 官方脚本语言

点击上方“CSDN”&#xff0c;选择“置顶公众号”关键时刻&#xff0c;第一时间送达&#xff01;如果微软的 Excel 中支持了人生苦短的 Python&#xff0c;你还会喜欢那个直接且易上手的 VBA 编程吗&#xff1f;近日&#xff0c;据国外媒体 BLEEPINGCOMPUTER 报道&#xff0c;微…

会话标识未更新 java_Appscan漏洞之会话标识未更新

本次针对 Appscan漏洞 会话标识未更新进行总结&#xff0c;如下&#xff1a;1. 会话标识未更新1.1、攻击原理在认证用户或者以其他方式建立新用户会话时&#xff0c;如果不使任何现有会话标识失效&#xff0c;攻击者就有机会窃取已认证的会话&#xff0c;此漏洞可结合XSS获取用…