【操作系统笔记二】链接阶段ELF文件

news2024/11/25 2:30:50

链接阶段:符号解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

链接阶段主要包含:

  • 符号解析
  • 重定位

一般情况下,每个 C 文件可以看成一个程序模块,比如下边的main.c就是一个程序模块

#include <stdio.h>

extern int shared; 
int sum(int *a, int n); 

int array[2] = {1, 2}; 
static int m = 10;

static int swap(int *a, int *b) 
{
    int tmp = *a;
    *a = *b;
    *b = tmp; 
}

int main() {
    static int b = 4;
    int val = sum(array, 2); 
    swap (&val, &shared);
    printf("%d, %d\n", val, shared); 
    return val;
}

每个程序模块一般会:

  1. 自己定义一些符号,称为符号定义
  2. 引用其他模块的符号,称为符号引用

符号定义:

  • 全局符号:非静态 C 函数和全局变量,比如上面代码中的 arraymain

  • 局部符号static 修饰的 C 函数和全局变量,相当于 Java/C++ 中的 private 比如上面代码中的 mswap

  • 本地符号:函数内部定义的符号
    1)非静态本地变量,比如上面代码中的val
    2)静态本地变量,比如上面代码中的b

  • 外部符号:引用其他模块定义的全局符号,比如上面代码中的 sharedsum以及 printf

说明:除了非 static 局部符号外,其他符号都是在链接器管理范畴。程序在运行时,函数中本地变量,属于函数调用栈管理。每个程序模块,在编译后,都会有一个符号表,用于记录程序模块中的符号。

上面代码中涉及 3 个外部符号:sharedsumprintf,其中 printf 符号是在 printf.o 程序模块中, gcc 工具会帮你自动找到。sharedsum 符号需要在 sum.o 程序模块中找到。这就是符号解析需要做的工作:输入的其他的程序模块中的符号表中,为每个外部符号,寻找确定的符号定义,并且关联它们。

在这里插入图片描述

链接阶段:重定位(Relocation)

在这里插入图片描述
在这里插入图片描述

  • 汇编后的.o文件中每个指令的位置,只是这条指令在文件中的偏移量,不是内存地址,所以此时.o文件还不能直接加载到内存运行。

  • 在链接阶段,合并所有.o文件中的指令,重新定位这些指令的位置,并为每个指令分配唯一内存地址。还需要重新定位外部符号的内存地址。这就是为什么.o文件称为可重定位目标文件!!!

  • 经过链接之后变成可执行目标文件,这时才可以加载到内存运行。

静态链接库

静态链接:将多个可重定位目标文件,合并成一个可执行目标文件。

在这里插入图片描述

静态库很有用,实现了程序代码的复用。但是,因为静态库的链接属于静态链接,会出现浪费内存的现象。比如,程序 A 和程序 B 都静态链接了 libvector.a 静态库,那么当程序 A 和程序 B 同时在运行时,内存中会有两份 libvector.a 的代码指令。

在这里插入图片描述

将静态链接库编译成动态链接库,就可以解决多份.a文件同时使用会重复浪费内存的问题。

ELF - 可重定位目标文件格式

在这里插入图片描述

一个典型的 ELF 目标文件的格式包含:ELF 头若干个节 (section) 以及描述节的头部表 (节头部表),如下图:

在这里插入图片描述

我们边对照着下面的程序,然后边看看以上每个节都存储些啥。

#include <stdio.h>

extern int shared;

int sum(int *a, int n);

int array[2] = {1, 2};
static int m = 10;
int* arr_p = (int*)&array;

int c;

static int swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main()
{
    static int b = 4;
    static int m;
    int val = sum(array, 2);
    swap(&val, &shared);
    printf("%d, %d\n", val, shared);
    return val;
}
  • .text :用于已编译程序的机器代码,通过下面的命令可以看到 .text 中存储的机器代码
objdump -Dj .text main2.o
// 查看二进制存储
objdump -sj .text main2.o
  • .rodata :用于存储只读数据,比如 printf 语句中的格式串等,通过下面的命令可以查看:
objdump -Dj .rodata main2.o
objdump -sj .rodata main2.o
  • .data :存储已初始化的全局和静态 C 变量,比如 arraym 等。非静态的局部 C 变量在运行的时候被保存在栈中,不会出现在 .data 中,
objdump -Dj .data main2.o
objdump -sj .data main2.o
  • .bss :未初始化的全局和静态 C 变量,以及所有被初始化为 0 的全局或静态变量。
objdump -Dj .bss main2.o
objdump -sj .bss main2.o

在上面的程序中,只有静态变量 m.bss 中,未初始化的全局变量 c ,会出现在可执行目标文件中的 .bss 中,在目标文件中,这个 .bss 节不占据实际的空间,它仅仅是一个占位符。

区分初始化和未初始化变量的目的是为了节省空间,未初始化变量不需要占据任何实际的磁盘空间

在运行的时候,给这些变量分配内存,并初始化为 0,所以所有被初始化为 0 的全局或静态变量也放在 .bss 中。

  • .symtab :符号表,存放程序中定义和引用的函数和全局符号的信息
objdump -t main2.o
  • .rel.text :也可以使用 .rela.text 表示,存储 .text 节中需要重定位的条目
objdump -rj .text main2.o

在这里插入图片描述

一般而言,以下两种情况需要重定位:

  1. 引用全局变量 (不管是内部还是外部)
  2. 调用外部函数

可以通过下面的指令来查看哪些指令需要生成重定位条目:

objdump -dx main2.o
  • .rel.data :也可以使用 rela.data 表示,存储 .data 节中需要重定位的条目
objdump -rj .data main2.o

在这里插入图片描述
以上的重定位条目来源于全局变量 arr_p

一般而言,任何已初始化的全局变量,如果它的初始值是一个全局变量地址或者外部定义函数的地址,都需要重定位。

ELF 文件格式详解

对上面的程序编译成可重定位目标文件,我们可以使用 readelf 来查看 ELF 文件的格式

readelf -all main2.o

我们先看看 ELF Headers

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          1232 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         14
  Section header string table index: 11

ELF Headers 是以一个 16 字节的数字开头,然后是生成该文件的字节顺序 (小端序)

还描述了文件的类型,这里是 REL ,表示的是可重定位目标文件,还有可执行目标文件,比如 prog 文件

还描述了机器类型,这里是 x86-64,还有节头部表的的文件偏移 (也就是节头部表在文件中起始位置),还有节头部表的大小和节的数目

接下来看看节头部表 (section header table)

Section Headers:
  [Nr] Name              Type             Address           Offset
		Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000076  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  000003f8
       0000000000000090  0000000000000018   I      12     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000b8
       000000000000001c  0000000000000000  WA       0     0     8
  [ 4] .rela.data        RELA             0000000000000000  00000488
       0000000000000018  0000000000000018   I      12     3     8
  [ 5] .bss              NOBITS           0000000000000000  000000d4
       0000000000000004  0000000000000000  WA       0     0     4
  [ 6] .rodata           PROGBITS         0000000000000000  000000d4
       0000000000000008  0000000000000000   A       0     0     1
  [ 7] .comment          PROGBITS         0000000000000000  000000dc
       000000000000002e  0000000000000001  MS       0     0     1
  [ 8] .note.GNU-stack   PROGBITS         0000000000000000  0000010a
       0000000000000000  0000000000000000           0     0     1
  [ 9] .eh_frame         PROGBITS         0000000000000000  00000110
       0000000000000058  0000000000000000   A       0     0     8
  [10] .rela.eh_frame    RELA             0000000000000000  000004a0
       0000000000000030  0000000000000018   I      12     9     8
  [11] .shstrtab         STRTAB           0000000000000000  00000168
       0000000000000066  0000000000000000           0     0     1
  [12] .symtab           SYMTAB           0000000000000000  000001d0
       00000000000001e0  0000000000000018          13    13     8
  [13] .strtab           STRTAB           0000000000000000  000003b0
       0000000000000043  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

节头部表描述了每一个节所在的位置和大小。包含了下面的 10 个字段:

  • Name 表示节的名称
  • Type 表示节的类型,NULL 表示无效的节,PROGBITS 表示包含了程序代码的节,RELA 表示包含重定位条目的节,STRTAB 表示包含了字符串表的节
  • Address 表示节在内存中的其实地址,因为可重定位目标文件是不可执行的,所以还没有内存地址,可执行文件中会记录每个节的其实内存地址
  • Offset 表示节的在文件中的偏移,也就是节在文件中存储的位置
  • Size 表示节中存储数据的大小
  • EntSize 表示节中每个条目的大小,前提是节中的条目大小是固定长度的,比如 symtab 符号表等
  • Flags 表示节的一些属性,比如 .text 的 flags 为 AX,表示可重定位且可执行
  • Link 表示当前节关联的其他节
  • Info 表示当前节的一些额外信息
  • Align 表示节中存储数据大小对应字节大小对齐,比如 4 字节对齐,8 字节对齐等

接下来看看 .symtab 符号表

Symbol table '.symtab' contains 20 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main2.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     5: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    3 m
     6: 0000000000000000    44 FUNC    LOCAL  DEFAULT    1 swap
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     8: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    5 m.2192
     9: 0000000000000018     4 OBJECT  LOCAL  DEFAULT    3 b.2191
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
    13: 0000000000000000     8 OBJECT  GLOBAL DEFAULT    3 array
    14: 0000000000000010     8 OBJECT  GLOBAL DEFAULT    3 arr_p
    15: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM c
    16: 000000000000002c    74 FUNC    GLOBAL DEFAULT    1 main
    17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND sum
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND shared
    19: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

字段解释:

  • Value 表示符号在对应节中的偏移位置
  • Size 表示符号表示的对象的大小
  • Type 表示符号的类型,NOTYPE 表示没有指定类型,FILE 表示文件类型,SECTION 表示一个节类型,FUNC 表示一个函数,OBJECT 表示的是一个变量
  • Bind 表示 LOCAL 和 GLOBAL
  • Vis
  • Ndx 符号所属的节的 index 值,UND 表示未定义的符号,ABS 表示绝对值,不参与重定位,COM 表示属于 COMMON 节,其他数字表示对应的节的 index
  • Name 表示符号的名字

sections 每节在文件中的具体位置

接下来,我们来看看每一节在可重定位目标文件中的具体位置

在文件的开头是 ELF Header:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          1232 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         14
  Section header string table index: 11

Entry point address 等于 0,表示的就是 ELF Header 位于文件开头,

Size of this header 等于 64 字节,表示的是 ELF Header 的大小是 64 个字节,那么 ELF Header 是从 0 ~ 63

Start of section headers 等于 1232 字节,表示的是 section headers table 也就是节头部表位于文件中第 1232 字节,根据这个,我们可以得到节头部表的内容:

Section Headers:
  [Nr] Name              Type             Address           Offset
		Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000076  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  000003f8
       0000000000000090  0000000000000018   I      12     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000b8
       000000000000001c  0000000000000000  WA       0     0     8
  [ 4] .rela.data        RELA             0000000000000000  00000488
       0000000000000018  0000000000000018   I      12     3     8
  [ 5] .bss              NOBITS           0000000000000000  000000d4
       0000000000000004  0000000000000000  WA       0     0     4
  [ 6] .rodata           PROGBITS         0000000000000000  000000d4
       0000000000000008  0000000000000000   A       0     0     1
  [ 7] .comment          PROGBITS         0000000000000000  000000dc
       000000000000002e  0000000000000001  MS       0     0     1
  [ 8] .note.GNU-stack   PROGBITS         0000000000000000  0000010a
       0000000000000000  0000000000000000           0     0     1
  [ 9] .eh_frame         PROGBITS         0000000000000000  00000110
       0000000000000058  0000000000000000   A       0     0     8
  [10] .rela.eh_frame    RELA             0000000000000000  000004a0
       0000000000000030  0000000000000018   I      12     9     8
  [11] .shstrtab         STRTAB           0000000000000000  00000168
       0000000000000066  0000000000000000           0     0     1
  [12] .symtab           SYMTAB           0000000000000000  000001d0
       00000000000001e0  0000000000000018          13    13     8
  [13] .strtab           STRTAB           0000000000000000  000003b0
       0000000000000043  0000000000000000           0     0     1

从上面可以看到 .text 节的 Offset 等于 0x00000040,也就说 .text 是从第 64 个字节开始,也就是位于 ELF Header 后面,.text 节的 Size 等于 0x0000000000000076,也就是 118 字节

.text 是从 64 ~ 181 字节段,对于 .text 需要 4 字节对齐,也就是 .text 实际大小为 120,那么 .text 就是从 64 ~ 183 字节段

接下来是 .data 节,起始位置为 184 字节,即 0xb8, .data 的大小是 0x1c

接下来是 .bss 和 .rodata 节,它们俩的起始位置都是:0xb8 + 0x1c = 0xd4,但是 .bss 是不占据磁盘空间的,只是作为标记而已

所以 0xd4 开始存储的实际上是 .rodata 节,大小为 0x8,接下来是 0xd4 + 0d8 = 0xdc,存储的是 .comment

按照上面的逻辑,可以将每一个部分定位在文件的具体位置上:

在这里插入图片描述
以上,我们可以通过偏移定位到每一个节的位置

在节的内部,也可以通过偏移定位节内的数据,比如符号表中:

Symbol table '.symtab' contains 20 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main2.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     5: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    3 m
     6: 0000000000000000    44 FUNC    LOCAL  DEFAULT    1 swap
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     8: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    5 m.2192
     9: 0000000000000018     4 OBJECT  LOCAL  DEFAULT    3 b.2191
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
    13: 0000000000000000     8 OBJECT  GLOBAL DEFAULT    3 array
    14: 0000000000000010     8 OBJECT  GLOBAL DEFAULT    3 arr_p
    15: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM c
    16: 000000000000002c    74 FUNC    GLOBAL DEFAULT    1 main
    17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND sum
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND shared
    19: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

符号 b 的值位于第 3 节,也就是 .data 节,它在 .data 节的 0x18 这个位置上,也就是第 24 个字节,值为 0x04000000,注意二进制是小端序存储的,所以 0x04000000 实际上是等于 0x00000004

objdump -sj .data main2.o

总结

接下来,我们来总结下 ELF 中所有的表,如下图:
在这里插入图片描述

  • 通过 ELF Header 中的信息,可以知道 ELF Header 的大小,以及节索引表的位置
  • 根据节索引表,就可以找到所有的节
  • 根据符号表,我们就可以知道所有的符号在每一个节中的偏移位置
  • 符号表用于链接阶段中符号解析
  • 链接阶段中的重定位需要用到 .rela.text 和 rela.data 等表

ELF - 可执行目标文件格式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ELF - 可执行目标文件也是由ELF头节头部表以及若干个节组成。

  • .text.rodata.data.bss这 些节已经被重定位到它们最终的运行时内存地址

  • init 节定义了一个小函数,叫做_init,程序的初始化代码会调用它

  • 因为文件是完全链接的(已被重定位),所以它不再需要 .rela 节了

为了使得 ELF 可执行目标文件更加容易加载到内存,根据指令和数据的特点,将可执行文件连续的节映射到连续的内存段中。

段头部表维护了这层映射关系:

在这里插入图片描述

总结

在这里插入图片描述
在这里插入图片描述

ELF 可执行目标文件格式跟 ELF 可重定位目标文件格式其实是差不多类似的,只不过多了一个 段头部表,另外每个部分的偏移地址被映射成了真实的内存地址。

也就是说,可重定位目标文件(.o文件)中每条指令和数据只有在文件中的相对偏移位置,到了链接阶段,才会真正的为每条指令和数据分配内存地址,因此才能加载到内存中执行,所以此时叫可执行目标文件。

一个程序的内存布局

一个程序需要的内存:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

【C语言】指针的进阶(四)—— 企业笔试题解析

笔试题1&#xff1a; int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】在x86环境下运行 【解析】 &a是取出整个数组的地址&#xff0c;&a就表示整个数组&#xff0c;因此…

oracle11g升级12c

目录 背景&#xff1a;oracle11.2.0.4升级至12.2.0.1版本 当前数据库版本 1&#xff09;上传12g安装包&#xff0c;解压开始安装 2&#xff09;不勾选 3&#xff09;升级数据库 4&#xff09;下一步 5&#xff09;修改路径11.2.0换成12.2.0 6&#xff09;下一步 7&…

shardingjdbc分库分表数据均衡性讨论

问题引入 最近一个业务系统中&#xff0c;因为数据量很大&#xff0c;经过技术选型&#xff0c;综合权衡选择了sharding-Jdbc&#xff0c;本文主要讨论的是分库分表的表达式 我们有一个批次总表A&#xff0c;还有一个明细表B&#xff0c;我们需要对明细表B进行水平拆分&#…

论文总结《A Closer Look at Few-shot Classification Again》

原文链接 A Closer Look at Few-shot Classification Again 摘要 这篇文章主要探讨了在少样本图像分类问题中&#xff0c;training algorithm 和 adaptation algorithm的相关性问题。给出了training algorithm和adaptation algorithm是完全不想关的&#xff0c;这意味着我们…

瑞芯微RK3568:Debian系统如何安装Docker

本文基于HD-RK3568-IOT评估板演示Debian系统安装Docker&#xff0c;该方法适用于RK356X全系产品。 HD-RK3568-IOT评估板基于HD-RK3568-CORE 工业级核心板设计&#xff08;双网口、双CAN、5路串口&#xff09;&#xff0c;接口丰富&#xff0c;适用于工业现场应用需求&#xff…

怒刷LeetCode的第13天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;滑动窗口 方法二&#xff1a;哈希表和双指针 方法三&#xff1a;动态规划 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;深度优先搜索&#xff08;DFS&#xff09; 方法二&#xff1a;树结构 …

rv1126-rv1109-RkLunch.sh

RkLunch.sh是个脚本,来自哪里 书接上回:rv1126-rv1109-瑞芯微的 IPC 程序_旋风旋风的博客-CSDN博客 修改之后 屏蔽了两行之后,就没有开机自启rklunch了: # [ -f /oem/RkLunch.sh ] && source /oem/RkLunch.sh 就是运行source /oem/RkLunch.sh 这里就跑了RkL…

基于微信小程序的驾校报名系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信小程序端的主要功能有&#xff1a;驾校教练的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码…

动手学深度学习(pytorch版)第二章-2.2数据预处理Note-pandas

1. 创建 import osos.makedirs(os.path.join(.., data), exist_okTrue) data_file os.path.join(.., data, house_tiny.csv) with open(data_file, w) as f:f.write(NumRooms,Alley,Price\n) # 列名f.write(NA,Pave,127500\n) # 每行表示一个数据样本f.write(2,NA,106000\…

平板用的触控笔什么牌子好?性价比高的触控笔推荐

随着平板电脑的普及&#xff0c;越来越多用户为了方便都选择了电容笔&#xff0c;电容笔已经完全代替了我们的手指&#xff0c;并且使我们的书写速度得到了极大的提升。然而&#xff0c;因为其的独特的重力压感功能与芯片技术&#xff0c;导致了原装笔的售价一直居高不下&#…

深入探析NCV7356D1R2G 单线CAN收发器各项参数

NCV7356D1R2G深力科是一款用于单线数据链路的物理层器件&#xff0c;能够使用多种具碰撞分解的载波感测多重存取 (CSMA/CR) 协议运行&#xff0c;如博世控制器区域网络 (CAN) 2.0 版。此串行数据链路网络适用于不需要高速数据的应用&#xff0c;低速数据可在物理介质部件和微处…

用selenium和xpath定位元素并获取属性值以及str字符型转json型

页面html如图所示&#xff1a; 要使用xpath定位这个div元素&#xff0c;并且获取其属性data-config的内容值。 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Optionshost127.0.0.1 port10808 …

线程安全,与多线程的应用

一、线程安全 1.什么是线程安全 2.用程序模拟线程安全问题 public class Account {private double money;//余额private String cardId;//卡号public Account() {}public Account(double money, String cardId) {this.money money;this.cardId cardId;}public void drawMone…

LeetCode 1993. 树上的操作:大模拟

【LetMeFly】1993.树上的操作&#xff1a;大模拟 力扣题目链接&#xff1a;https://leetcode.cn/problems/operations-on-tree/ 给你一棵 n 个节点的树&#xff0c;编号从 0 到 n - 1 &#xff0c;以父节点数组 parent 的形式给出&#xff0c;其中 parent[i] 是第 i 个节点的…

基于微信小程序的健康评估系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信端的主要功能有&#xff1a;医生微信端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码获取…

iOS17正式版BUG汇总:无法正常拨打电话、小组件不可用、无线充电不可用等问题

今天凌晨 iOS 17 正式版发布&#xff0c;相信不少尝鲜派已经更新体验了iOS17的新功能了&#xff0c;但还有很多用户选择观望看是否要升级&#xff0c;小编汇总了目前已更新的用户反馈的已知BUG&#xff0c;供大家查看是否要更新iOS17正式版&#xff01; 目前已知BUG&#xff1…

【LeetCode75】第六十二题 多米诺和托米诺平铺

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我一个数字n&#xff0c;表示我们有2*n大小的地板需要铺。 我们拥有两种瓷砖&#xff0c;一种的长度为2的多米诺&#xff0c;另一…

Jetpack Compose干货,如何让Compose Dialog从屏幕任意方向进入

一、前言 来个效果图&#xff0c;基于Compose Dialog&#xff0c;最终要实现的库能力如下&#xff1a; 这里使用的是这个包下面的&#xff1a; androidx.compose.ui.window.Dialog androidx.compose.material3.AlertDialog它内部调用的也是androidx.compose.ui.window.Dialog …

Centos7 安装部署 Kubernetes(k8s) 高可用集群

1&#xff1a;基础环境准备 宿主机系统集群角色服务器IP主机名称容器centos7.6master192.168.2.150ks-m1dockercentos7.6master192.168.2.151ks-n1dockercentos7.6master192.168.2.152ks-n2docker 1.1 服务器初始化及网络配置 VMware安装Centos7并初始化网络使外部可以访问*…

No2.详解【2023年全国大学生数学建模竞赛】C题——蔬菜类商品的自动定价与补货决策(代码 + 详细输出 + 数据集代码 下载)

只有不回避痛苦和迷茫的人,才有资格去谈乐观和坚定。命运不会厚待谁,悲喜也不会单为你准备。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能…