【随笔】汇编(寄存器、内存模型、常用指令、语法)

文章目录

  • 一、简介
  • 二、寄存器
  • 三、内存模型
    • 3.1 Heap
    • 3.2 Stack
  • 四、指令
    • 4.1 示例
    • 4.2 语法
    • 4.3常用指令

一、简介

汇编语言(英语:assembly language)是任何一种用于电子计算机、微处理器、微控制器,或其他可编程器件的低级语言。在不同的设备中,汇编语言对应着不同的机器语言指令集。一种汇编语言专用于某种计算机系统结构,而不像许多高级语言,可以在不同系统平台之间移植。

使用汇编语言编写的源代码,然后通过相应的汇编程序将它们转换成可执行的机器代码。这一过程被称为汇编过程

汇编语言使用助记符Mnemonics)来代替和表示特定低级机器语言的操作。特定的汇编目标指令集可能会包括特定的操作数。许多汇编程序可以识别代表地址和常量的标签(Label)和符号(Symbols),这样就可以用字符来代表操作数而无需采取写死的方式。普遍地说,每一种特定的汇编语言和其特定的机器语言指令集是一一对应的。

许多汇编程序为程序开发、汇编控制、辅助调试提供了额外的支持机制。有的汇编语言编写工具经常会提供宏,它们也被称为宏汇编器。

现在汇编语言已不像其他大多数的程序设计语言一样被广泛用于程序设计,在今天的实际应用中,它通常被应用在底层硬件操作和高要求的程序优化的场合。驱动程序、嵌入式操作系统和实时运行程序中都会需要汇编语言

在这里插入图片描述

最早的时候,编写程序就是手写二进制指令,然后通过各种开关输入计算机,比如要做加法了,就按一下加法开关。后来,发明了纸带打孔机,通过在纸带上打孔,将二进制指令自动输入计算机。

为了解决二进制指令的可读性问题,工程师将那些指令写成了八进制。二进制转八进制是轻而易举的,但是八进制的可读性也不行。很自然地,最后还是用文字表达,加法指令写成 ADD。内存地址也不再直接引用,而是用标签表示。

这样的话,就多出一个步骤,要把这些文字指令翻译成二进制,这个步骤就称为 assembling,完成这个步骤的程序就叫做 assembler。它处理的文本,自然就叫做 aseembly code。标准化以后,称为 assembly language,缩写为 asm,中文译为汇编语言。

每一种 CPU 的机器指令都是不一样的,因此对应的汇编语言也不一样。本文介绍的是目前最常见的 x86 汇编语言。

  1. x86/x64 汇编:这是应用最广泛的汇编语言类型,用于 Intel 和 AMD 等 x86 架构的处理器,包括 32 位 (x86) 和 64 位 (x64)。这种汇编语言在 PC、服务器和许多嵌入式系统中被广泛使用。
  2. ARM 汇编:ARM 架构是另一种常见的处理器架构,广泛应用于移动设备、嵌入式系统以及一些服务器。ARM 汇编用于编写针对 ARM 处理器的低级代码。
  3. MIPS 汇编:MIPS 是一种 RISC 架构(精简指令集计算机),在一些嵌入式系统和早期的个人计算机中使用。MIPS 汇编用于编写针对 MIPS 处理器的低级代码。
  4. 68k 汇编:68k 汇编用于 Motorola 68k 系列处理器,这些处理器在历史上曾经被广泛应用于个人电脑、工作站和嵌入式系统中。
  5. AVR 汇编:AVR 是一种常见的低功耗、高性能的 8 位和 32 位微控制器架构,用于许多嵌入式系统和物联网设备。AVR 汇编用于编写针对 AVR 微控制器的低级代码。
  6. PowerPC 汇编:PowerPC 是 IBM、Motorola 和苹果公司合作开发的一种处理器架构,曾被广泛应用于苹果电脑和一些服务器中。PowerPC 汇编用于编写针对 PowerPC
    处理器的低级代码。

二、寄存器

寄存器(Register)是CPU 内用来暂存指令、数据和地址的电脑存储器。寄存器的存贮容量有限,读写速度非常快。在计算机体系结构里,寄存器存储在已知时间点所作计算的中间结果,通过快速地访问数据来加速计算机程序的执行。

寄存器位于存储器层次结构的最顶端,也是CPU可以读写的最快的存储器,事实上所谓的暂存已经不像存储器,而是非常短暂的读写少量信息并马上用到,因为通常程序执行的步骤中,这期间就会一直使用它。寄存器通常都是以他们可以保存的比特数量来计量,举例来说,一个8位寄存器或32位寄存器。在中央处理器中,包含寄存器的部件有指令寄存器(IR)、程序计数器和累加器。寄存器现在都以寄存器数组的方式来实现,但是他们也可能使用单独的触发器、高速的核心存储器、薄膜存储器以及在数种机器上的其他方式来实现出来。
在这里插入图片描述

寄存器也可以指代由一个指令之输出或输入可以直接索引到的寄存器组群,这些寄存器的更确切的名称为“架构寄存器”。例如,x86指令集定义八个32位寄存器的集合,但一个实现x86指令集的CPU内部可能会有八个以上的寄存器。

32位 CPU、64位 CPU 这样的名称,其实指的就是寄存器的大小。32 位 CPU 的寄存器大小就是4个字节。

x86架构CPU中常见的寄存器,包括通用寄存器、段寄存器、标志寄存器和指令指针寄存器。这些寄存器在不同的x86处理器中可能会有所不同,但是以下是通用的:

寄存器描述
EAX累加器 (Accumulator)
EBX基址寄存器 (Base)
ECX计数寄存器 (Counter)
EDX数据寄存器 (Data)
ESI源索引寄存器 (Source Index)
EDI目的索引寄存器 (Destination Index)
ESP栈指针寄存器 (Stack Pointer)
EBP基址指针寄存器 (Base Pointer)
EIP指令指针寄存器 (Instruction Pointer)
CS代码段寄存器 (Code Segment)
DS数据段寄存器 (Data Segment)
SS栈段寄存器 (Stack Segment)
ES附加段寄存器 (Extra Segment)
FS附加段寄存器 (Extra Segment)
GS附加段寄存器 (Extra Segment)
EFLAGS标志寄存器 (Flags)

这些寄存器的前缀"E"表示32位寄存器,若是64位寄存器,则为"R"。例如,64位下的累加器寄存器为RAX。(16位则没有前缀)

三、内存模型

3.1 Heap

寄存器只能存放很少量的数据,大多数时候,CPU 要指挥寄存器,直接跟内存交换数据。所以,除了寄存器,还必须了解内存怎么储存数据。

程序运行的时候,操作系统会给它分配一段内存,用来储存程序和运行产生的数据。这段内存有起始地址和结束地址,比如从0x1000到0x8000,起始地址是较小的那个地址,结束地址是较大的那个地址。

程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用malloc等命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址0x1000开始给他分配,一直分配到地址0x100A,如果再要求得到22个字节,那么就分配到0x1020
在这里插入图片描述

这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。

“堆”(Heap)也可以指代一种数据结构,与内存中的堆不同。在数据结构中,堆通常指的是一种特殊的树形结构,用于实现优先队列。

3.2 Stack

除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域。

int main() {
   int a = 2;
   int b = 3;
}

上面代码中,系统开始执行main函数时,会为它在内存里面建立一个frame),所有main的内部变量(比如a和b)都保存在这个帧里面。main函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

int main() {
   int a = 2;
   int b = 3;
   return add_ab(a, b);
}

上面代码中,main函数内部调用了add_ab函数。执行到这一行的时候,系统也会为add_ab新建一个帧,用来储存它的内部变量。也就是说,此时同时存在两个帧:main和add_ab。一般来说,调用栈有多少层,就有多少帧。

等到add_ab运行结束,它的帧就会被回收,系统会回到函数main刚才中断执行的地方,继续往下执行。通过这种机制,就实现了函数的层层调用,并且每一层都能使用自己的本地变量。

所有的帧都存放在 Stack,由于帧是一层层叠加的,所以 Stack 叫做栈。生成新的帧,叫做"入栈"(push);栈的回收叫做"出栈(pop)。Stack 的特点就是,最晚入栈的帧最早出栈(因为最内层的函数调用,最先结束运行),这就叫做"后进先出(LIFO)"的数据结构。每一次函数执行结束,就自动释放一个帧,所有函数执行结束,整个 Stack 就都释放了。
在这里插入图片描述

Stack 是由内存区域的结束地址开始,从高位(地址)向低位(地址)分配。 比如,内存区域的结束地址是0x8000,第一帧假定是16字节,那么下一次分配的地址就会从0x7FF0开始;第二帧假定需要64字节,那么地址就会移动到0x7FB0。

四、指令

4.1 示例

C语言:

在这里插入图片描述
转成汇编:

gcc -S temp.c

汇编代码:一个高级语言的简单操作,底层可能由几个,甚至几十个 CPU 指令构成。CPU 依次执行这些指令,完成这一步操作。(注释是我加的)

        .file   "temp.c"             ; 源文件名称
        .text                        ; 开始文本段
        .globl  add_ab               ; 声明 add_ab 是一个全局函数
        .type   add_ab, @function    ; 指定 add_ab 是一个函数类型
add_ab:                             ; 函数 add_ab 的开始
.LFB0:
        .cfi_startproc
        endbr64                     ; 提高对分支目标地址(BTA)的保护
        pushq   %rbp                ; 保存旧的栈基址指针
        .cfi_def_cfa_offset 16      ; 指定栈上的数据偏移量
        .cfi_offset 6, -16          ; 指定寄存器 %rbp 的偏移
        movq    %rsp, %rbp          ; 设置 %rbp 指向当前栈顶,建立新的栈帧
        .cfi_def_cfa_register 6     ; 指定 %rbp 为当前帧指针
        movl    %edi, -4(%rbp)      ; 将函数参数 %edi(第一个参数)存储到栈上的位置
        movl    %esi, -8(%rbp)      ; 将函数参数 %esi(第二个参数)存储到栈上的位置
        movl    -4(%rbp), %edx      ; 从栈中加载第一个参数 %edx
        movl    -8(%rbp), %eax      ; 从栈中加载第二个参数 %eax
        addl    %edx, %eax          ; 将 %edx 和 %eax 相加
        popq    %rbp                ; 恢复之前的栈基址指针
        .cfi_def_cfa 7, 8           ; 指定栈顶为 %rsp,栈基址为 %rbp
        ret                         ; 函数返回
        .cfi_endproc
.LFE0:
        .size   add_ab, .-add_ab    ; 指定函数 add_ab 的大小
        .section        .rodata      ; 开始只读数据段
.LC0:
        .string "x+y=%d"             ; 定义一个字符串常量,用于 printf 函数
        .text                        ; 返回文本段
        .globl  main                 ; 声明 main 函数为全局函数
        .type   main, @function      ; 指定 main 是一个函数类型
main:                               ; main 函数的开始
.LFB1:
        .cfi_startproc
        endbr64                     ; 提高对分支目标地址(BTA)的保护
        pushq   %rbp                ; 保存旧的栈基址指针
        .cfi_def_cfa_offset 16      ; 指定栈上的数据偏移量
        .cfi_offset 6, -16          ; 指定寄存器 %rbp 的偏移
        movq    %rsp, %rbp          ; 设置 %rbp 指向当前栈顶,建立新的栈帧
        .cfi_def_cfa_register 6     ; 指定 %rbp 为当前帧指针
        subq    $16, %rsp           ; 为局部变量分配空间
        movl    $2, -8(%rbp)        ; 将常量 2 存储到栈上的位置(第一个局部变量)
        movl    $3, -4(%rbp)        ; 将常量 3 存储到栈上的位置(第二个局部变量)
        movl    -4(%rbp), %edx      ; 从栈中加载第一个局部变量 %edx
        movl    -8(%rbp), %eax      ; 从栈中加载第二个局部变量 %eax
        movl    %edx, %esi          ; 复制 %edx 到 %esi
        movl    %eax, %edi          ; 复制 %eax 到 %edi
        call    add_ab              ; 调用 add_ab 函数
        movl    %eax, %esi          ; 将返回值(和)存储到 %esi 中
        leaq    .LC0(%rip), %rdi    ; 将字符串地址存储到 %rdi 中
        movl    $0, %eax            ;0 存储到 %eax
        call    printf@PLT          ; 调用 printf 函数
        movl    $0, %eax            ;0 存储到 %eax,表示返回值为 0
        leave                       ; 恢复栈帧
        .cfi_def_cfa 7, 8           ; 指定栈顶为 %rsp,栈基址为 %rbp
        ret                         ; 函数返回
        .cfi_endproc
.LFE1:
        .size   main, .-main        ; 指定函数 main 的大小
        .ident  "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0" ; 编译器的版本信息
        .section        .note.GNU-stack,"",@progbits    ; 用于指示栈是否是可执行的
        .section        .note.gnu.property,"a"           ; GNU 属性节
        .align 8
        .long    1f - 0f
        .long    4f - 1f
        .long    5
0:
        .string  "GNU"
1:
        .align 8
        .long    0xc0000002
        .long    3f - 2f
2:
        .long    0x3
3:
        .align 8
4:

4.2 语法

汇编语言的语法可以根据不同的体系结构有所不同,以下是 x86 架构汇编语言的一般语法:

指令格式:

[label:]   mnemonic   [operands]   [;comment]
  • label:可选的,用于标识代码的位置,以冒号结尾。
  • mnemonic:指令助记符,代表要执行的操作。
  • operands:操作数,指定指令要操作的数据。
  • comment:注释,用于解释指令的作用或其他信息。

基本元素:

  • 指令(Instructions):表示要执行的操作,如 mov, add, sub, jmp 等。
  • 寄存器(Registers):用于存储数据和执行操作,如 eax, ebx, ecx 等。
  • 内存操作数(Memory Operands):表示内存中的数据,如 [address][ebx] 等。
  • 立即数(Immediate Values):常数值,直接指定在指令中,如 5, 0xFF, 0b1010 等。
  • 标签(Labels):用于标识代码的位置,通常在循环或条件跳转中使用。

指令示例:

; 注释示例
section .text             ; 代码段开始

    global _start         ; 全局声明,程序入口

_start:
    mov eax, 5            ; 将立即数 5 存入 eax 寄存器
    mov ebx, 3            ; 将立即数 3 存入 ebx 寄存器
    add eax, ebx          ; eax = eax + ebx
    sub eax, 1            ; eax = eax - 1

    cmp eax, 10           ; 比较 eax 和立即数 10
    jl  less_than_ten     ; 如果 eax < 10,则跳转到 less_than_ten 标签

    mov eax, 10           ; 如果不满足条件,将立即数 10 存入 eax

less_than_ten:
    mov ecx, eax          ; 将 eax 的值存入 ecx

    ; 调用系统调用 exit
    mov eax, 1            ; 系统调用号 1 表示 exit
    xor ebx, ebx          ; 退出码为 0
    int 0x80              ; 调用系统调用

section .data             ; 数据段开始
    my_var db 'A'         ; 定义一个名为 my_var 的字节数据,值为字符 'A'

汇编语言对空白字符不敏感,但大小写敏感。同时,注释以分号 ; 开始,可以在语句后添加注释。

4.3常用指令

常用的汇编指令以及简要说明和示例:

指令说明示例
mov将数据从一个位置复制到另一个位置mov eax, ebx
add加法操作add eax, ebx
sub减法操作sub eax, ebx
mul无符号乘法mul ebx
div无符号除法div ebx
and按位与操作and eax, ebx
or按位或操作or eax, ebx
xor按位异或操作xor eax, ebx
not按位取反操作not eax
shl逻辑左移shl eax, 1
shr逻辑右移shr eax, 1
cmp比较两个操作数的值,设置标志位cmp eax, ebx
test测试两个操作数的位并设置标志位test eax, ebx
jmp无条件跳转jmp label
je等于时跳转je label
jne不等于时跳转jne label
jl小于时跳转jl label
jg大于时跳转jg label
jle小于等于时跳转jle label
jge大于等于时跳转jge label
call调用函数或过程call function_name
ret从函数返回ret
push将数据压入栈push eax
pop从栈中弹出数据pop eax
nop空操作nop
int产生软中断int 0x80
hlt暂停处理器执行hlt

示例:

假设有以下寄存器的初始值:

  • eax = 5
  • ebx = 3

示例汇编指令和注释如下:

; 将 ebx 加到 eax 中
add eax, ebx    ; eax = 5 + 3 = 8

; 乘以 ebx 的值
mul ebx         ; eax = 8 * 3 = 24

; 将结果保存到变量 result 中
mov [result], eax ; result = 24

; 比较 eax 和 ebx 的值
cmp eax, ebx    ; 设置标志位,用于后续条件跳转

; 如果 eax 大于 ebx,跳转到 label1
jg label1       ; 如果 24 > 3,则跳转

; 否则跳转到 label2
jmp label2

label1:
; 如果跳转到这里,说明 24 > 3
; 这里可以添加一些操作

label2:
; 如果跳转到这里,说明 24 <= 3
; 这里可以添加一些操作

; 函数调用示例
call my_function  ; 调用函数 my_function

; 从函数返回
ret

; 定义一个标签,用于跳转
label3:
; 这里可以添加一些操作

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

项目打包部署流程(前后端本地打包部署)

本地部署 后端 首先打成jar包&#xff0c;尝试在本地运行 由于项目是maven父子工程 首先要把父项目进行install 配置好jdk环境&#xff08;两套环境可选&#xff09;这里使用的是jdk17 path中把java的环境置顶 查看当前jdk环境&#xff08;保证正确的jdk环境&#xff09; 在…

以太坊开发学习-solidity(二)值类型

文章目录 第一个Solidity程序编译并部署代码变量值类型1. 布尔型2. 整型3. 地址类型4. 定长字节数组 第一个Solidity程序 开发工具&#xff1a;remix 本教程中&#xff0c;我会用remix来跑solidity合约。remix是以太坊官方推荐的智能合约开发IDE&#xff08;集成开发环境&#…

斯坦福大学推出pyvene:开创性的AI模型干预Python库

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

python讲解(3)

目录 一.类型的意义 二.动态静态类型 三.程序的输入和输出 输出&#xff1a; 输入&#xff1a; 四.运算符 算术运算符 % ** // 关系运算符 一.类型的意义 1.区别 不同的类型占据不同的内存&#xff0c;单位是字节int 默认是4个字节&#xff0c;根据需要动态扩…

Vue2(五):收集表单数据、过滤器、自定义指令、Vue的生命周期

一、收集表单数据 爱好&#xff1a;学习<input type"checkbox" value"study" v-model"hobby">打游戏<input type"checkbox" value"games" v-model"hobby">吃饭<input type"checkbox" v…

前端安全——最新:lodash原型漏洞从发现到修复全过程

人生的精彩就在于你永远不知道惊喜和意外谁先来&#xff0c;又是一个平平无奇的早晨&#xff0c;我收到了一份意外的惊喜——前端某项目出现lodash依赖原型污染漏洞。咋一听&#xff0c;很新奇。再仔细一看&#xff0c;呕吼&#xff0c;更加好奇了~然后就是了解和修补漏洞之旅。…

C++开发基础——类对象与构造析构

一、基础概念 类&#xff1a;用户自定义的数据类型。 对象&#xff1a;类类型的变量&#xff0c;类的实例。 类的成员&#xff1a;成员变量和成员函数。 成员变量&#xff1a;类中定义的变量。 成员函数&#xff1a;类中定义的函数。 定义类的代码样例&#xff1a; class…

带你深入了解数据库的事务

为什么要使用事务 日常开发中&#xff0c;很多操作&#xff0c;不是通过一个SQL就能完成的&#xff0c;往往需要多个SQL配合完成 当执行多个SQL操作的时候&#xff0c;如果中间出现了特殊的情况&#xff08;程序崩溃&#xff0c;系统奔溃&#xff0c;网络断开&#xff0c;主机…

大数据任务调度平台选型建议

一 背景 数仓建设过程中&#xff0c;随着业务发展&#xff0c; ETL 任务调度越来越多&#xff0c;并且这些任务的形态多种多样。怎么样让大量的 ETL 任务准确的完成调度而不出现问题&#xff0c;甚至在任务调度执行中出现错误的情况下&#xff0c;任务能够完成自我恢复甚至执行…

分治法排序:原理与C语言实现

分治法排序&#xff1a;原理与C语言实现 一、分治法与归并排序概述二、归并排序的C语言实现三、归并排序的性能分析四、归并排序的优化 在计算机科学中&#xff0c;分治法是一种解决问题的策略&#xff0c;它将一个难以直接解决的大问题&#xff0c;分割成一些规模较小的相同问…

前后端分离项目部署服务器教程--实践成功

文章目录 项目介绍流程1租界云服务2通过远程软件连接服务器3部署前后端代码停止功能文件 环境配置1.安装jdk2.安装Nginx3.安装mysql数据库 花了将近一天部署前后端的项目&#xff0c;写一个日志记录一下&#xff0c;话说孰能生巧。明天把服务器恢复初始在部署一下。 项目介绍 …

【Node.js从基础到高级运用】十四、Node.js 错误处理与日志记录

引言 在这篇博客文章中&#xff0c;我们将深入探讨Node.js中的错误处理和日志记录的最佳实践。我们会了解如何在Node.js应用程序中有效地捕获和处理错误&#xff0c;并利用日志库如morgan来记录应用程序的活动和错误信息。 第1部分&#xff1a;Node.js中的错误处理 同步代码中…

【Node.js从基础到高级运用】十三、NodeJS中间件高级应用

在现代web开发中&#xff0c;Node.js因其高效和灵活性而备受青睐。其中&#xff0c;中间件的概念是构建高效Node.js应用的关键。在这篇博客文章中&#xff0c;我们将深入探讨Node.js中间件的高级应用&#xff0c;包括创建自定义中间件、使用第三方中间件等。我们将从基础讲起&a…

CTF题型 Http请求走私总结Burp靶场例题

CTF题型 Http请求走私总结&靶场例题 文章目录 CTF题型 Http请求走私总结&靶场例题HTTP请求走私HTTP请求走私漏洞原理分析为什么用前端服务器漏洞原理界定标准界定长度 重要!!!实验环境前提POST数据包结构必要结构快速判断Http请求走私类型时间延迟CL-TETE-CL 练习例题C…

三 C#插入排序算法

简介 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 插入排序实现原理 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 具体实现步骤…

Java类的初始化顺序

请直接看原文: Java类的初始化顺序_java创建顺序-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 对于静态变量、静态初始化块、变量、初始化块、构造器&#xff0c;它们的…

滴答拍摄影项目|基于Spring Boot框架+ Mysql+Java+ Tomcat的滴答拍摄影项目设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

centos创建并运行一个redis容器 并支持数据持久化

步骤 : 创建redis容器命令 docker run --name mr -p 6379:6379 -d redis redis-server --appendonly yes 进入容器 : docker exec -it mr bash 链接redis : redis-cli 查看数据 : keys * 存入一个数据 : set num 666 获取数据 : get num 退出客户端 : exit 再退…

elk收集k8s微服务日志

一、前言 使用filebeat自动发现收集k8s的pod日志&#xff0c;这里分别收集前端的nginx日志&#xff0c;还有后端的服务java日志&#xff0c;所有格式都是用json格式&#xff0c;建议还是需要让开发人员去输出java的日志为json&#xff0c;logstash分割java日志为json格式&#…

java实现word转pdf

引入依赖包 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId…