鸿蒙内核源码分析 (编码方式篇) | 机器指令是如何编码的?

news2025/1/19 13:10:33

本篇说清楚 ARM指令是如何被编码的,机器指令由哪些部分构成,指令有哪些类型,每种类型的语法又是怎样的 ?

代码案例 | C -> 汇编 -> 机器指令

看一段C语言编译(clang)成的最后的机器指令(armv7)

int main(){
    int a = 0;
    if( a != 1) 
        a = 2*a + 1;
    return a;
}

生成汇编代码如下:

    main:
60c: sub	sp, sp, #8
610: mov	r0, #0
614: str	r0, [sp, #4]
618: str	r0, [sp]
61c: ldr	r0, [sp]
620: cmp	r0, #1
624: beq	640 <main+0x34>
628: b	62c <main+0x20>
62c: ldr	r1, [sp]
630: mov	r0, #1
634: orr	r0, r0, r1, lsl #1
638: str	r0, [sp]
63c: b	640 <main+0x34>
640: ldr	r0, [sp]
644: add	sp, sp, #8
648: bx	lr

汇编代码对应的机器指令如下图所示:

便于后续分析,将以上代码整理成如下表格

汇编代码机器指令(十六进制表示)机器指令(二进制表示)
sub sp, sp, #8e24dd0081110 0010 0100 1101 1101 0000 0000 1000
mov r0, #0e3a000001110 0011 1010 0000 0000 0000 0000 0000
str r0, [sp, #4]e58d00041110 0101 1000 1101 0000 0000 0000 0100
str r0, [sp]e58d00001110 0101 1000 1101 0000 0000 0000 0000
ldr r0, [sp]e59d00001110 0101 1001 1101 0000 0000 0000 0000
cmp r0, #1e35000011110 0011 0101 0000 0000 0000 0000 0001
beq 640 <main+0x34>0a0000050000 1010 0000 0000 0000 0000 0000 0101
b 62c <main+0x20>eaffffff1110 1010 1111 1111 1111 1111 1111 1111
ldr r1, [sp]e59d10001110 0101 1001 1101 0001 0000 0000 0010
mov r0, #1e3a000021110 0011 1010 0000 0000 0000 0000 0001
orr r0, r0, r1, lsl #1e18000811110 0001 1000 0000 0000 0000 1000 0001
str r0, [sp]e58d00001110 0101 1000 1101 0000 0000 0000 0000
b 640 <main+0x34>eaffffff1110 1010 1111 1111 1111 1111 1111 1111
ldr r0, [sp]e59d10001110 0101 1001 1101 0001 0000 0000 0000
add sp, sp, #8e28dd0081110 0010 1000 1101 1101 0000 0000 1000
bx lre12fff1e1110 0001 0010 1111 1111 1111 0001 1110

CPSR寄存器

在理解本篇之前需了解下CPSR寄存器的高4[31,28] 表达的含义。关于寄存器的详细介绍可翻看 系列篇的 (寄存器篇)

N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行!意义重大!

  • CPSR的第31位是 N,符号标志位。它记录相关指令执行后,其结果是否为负。
    如果为负 N = 1,如果是非负数 N = 0
  • CPSR的第30位是Z0标志位。它记录相关指令执行后,其结果是否为0
    如果结果为0。那么Z = 1。如果结果不为0,那么Z = 0
  • CPSR的第29位C,进位标志位(Carry)。一般情况下,进行无符号数的运算。
    加法运算:当运算结果产生了进位时(无符号数溢出),C=1,否则C=0
    减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1
  • CPSR的第28位是V,溢出标志位(Overflow)。在进行有符号数运算的时候,
    如果超过了机器所能标识的范围,称为溢出。

指令格式

ARM 指令流是一连串的字对齐的四字节指令流。每个 ARM 指令是一个单一的 32 位字(4字节),如图(3)

解读
图为ARM指令的编码一级格式,所有的指令都必须符合一级格式,分成三部分:

  • 条件域: cond[31:28]表示,条件域会影响CPSR的条件码N、Z、C、V标志位。
  • 类型域: op1[27:25], op[4]arm将指令分成了六大类型 。
  • 操作域: 剩下的[24:5][4:0] 即图中的空白位/保留位,这是留给下级自由发挥的,不同的类型对这些保留位有不同的定义。可以理解为因类型变化而变化的二级格式。
  • 那有了二级格式会不会有三级格式 ? 答案是必须有, 二级格式只会对保留位定义部分位,会留一部分给具体的指令格式自由发挥。
  • 一定要理解这种层次结构才能理解ARM指令集的设计总思路,因为RISC(精简指令集) 的指令长度是固定的16/32/64位,以32位为例,所有的指令设计必须全用32位来表示,如果只有一层结构是难以满足众多的指令设计需求的,要灵活有包容就得给适当的空间发挥。

条件域

cond 为条件域,每一条可条件执行的条件指令都有4位的条件位域,2^4能表示16种条件

cond助记符含义(整型)含义(浮点型)条件标志
0000EQ相等相等Z == 1
0001NE不等不等或无序Z == 0
0010CS进位大于等于或无序C == 1
0011CC进位清除小于C == 0
0100MI减、负数小于N == 1
0101PL加、正数或 0大于等于或无序N == 0
0110VS溢出无序V == 1
0111VC未溢出有序V == 0
1000HI无符号大于大于或无序C == 1 and Z == 0
1001LS无符号小于或等于小于或等于C == 0 or Z == 1
1010GE有符号大于或等于大于或等于N == V
1011LT有符号小于小于或无序N != V
1100GT有符号大于大于Z == 0 and N ==V
1101LE有符号大于或等于小于等于或无序Z == 1 or N != V
1110无条件无条件任何
  • 大部分的指令都是 1110 = e,无条件执行指令,只要看到 e开头的机器指令都属于这类
    beq 640 <main+0x34>	// 机器码 0a000005 <=>	0000 1010 0000 0000 0000 0000 0000 0101
                                                    0000	EQ	Equal(相等)	Z == 1

类型域

图(3) 的 op1 域位于 bits[27:25],占三位;op 域位于 bit[4],占一位。它们的取值组合在一起,决定指令所属的分类(Instruction Class),其值对应的关系如下

op1    op    指令类型
00x    -     数据处理以及杂项指令
010    -     load/store word类型 或者 unsigned byte
011    0     同上
011    1     媒体接口指令
10x    -     跳转指令和块数据操作指令,块数据操作指令指 STMDA 这类,连续内存操作。
11x    -     协处理器指令和 svc 指令,包括高级的 SIMD 和浮点指令。

操作域

操作域是因类型变化而变化的二级格式 ,作用于保留位。包含

00x | 数据处理类指令

  • 上图为涉及数据处理指令的对应编码,由 op[占5位]op2[占2位]两项来确定指令的唯一性
  • 一般情况下只需op指定唯一性,图中 SUB指令对应为 0010x,而代码案例中的第一句
    sub	sp, sp, #8  // 机器码 e24dd008 <=> 1110 001`0 0100` 1101 1101 0000 0000 1000
对应`[24:20]`位就是`0 0100`,从而`CPU`在译码阶段将其解析为`SUB`指令执行
  • 需要用到op2的是 MOV系列指令,包括逻辑/算术左移右移,例如:
    mov r0, #0	//e3a00000 <=> 1110 0011 1010 0000 0000 0000 0000 0000

中的op = 1 1010 ,op2 = 00 对应 MOV(register,ARM) on page A8-489 00x中的x表示数据处理分两种情况
* 000 无立即数参与(寄存器之间) ,图A5.2.1 表示了这种情况 [27:25]= 000
* 001 有立即参与的运算,例如 mov r0, #0 中的 [27:25]= 001,此处未展示图,可前往 ARM体系架构参考手册.pdf 翻看

010 | 加载存储指令

  • Load/store是一组内存访问指令,用来在ARM寄存器和内存之间进行数据传送,ARM指令中有3种基本的数据传送指令

    • 单寄存器 Load/Store 内存访问指令(single register):这些指令为ARM寄存器和存储器提供了更灵活的单数据项传送方式。数据可以使字节,16位半字或32位字
    • 多寄存器 Load/Store 内存访问指令:可以实现大量数据的同时传送,主要用于进程的进入和退出、保存和恢复工作寄存器以及复制寄存器中的一片(一块)数据
    • 寄存器交换指令(single register swap): 实现寄存器数据和内存数据进行交换,而且是在一条指令中完成,执行过程中不会受到中断干扰
  • 出现在代码案例中的

    str r0, [sp, #4] //  机器码 e58d0004 <=>	1110 0101 1000 1101 0000 0000 0000 0100
    str r0, [sp]	 //  机器码 e58d0000 <=>	1110 0101 1000 1101 0000 0000 0000 0000
                         将r0中的字数据写入以SP为地址的存储器中
    ldr r0, [sp]	 //  机器码 e59d0000 <=>	1110 0101 1001 1101 0000 0000 0000 0000
                         存储器地址地址为SP的数据读入r0 寄存器

[27:25] = 010说明都属于这类指令,完成对内存的读写,包括 LDRLDRBLDRHSTRSTRBSTRH六条指令。

ldr 为加载指令,但是加载到内存还是寄存器,这该怎么记 ? 因为主角是CPU,加载有进来的意思,将内容加载至寄存器中。STR有出去的意思,将内容保存到内存里。

[sp]相当于C语言的 *sp ,sp 指向程序运行栈当前位置

  • 具体可看 >> ARM的六条访存指令集—LDR、LDRB、LDRH、STR、STRB、STRH
010 | 多媒体指令

多媒体指令使用较少,但是它涉及指令却很多

10x | 跳转/分支/块数据处理 指令

  • 出现在代码案例中的
    beq 640 <main+0x34>	// 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101
    b 62c <main+0x20>	// 机器码 eaffffff <=> 1110 1010 1111 1111 1111 1111 1111 1111

[27:25] = 101说明都属于这类指令

  • 听得很多的poppush也属于这类,成块的数据操作,例如push常用于将函数的所有参数一次性入栈。
  • 内存 <> 寄存器 批量数据搬运指令 STMDA (STMED) LDMDA/LDMF
11x | 软中断/协处理器 指令

  • 其中最有名的就是svc 0,在系列篇中曾多次提及它,此处详细说下 svc, svc全称是 Supervisor Call, SupervisorCPU的管理模式,svc导致处理器进入管理模式,很多人问的系统调用底层是怎么实现的? svc就是答案。
  • 例如 printf是个标准库函数,在标准库的底层代码中会调用 svc 0,导致用户态的 ARM 程序通常将系统调用号传入 R7 寄存器(也被鸿蒙内核使用),然后用 SVC 指令调用 0 号中断来直接执行系统调用,
  • 在以前的ARM架构版本中,SVC指令被称为SWI,软件中断。
  • 描述svc功能的详细伪代码如下,请尝试读懂它
      The TakeSVCException() pseudocode procedure describes how the processor takes the exception:
      // TakeSVCException()
      // ==================
      TakeSVCException()
      // Determine return information. SPSR is to be the current CPSR, after changing the IT[]
      // bits to give them the correct values for the following instruction, and LR is to be
      // the current PC minus 2 for Thumb or 4 for ARM, to change the PC offsets of 4 or 8
      // respectively from the address of the current instruction into the required address of
      // the next instruction, the SVC instruction having size 2bytes for Thumb or 4 bytes for ARM.
      ITAdvance();
      new_lr_value = if CPSR.T == '1' then PC-2 else PC-4;
      new_spsr_value = CPSR;
      vect_offset = 8;
      // Check whether to take exception to Hyp mode
      // if in Hyp mode then stay in Hyp mode
      take_to_hyp = (HaveVirtExt() && HaveSecurityExt() && SCR.NS == '1' && CPSR.M == '11010');
      // if HCR.TGE is set to 1, take to Hyp mode through Hyp Trap vector
      route_to_hyp = (HaveVirtExt() && HaveSecurityExt() && !IsSecure() && HCR.TGE == '1'
      && CPSR.M == '10000'); // User mode
      // if HCR.TGE == '1' and in a Non-secure PL1 mode, the effect is UNPREDICTABLE

      preferred_exceptn_return = new_lr_value;
      if take_to_hyp then
      EnterHypMode(new_spsr_value, preferred_exceptn_return, vect_offset);
      elsif route_to_hyp then
      EnterHypMode(new_spsr_value, preferred_exceptn_return, 20);
      else
      // Enter Supervisor ('10011') mode, and ensure Secure state if initially in Monitor
      // ('10110') mode. This affects the Banked versions of various registers accessed later
      // in the code.
      if CPSR.M == '10110' then SCR.NS = '0';
      CPSR.M = '10011';
      // Write return information to registers, and make further CPSR changes: IRQs disabled,
      // IT state reset, instruction set and endianness set to SCTLR-configured values.
      SPSR[] = new_spsr_value;
      R[14] = new_lr_value;
      CPSR.I = '1';
      CPSR.IT = '00000000';
      CPSR.J = '0'; CPSR.T = SCTLR.TE; // TE=0: ARM, TE=1: Thumb
      CPSR.E = SCTLR.EE; // EE=0: little-endian, EE=1: big-endian
      // Branch to SVC vector.
      BranchTo(ExcVectorBase() + vect_offset);
  • 这部分内容在系列篇 (寄存器篇) ,(系统调用篇) ,(标准库篇) 中都有提及。

具体指令

细看几条代码案例出现的常用指令

sub sp, sp, #8
sub	sp, sp, #8  // 机器码 e24dd008 < = > 1110 0010 0100 1101 1101 0000 0000 1000

是减法操作指令,减法编码格式为

图中除了给出格式语法还有一段伪代码用于描述指令的使用条件

  • sp为 13号寄存器, lr为 14号寄存器 ,pc为 15号寄存器。

  • 如果是PC寄存器(Rn = 15)S等于0 查看 ADR指令。。

  • 如果是SP寄存器(Rn = 13) 看 SUB(申请栈空间)。

  • 如果是PC寄存器(Rd = 15)S等于1 。查看 subs pc lr相关指令

  • 套用格式结合源码

    condop1操作码SRnRdimm12(立即数)
    111000100100110111010000 0000 1000
    无条件执行表示数据处理SUBspsp8
mov r0, #0

mov r0, #0	//e3a00000	1110 0011 1010 0000 0000 0000 0000 0000
bx lr

bx lr	e12fff1e	1110 0001 0010 1111 1111 1111 0001 1110
  • Rm = 1110 对应 lr 寄存器 ,其相当于高级语言的 return,函数执行完了需切回到调用它的函数位置继续执行,lr保存的就是那个位置,从哪里来就回到哪里去。

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

在这里插入图片描述

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

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

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

相关文章

dataframe数据常用python操作

dataframe数据常用python操作 dataframe数据常用知识点1.创建dataframe1.1使用字典创建DataFrame&#xff1a;1.2使用列表创建DataFrame&#xff1a;1.3使用numpy数组创建DataFrame&#xff1a;1.4从TXT文件中创建DataFrame&#xff1a;1.5从CSV文件中创建DataFrame&#xff1a…

卡巴斯基:2024年Q1漏洞和利用报告

近日&#xff0c;卡巴斯基发布了《2024年Q1漏洞和利用报告》&#xff0c;提供了一系列有洞察力的统计和分析快照&#xff0c;揭示了新漏洞和利用的发展趋势&#xff0c;以及攻击者最常利用的漏洞概述。为组织获悉和应对相关威胁提供了有价值的见解。 已注册漏洞统计数据 为了…

大企业总部与分部组网方案

在全球化的经济环境中&#xff0c;大企业往往设有总部和多个地理分散的分部。为了确保信息的快 速流通、资源的优化配置以及管理的高效运作&#xff0c;构建一个稳定、安全且高效的组网方案显 得尤为重要。本文将探讨大企业如何通过技术手段和管理策略&#xff0c;实现总部与分…

常见加解密算法03 - RC4逆向认识

各位聪明绝顶&#xff0c;才高八斗的读者们你们好&#xff01;今天我们主要讨论编译之后的RC4算法识别。 题外话&#xff0c;之前看到一个蛋疼的小知识&#xff0c;说“势”这个字最好不好查词典释义。我是很好奇的&#xff0c;果然后来无法直视势不可挡这个成语。 言归正传&am…

Python tensor向量维度转换,不同维度的向量转化为相同的维度,经过全连接层MLP的维度转换,代码实战

问题&#xff1a;在机器学习特征工程中&#xff0c;假如每类特征需要转化为相同的维度进行拼接&#xff0c;那该怎么办呢&#xff1f;接一个全连接层MLP就可以了。 例子&#xff1a;将&#xff08;128,64&#xff09; 维度的向量转化为&#xff08;128,32&#xff09;维。 impo…

安装ps提示找不到msvcp140.dll,无法继续执行此代码如何修复

MSVCP140.dll&#xff0c;作为Windows操作系统中的一个关键组件&#xff0c;扮演着不可或缺的角色&#xff0c;尤其对于基于C开发的应用程序而言。本文旨在深入探讨这一动态链接库文件的功能、重要性、常见问题及解决方案&#xff0c;为您提供全面的MSVCP140.dll指南。 一、MSV…

zookeeper集群部署以及zookeeper原理

文章目录 简介工作原理特性官网地址准备节点准备环境准备JAVA主机映射 部署 简介 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服…

触摸播放视频,并用iframe实现播放外站视频

效果&#xff1a; html: <div:style"{ height: homedivh }"class"rightOne_content_div_div"mouseenter"divSeenter(i)"mouseleave"divLeave(i)"click"ItemClick(i)"><!-- isUser是否是用户上传 --><divv-if…

react18【系列实用教程】组件 (2024最新版 | 含父子组件传值、兄弟组件传值、越层组件传值、“插槽“)

什么是组件&#xff1f; 一个组件就是用户界面的一部分&#xff0c;它可以有自己的逻辑和外观。 组件之间可以互相嵌套&#xff0c;也可以复用多次 为什么要用组件&#xff1f; 组件能让开发者像搭积木一样快速构建一个完整的庞大应用&#xff0c;大大提升了开发效率&#xff…

刚刚OpenAI发布ChatGPT-4o模型,免费使用GPT4o并免费提供更多功能

就在今日凌晨1点&#xff0c;OpenAI举行了春季发布会&#xff0c;发布了GPT-4o 并免费提供更多功能。 亲测GPT-4o已经可以免费试用&#xff0c;每个人都可以使用它并从中受益&#xff0c;GPT4终于不再是少部分人的玩物。 点击加入ChatGPT4交流群&#xff1a;https://www.aijour…

vue3专栏项目 -- 五、权限管理(上)

一、登录部分 1、第一部分&#xff1a;获取token 前面我们主要是在获取数据上下功夫&#xff0c;到目前为止我们已经能获取首页和详情页的数据了&#xff0c;现在我们将数据转移到权限管理上来&#xff0c;也就是说我们要处理用户登录、注册等一系列的行为&#xff0c;在这部…

JavaScript:正则表达式属于字符串吗-不属于/字符串转正则表达式的两种方法

一、需求描述 js 字符串转正则表达式 二、理解正则表达式属于字符串吗? 正则表达式不属于字符串&#xff0c;它是一种用于匹配、查找和操作文本的模式。正则表达式是一种特殊的语法&#xff0c;用于描述字符串的特征。通过使用正则表达式&#xff0c;可以检查一个字符串是否…

找不到vcomp140.dll多种修复方法分享,轻松解决dll报错问题

当你在尝试运行某款软件时&#xff0c;系统突然弹出一个错误提示&#xff0c;明确指出“vcomp140.dll文件丢失”&#xff0c;这个错误通常会导致某些应用程序无法正常运行。为了解决这个问题&#xff0c;我们需要采取一些修复措施。本文将详细介绍vcomp140.dll丢失的5种修复方法…

Java的VO,BO,PO,DO,DTO

写在前面 本文看下VO&#xff0c;BO&#xff0c;PO&#xff0c;DO&#xff0c;DTO&#xff0c;都是啥&#xff01; 1&#xff1a;正文 先看一张图&#xff0c;看了图就能知道个大概了&#xff1a; 1.1&#xff1a;PO 全称是persistent object&#xff0c;对应数据的表&am…

记录一次 vue2 前端项目整合过程

整合成功效果图 具体说明&#xff1a; 项目A是现在的vue2前端项目&#xff0c;项目B是一个开源的工作流前端&#xff0c;项目后端代码已经整合了&#xff0c;就不多提了。这里主要记录下前端整合的过程和思路。 1、开源工作流里面的功能&#xff0c;拷贝到自己对应的vue2项目里…

【Linux】解析键盘组合键产生信号的完整过程:从硬件中断到信号发送

前言 每一个了解Linux的都知道这样一个知识&#xff0c;CtrlC组合键能够终止一个进程。 个人了解进程相关知识之后知道&#xff0c;一个进程被终止只会有有三种情况&#xff1a; 代码运行完毕&#xff0c;结果正确代码运行完毕&#xff0c;结果不正确代码运行异常&#xff…

广东省网络安全竞赛部分web,misc wp

我的队伍只做了5题&#xff0c;还是太菜了&#xff0c;本来不想发的&#xff0c;但是写都写了&#xff0c;还是水一篇博客吧 这里是我们队的wp misc1 给了一个压缩包&#xff0c;解压需要密码&#xff0c;用纯数字密码没跑出来&#xff0c;感觉可能不是要强跑&#xff0c;看…

海外媒体宣发:新加坡.马来西亚如何在海外媒体投放新闻通稿-大舍传媒

导言 随着全球化的进程加速&#xff0c;海外市场对于企业的发展越来越重要。而在海外媒体上宣传企业的新闻通稿&#xff0c;成为了拓展海外市场和提升企业知名度的重要手段之一。本文将介绍大舍传媒对于如何在海外媒体上投放新闻通稿的经验和策略。 准备工作&#xff1a;了解…

Patch-Wise Graph Contrastive Learning for Image Translation

Patch-Wise Graph Contrastive Learning for Image Translation 图像翻译中的逐块图对比学习 Chanyong Jung1, Gihyun Kwon1, Jong Chul Ye1, 2 Chanyong Jung&#xff0c;Gihyun Kwon&#xff0c;Jong Chul Ye 1, 2 Abstract 摘要 Patch-Wise Graph Cont…

CSS实现渐变色

渐变色分为线性渐变和径向渐变。 线性渐变linear-gradient(方向, 颜色1, 颜色2, … ,颜色n)径向渐变radial-gradient(颜色1 覆盖区域大小, 颜色2 覆盖区域大小, … ) 线性渐变的方向可以为&#xff1a; ​ 1、一个方向值时&#xff1a; to bottom 表示从上边到下边渐变 ​ 2、…