资讯详情

哈工大2021~2022春季学期《计算机系统》大作业

题 目 程序人生-Hello’s P2P

专 业 计算机类

学 号 120L021025

班 级 2003001

学 生

指 导 教 师 史先俊

本文结合本学期对计算机系统和知识的深入理解hello.c例如,该程序分析了从预处理、编译、汇编、链接到生成可执行文件的生命过程,以及程序执行过程中的过程管理、存储空间管理和I/O通过硬件与软件的相互交织、共同合作,探讨系统如何实现运行程序的最终目标,学以致用,整合本学期所学知识,真正做到对计算机系统有深入了解。

计算机系统;程序;生命过程;整合

1.1 Hello简介... - 4 -

1.2 环境与工具... - 4 -

1.3 中间结果... - 5 -

1.4 本章小结... - 5 -

2.1 预处理的概念和作用... - 7 -

2.2在Ubuntu下一个预处理命令... - 7 -

2.3 Hello预处理结果分析... - 7 -

2.4 本章小结...................................................................................................... - 8 -

3.1 编译的概念与作用......................................................................................... - 9 -

3.2 在Ubuntu下编译的命令............................................................................. - 9 -

3.3 Hello的编译结果解析................................................................................ - 10 -

3.4 本章小结....................................................................................................... - 13 -

4.1 汇编的概念与作用....................................................................................... - 14-

4.2 在Ubuntu下汇编的命令........................................................................... - 14 -

4.3 可重定位目标elf格式............................................................................... - 14 -

4.4 Hello.o的结果解析.................................................................................... - 18 -

4.5 本章小结....................................................................................................... - 19 -

5.1 链接的概念与作用....................................................................................... - 20 -

5.2 在Ubuntu下链接的命令........................................................................... - 20 -

5.3 可执行目标文件hello的格式.................................................................. - 20 -

5.4 hello的虚拟地址空间................................................................................ - 26 -

5.5 链接的重定位过程分析............................................................................... - 26 -

5.6 hello的执行流程........................................................................................ - 27 -

5.7 Hello的动态链接分析................................................................................ - 28 -

5.8 本章小结....................................................................................................... - 29 -

6.1 进程的概念与作用....................................................................................... -30 -

6.2 简述壳Shell-bash的作用与处理流程..................................................... - 30 -

6.3 Hello的fork进程创建过程..................................................................... - 30 -

6.4 Hello的execve过程................................................................................. - 31 -

6.5 Hello的进程执行........................................................................................ - 31 -

6.6 hello的异常与信号处理............................................................................ - 32 -

6.7本章小结....................................................................................................... - 32 -

7.1 hello的存储器地址空间............................................................................ - 33 -

7.2 Intel逻辑地址到线性地址的变换-段式管理............................................ - 33 -

7.3 Hello的线性地址到物理地址的变换-页式管理...................................... - 34 -

7.4 TLB与四级页表支持下的VA到PA的变换............................................. - 34 -

7.5 三级Cache支持下的物理内存访问.......................................................... - 35 -

7.6 hello进程fork时的内存映射................................................................... - 35-

7.7 hello进程execve时的内存映射............................................................... -36 -

7.8 缺页故障与缺页中断处理........................................................................... - 36 -

7.9动态存储分配管理....................................................................................... - 36 -

7.10本章小结..................................................................................................... - 37 -

8.1 Linux的IO设备管理方法.......................................................................... - 38 -

8.2 简述Unix IO接口及其函数....................................................................... - 38 -

8.3 printf的实现分析........................................................................................ - 39 -

8.4 getchar的实现分析.................................................................................... - 40 -

8.5本章小结....................................................................................................... - 40 -

第1章 概述

1.1 Hello简介

1.1.1 P2P,即Program to Process,是指程序从编辑器下的高级语言程序(如hello.c)生成可执行程序(如hello)、执行可执行程序产生进程的过程。P2P的具体过程如下:cpp预处理器将hello.c文件中的#开头的命令进行预处理,插入对应的系统头文件中的内容,得到hello.i文件;然后编译器ccl将其翻译为汇编语言文件hello.,此后汇编器as负责将汇编语言翻译为机器指令代码,得到hello.o文件;最后,在链接阶段,链接器ld将hello.o和其它用到的预编译好的目标文件合并到一起并且完成引用的重定位工作,就得到了可执行文件hello。在输入命令执行改文件时,shell程序会调用fork函数创建子进程,并且在子进程中加载该程序,由此,hello.c就由高级语言程序变成了一个进程。

1.1.2 020,即“From 0 to 0”由子进程开始执行(此前内存无hello相关内容)到子进程结束被回收(内存中仍无hello相关内容)的过程。020的具体过程如下:在子进程调用execve函数加载hello执行后,操作系统OS为其映射虚拟内存至物理内存,CPU也为运行此进程分配时间,通过Cache,TLB等机制加速访问时间;程序结束后,shell父进程通过相应的信号机制得到子进程结束的消息并且回收进程,同时内核将控制权转移回shell,子进程的相关数据被清除。

1.2 环境与工具

1.2.1硬件环境:

主机:X64 CPU(i7-10750H);2.6 GHz;2×8G RAM;1T SSD;

虚拟机:X64 CPU(i7-10750H);2.6 GHz;4.0G RAM;20G SSD。

1.2.2软件环境:

Windows:Windows 10家庭版;

Linux:VMware Workstation 15 Pro;Ubuntu 64位-20.04。

1.2.3调试工具:

Windows下:Visual Studio 2022,Dev-C++;CodeBlocks 20.03;

Linux下:CodeBlocks 20.03;gcc9.3.0/Linux。

1.3 中间结果

所有中间产物的名称与功能见下表:

文件名

功能

hello.c

源程序

hello.i

预处理后得到的文本文件

hello.s

编译后得到的汇编语言文件

hello.o

汇编后得到的可重定位目标文件

hello

链接后得到的可执行目标文件

hello.elf

用readelf读取hello.o得到的ELF格式信息

hello_elf.txt

用readelf读取hello得到的ELF格式信息

hello.asm

反汇编hello.o得到的反汇编文件

hello2.asm

反汇编hello可执行文件得到的反汇编文件

1.4 本章小结

       本章简要介绍了程序的P2P和020过程,即高级语言程序经预处理、编译、汇编、链接生成可执行程序,从执行程序生成子进程至回收子进程的过程;介绍了为编写论文使用的计算机的软硬件环境和调试工具;展示了为编写本论文生成的中间结果文件。

第2章 预处理

2.1 预处理的概念与作用

       2.1.1预处理的概念:预处理是指在编译之前进行的处理,由预处理器(cpp)根据以字符#开头的预处理命令,修改原始的C程序,读取系统头文件的内容,并把它们直接插入程序文本中,得到.i扩展名文件。C语言的预处理主要包括宏定义、文件包含、条件编译三个方面的内容。

       2.1.2预处理的作用:预处理通过调用系统头文件,用实际内容替换用#define定义的字符串,简化了程序,使编译器编译程序时更加方便。

2.2在Ubuntu下预处理的命令

预处理命令为

图2.1 Ubuntu下的预处理命令

2.3 Hello的预处理结果解析

       hello.c经预处理得到hello.i文件,hello.i共3060行,大小为64.7KB。

       源文件最开始的stdio.h、stdlib.h、unistd.h三个头文件被系统头文件的实际展开内容替换,展开的具体流程如下(以stdio.h为例):cpp先删除指令#include <stdio.h>,并到Ubuntu系统的默认的环境变量中寻找 stdio.h,最终打开路径/usr/include/stdio.h下的stdio.h文件。若stdio.h文件中使用了#define语句,则按照上述流程继续递归地展开,直到所有#define语句都被解释替换掉为止。除此之外,cpp还会进行删除程序中的注释和多余的空白字符等操作,并对一些值进行替换。

此后是所调用函数的声明,文件的最后是hello.c中的main()函数,它位于第3047到第3060行。

图2.2 hello.i的解析

图2.3 头文件的展开内容

图2.4 所调用函数的声明

图 2.5 hello.i中的main函数

2.4 本章小结

       本章介绍了预处理的概念、过程、作用、实现指令,并对预处理操作的结果进行了解析,展示了程序生命周期中的第一步。

第3章 编译

3.1 编译的概念与作用

3.1.1 编译的概念

      编译是指利用编译程序从经预处理的源语言编写的源程序产生汇编指令的过程。编译过程中,C编译器ccl通过词法分析、语法分析、语义检查、中间代码生成、代码优化和目标代码生成,将合法指令翻译成等价汇编代码,并就语法错误给出错误分析。通过编译,编译器将文本文件 hello.i 翻译成汇编语言文件 hello.s,在hello.s中,以文本的形式描述了一条条低级机器语言指令。

3.1.2 编译的作用

      编译将预处理后的文本文件转化为汇编语言文件,为把高级语言变成计算机可以识别的二进制语言的过程做准备。

3.2 在Ubuntu下编译的命令

Ubuntu下编译命令为。(或/usr/lib/gcc/x86_64-linux-gnu/9/cc1-m64 -Og -no-pie -fno-PIC hello.i -o hello.s)

3.3 Hello的编译结果解析

经编译生成的hello.s共78行,大小1.3KB。

      立即数1,4等直接转化为$1、$4,并直接使用,如第25行cmpl  $4, %edi。

      int i 作为局部变量被存储在寄存器或者栈空间中。i作为函数内部的局部变量,并不占用文件实际节的空间,只在运行时存在于栈中。对于i的操作就是直接对寄存器或栈进行操作。

int argc是main函数的第一个参数,64位编译下,argc由寄存器%rdi传入,进而保存在堆栈中。

      字符串"用法: Hello 学号 姓名 秒数!\n"和"Hello %s %s\n"均被储存在.text数据段中,其中汉字采用UTF-8编码,每个汉字占3个字节。

      

              char *argv[]是main函数的第二个参数。在hello.s中,其首地址保存在栈中。访问时,通过寄存器%rbx寻址的方式访问。图中%rsi储存argv[1],%rdi储存argv[2]。

       对局部变量的赋值使用movx指令完成。如将局部循环变量i的值保存在寄存器%ebp中。

(不)等于判断及其跳转使用cmpx和jne指令实现。如if(argc!=4),若判断结果不相等,则CS:IP跳转到jne后地址的指令,若相等则继续顺序执行指令。

       小于(或大于)判断使用cmpx及jle(jge)指令实现。编译器会将i<8转换为i<=7来判断。同理for(i=0;i<8;i++),若i<8的判断结果为真,则跳转到L3继续执行循环体,若i<8不再成立,则继续顺序执行指令脱离循环。

       通过将首地址保存在栈中,并使用寄存器%rbx通过相对寻址的方式访问。

i++使用addq指令实现

还有一些算数操作,如修改访问数组使用的寄存器等操作,同样通过addq和subq等指令实现。

(1)调用函数采用callq指令实现,执行这条指令后程序计数器CS:IP会被设置为callq后的地址。

(2)函数参数传递采用movx指令通过寄存器实现。前6个参数传入的寄存器分别为%rdi、%rsi、%rdx、%rcx、%r8、%r9,第7个及以后的参数通过堆栈传递。以main函数为例,argc作为第一个参数,使用%edi传入,argv[]作为第二个参数,使用%rsi传入。

(3)在函数执行开始时,可能需要为其局部变量分配空间,在返回前,又必须释放这些空间。

3.4 本章小结

本章介绍了编译的概念与作用,编译是将文本文件翻译成汇编语言文件,为后续将其转化为二进制机器指令文件做准备的过程。同时,本章以hello.s文件为例,介绍了编译器如何处理各个数据类型以及各类操作,验证了大部分数据、操作在汇编代码中的实现。

第4章 汇编

4.1 汇编的概念与作用

汇编是指汇编器(assembler)将以.s结尾的汇编程序翻译成机器语言指令,并把这些指令打包成可重定位目标程序格式,最终结果保存在.o 目标文件中的过程。

将汇编语言翻译为机器指令,并将这些指令以可重定位目标程序格式保存在.o文件中。为后续的链接和执行过程奠基。

4.2 在Ubuntu下汇编的命令

Ubuntu下的汇编命令为

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

生成ELF文件的指令为

生成的ELF文件包括ELF头、节头、程序头(本文件无)、重定位节等内容。具体如下:

(1)ELF头:以 16字节的二进制序列 Magic 开始,其描述了生成该文件的系统的字的大小和字节顺序。此后是帮助链接器语法分析和解释目标文件的信息,其中包括 ELF 头大小、目标文件类型、机器类型、节头部表的文件偏移、节头部表中条目的大小和数量等以及类别、数据、ABI版本、类型等相关信息。

(2)节头

       包含了文件中出现的各个节的详细信息,包括节的标号、类型、大小和位置(地址与偏移量)等信息。

(3)重定位节

.rela.text:是一个.text 节中位置的列表,包含.text 节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。

.rela.text 中的8 条重定位信息分别是对.L0(第一个 printf 中的字符串)、puts 函数、exit 函数、.L1(第二个 printf 中的字符串)、printf 函数、sleepsecs、sleep 函数、getchar 函数进行重定位声明。

             .rela.eh_frame同理。

偏移量

代表需要进行重定向的代码在.text或.data节中的偏移位置

信息

包括symbol和type两部分,其中symbol占前半部分,type占后半部分,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型

类型

重定位到的目标的类型

符号名称+加数

计算重定位位置的辅助信息

(4)符号表(Symbol table)

     

符号表中保存着定位、重定位程序中符号定义和引用的信息,所有重定位需要引用的符号都在其中声明。

4.4 Hello.o的结果解析

生成hello.o的反汇编文件的指令为

机器语言由二进制指令构成,其与汇编指令基本上一一对应。

对比两版本对hello.c源程序的转换,可以发现,反汇编文件的篇幅明显短于汇编文件,但对于具体的语句类型,汇编文件与反汇编文件几乎一致,不同点具体如下:

(1)格式上,hello.i前没有一串二进制数,即相应的机器码,而反汇编代码前面有与之对应的机器码。

(2)数据内容上,立即数在hello.i这一汇编语言文本文件中为十进制,而在反汇编代码中为十六进制。

(3)跳转方式不同。在汇编代码中,代码直接声明具体的段存储位置,通过助记符(如.L0、.L1等)存储在.rodata段中;而反汇编代码是依据地址跳转的。

(4)重定位条目不同。汇编代码仍然采用直接声明的方式,即通过助记符;而反汇编代码采用重定向的方式进行跳转,机器代码在此处留下一些地址以供链接时重定向。

4.5 本章小结

       本章介绍了汇编的概念与作用,在Ubuntu下通过实际操作将hello.s文件翻译为hello.o文件,并生成hello.o的ELF格式文件hello.elf,研究了ELF格式文件的具体结构。通过解析hello.o文件并比较hello.o的反汇编文件hello.asm与hello.s有何区别,了解了汇编语言与机器语言的异同之处。

第5章 链接

5.1 链接的概念与作用

  链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。链接执行符号解析、重定位过程。

  把可重定位目标文件和命令行参数作为输入,产生一个完全链接的,可以加载运行的可执行目标文件。使得分离编译成为可能。

5.2 在Ubuntu下链接的命令

在Ubuntu下链接的命令为

5.3 可执行目标文件hello的格式

       用readelf查看hello的ELF格式的命令为

(1)ELF头:ELF头以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。

(2)节头:节头描述了26个节的相关信息,其中一些段的作用如下:

interp段:动态链接器在操作系统中的位置不是由系统配置决定,也不是由环境参数指定,而是由ELF文件中的 .interp段指定。该段里保存的是一个字符串,这个字符串就是可执行文件所需要的动态链接器的位置,常位于/lib/ld-linux.so.2。(通常是软链接)

dynamic段:该段中保存了动态链接器所需要的基本信息,是一个结构数组,可以看做动态链接下 ELF文件的“文件头”。存储了动态链接会用到的各个表的位置等信息。

dynsym段:该段与 “.symtab”段类似,但只保存了与动态链接相关的符号,很多时候,ELF文件同时拥有 .symtab 与 .synsym段,其中 .symtab 将包含 .synsym 中的符号。该符号表中记录了动态链接符号在动态符号字符串表中的偏移,与.symtab中记录对应。

dynstr段:该段是 .dynsym 段的辅助段,.dynstr 与 .dynsym 的关系,类比与 .symtab 与 .strtab的关系 hash段:在动态链接下,需要在程序运行时查找符号,为了加快符号查找过程,增加了辅助的符号哈希表,功能与 .dynstr 类似

rel.dyn段:对数据引用的修正,其所修正的位置位于 “.got”以及数据段(类似重定位段 “rel.data”)

rel.plt段:对函数引用的修正,其所修正的位置位于 “.got.plt”。

(3)程序头:如图

(4)Section to Segment mapping:如图。

(5)Dynamic section:如图。

(6)重定位节:.rela.dyn和.rela.plt,如图。

(7)符号表:.dynsym和.symtab等,存放程序中定义和引用的函数和全局变量的信息。hello程序的符号表包含编号Num、Value、Size、Type、Bind、Vis、Ndx、Name等字段。

(8)版本信息:如图。

5.4 hello的虚拟地址空间  

      edb的分析结果表明,虚拟地址空间从0x401000开始。

如.interp段地址从0x4002e0开始,偏移量为0x2e0,大小为0x1c,对齐要求为1。

5.5 链接的重定位过程分析

使用objdump查看hello汇编语言的指令为

      hello与hello.o的区别如下:

(1)hello.o反汇编代码虚拟地址从0开始;而hello反汇编代码从0x400000开始。

(2)hello.o反汇编代码的第一个段是.text段,然后为main函数;而由于链接过程中重定位而添加进来了各种函数、数据,在hello反汇编的结果中开始的函数和调用的函数填充在main函数之前。

(3)call函数引用全局变量和跳转模块值时地址也有所变化。可执行文件跳转和应用就是相对或绝对的虚拟内存地址;而hello.o反汇编的跳转的就是只要hello数据时对应的位置。

5.6 hello的执行流程

       程序名称和地址见表。

      

程序名称

程序地址

ld-2.27.so!_dl_start

0x7fce8cc38ea0

ld-2.27.so!_dl_init

0x7fce8cc47630

hello!_start

0x400500

libc-2.27.so!__libc_start_main

0x7fce8c867ab0

-libc-2.27.so!__cxa_atexit

0x7fce8c889430

-libc-2.27.so!__libc_csu_init

0x4005c0

hello!_init

0x400488

libc-2.27.so!_setjmp

0x7fce8c884c10

-libc-2.27.so!_sigsetjmp

0x7fce8c884b70

--libc-2.27.so!__sigjmp_save

0x7fce8c884bd0

hello!main

0x400532

hello!puts@plt

0x4004b0

hello!exit@plt

0x4004e0

*hello!printf@plt

--

*hello!sleep@plt

--

*hello!getchar@plt

--

ld-2.27.so!_dl_runtime_resolve_xsave

0x7fce8cc4e680

-ld-2.27.so!_dl_fixup

0x7fce8cc46df0

--ld-2.27.so!_dl_lookup_symbol_x

0x7fce8cc420b0

libc-2.27.so!exit

0x7fce8c889128

5.7 Hello的动态链接分析

动态链接项目中,查看dl_init前后项目变化。对于动态共享链接库中PIC函数,编译器加重定位记录,等待动态链接器处理,为避免运行时修改调用模块的代码段,链接器采用延迟绑定的策略,将过程地址的绑定推迟到第一次调用该过程。动态链接器使用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。

在dl_init调用之前,对于每一条PIC函数调用,调用的目标地址都实际指向PLT中的代码逻辑,初始时每个GOT条目都指向对应的PLT条目的第二条指令。

在dl_init调用之后,0x6008c0和0x6008c0处的两个8字节的数据分别发生改变。

和PLT联合使用时,GOT[0]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。其中GOT[1]指向重定位表(依次为.plt节需要重定位的函数的运行时地址)用来确定调用的函数地址, GOT[2]是动态链接器ld-linux.so模块中的入口点。

在之后的函数调用时,首先跳转到PLT执行.plt中逻辑,第一次访问时,GOT地址为下一条指令,将函数序号压栈,然后跳转到PLT[0],在PLT[0]中将重定位表地址压栈,然后访问动态链接器,在动态链接器中使用函数序号和重定位表确定函数运行时地址,重写GOT,再将控制传递给目标函数。之后如果对同样函数调用,第一次访问跳转直接跳转到目标函数。

5.8 本章小结

本章介绍了链接的概念、作用。使用Ubuntu下的链接指令将hello.o转换为可了执行目标文件,并分析了hello的反汇编结果与hello.o的异同,其中用到了rodata中的重定位条目,并分析了程序如何实现的动态库链接。

第6章 hello进程管理

6.1 进程的概念与作用

      

  进程是一个执行中程序的实例,系统的每个程序都运行在某个进程的上下文。上下文是由程序正确运行所需的状态组成的,这个状态包括存放在内存里的程序的代码和数据,它的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。

  进程为我们塑造一种假象,我们的程序好像是当前唯一运行的程序,我们的程序好像独占CPU和内存,我们程序的代码和数据好像是系统内存中唯一的内容。

6.2 简述壳Shell-bash的作用与处理流程

在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。同时它又是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。

  Bash是一个命令处理器,通常运行于文本窗口中,并能执行用户直接输入的命令。Bash还能从文件中读取命令,这样的文件称为脚本。和其他Unix shell 一样,它支持文件名替换(通配符匹配)、管道、here文档、命令替换、变量,以及条件判断和循环遍历的结构控制语句。包括关键字、语法在内的基本特性全部是从sh借鉴过来的。其他特性,例如历史命令,是从csh和ksh借鉴而来。总的来说,Bash虽然是一个满足POSIX规范的shell,但有很多扩展。

  shell-bash的处理流程如下:

  (1)用户输入命令;

  (2)shell对用户输入命令进行解析,判断是否为内置命令;

  (3)若为内置命令,调用内置命令处理函数,否则调用execve函数创建一个子进程进行运行。

  (4)判断是否为前台运行程序,如果是,则调用等待函数等待前台作业结束;否则将程序转入后台,直接开始下一次用户输入命令。

    (5)shell应该接受键盘输入信号,并对这些信号进行相应处理。

6.3 Hello的fork进程创建过程

当在shell上输入./hello时,由于这个不是一个内置的shell命令,所以shell会认为hello是一个可执行目标文件,通过调用某个驻留在存储器中被称为加载器的操作系统代码来运行它。

  当shell运行一个程序时,父进程通过fork函数生成这个程序的进程。新创建的子进程几乎但不完全与父进程相同,包括代码、数据段、堆、共享库以及用户栈。父进程和新创建的子进程之间最大的区别在于他们有不同的PID。

6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行一个新程序。

  execve函数加载并运行可执行文件filename(hello),且带参数列表argv和环境变量envp。只有当出现错误时,例如找不到filename,execve才会返回到调用程序。

  当加载器运行时,它创建一个类似与图 6-2 的内存映像。在程序头部表的引导下,加载器将可执行文件的片复制到代码段和数据段,接下来,加载器跳转到程序的入口,_start函数的地址,这个函数是在系统目标文件ctrl.o中定义的,对所有的c程序都一样。_start函数调用系统启动函数,_libc_start_main,该函数定义在libc.so里,初始化环境,调用用户层的main函数,处理main函数返回值,并且在需要的时候返回给内核。

6.5 H

标签: 各式连接器fce17

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台