当前位置: 首页 > news >正文

Linux库制作与原理:【静态库】【动态库】【目标文件】【ELF文件】【ELF从形成到假造轮廓】【理解链接和加载】

目录

一.什么是库

二.静态库

2.1创建静态库

 我们在之前的路径下新建lib使用我们自己的库

2.2 使用makefile生成静态库

 三.动态库

3.1动态库生成

3.2动态库使用

3.3库运行搜索路径

四.目标文件

五.ELF文件

六.ELF从形成到加载轮廓

6.1ELF形成可执行

6.2 ELF可执行文件加载

七.理解链接和加载

7.1静态链接

 7.2ELF加载与进程地址空间

7.2.1虚拟地址/逻辑地址

 7.2.2重新理解进程虚拟地址空间

​编辑 7.3动态链接与动态库加载

7.3.1进程如何看到动态库

7.3.2进程间共享库 

 7.3.3动态链接

7.3.3.1概要

7.3.3.2我们的可执行程序被编译器动了手脚

 7.3.3.3动态库中的相对地址

7.3.3.4我们的程序,怎么和库具体映射起来的

7.3.3.5我们的程序,怎么进行库函数调用

7.3.3.6全局偏移量表GOT(global  offset  table) 

7.3.3.7库间依赖 

7.3.4总结


一.什么是库

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:

• 静态库 .a[Linux]、.lib[windows]

• 动态库 .so[Linux]、.dll[windows]

预备代码,第三节: 

简单封装的libc库

二.静态库

• 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再 需要静态库。

• 一个可执行程序可能用到许多的库,这些库运行有的是静态库,有的是动态库,而我们的编译默 认为动态链接库,只有在该库下找不到动态.so的时候才会采用同名静态库。我们也可以使用 gcc  的 -static 强转设置链接静态库。

2.1创建静态库

我创建一个目录里面有文件,其中.h里面是声明,.c是方法是实现,usercode.c是我们调用的方法。

我们知道,多文件执行形成.o文件,进行链接后才能使用: 

我们不想让别人看到我自己函数的实现,就可以用这种方式发给被人,让别人去链接。

命令:ar

 把当前目录的.o文件全部放在libmyc.a文件里。这样就算是打包好了

新开一个目录 ,把我们上面打包好的库和.h文件拿过来

不需要解包,我们在进行链接时需要指明在哪里找库,因为我们的是第三方库,OS只会在系统找,这里我们就需要指明在哪个库,-L .在当前路径,叫做myc的库

 我们在之前的路径下新建lib使用我们自己的库

把.h放在lib里的include下,把.o(应该是libmyc.a,写错了)放在lib里的mylib下:

之后放在一个压缩包里 

 

在其他地方就可以直接下载下来:

解压:

解压完毕就是需要形成.o文件,这里需要使用-I选项来表明头文件在哪里: 

注意把lib/mylib里面的.o应该是libmyc.a文件,上面弄错了。 

下面就是执行的过程: 

• -L: 指定库路径

• -I: 指定头文件搜索路径

• -l: 指定库名 

• 测试目标文件生成后,静态库删掉,程序照样可以运行 

• 关于 -static 选项,稍后介绍

• 库文件名称和引入库的名称:去掉前缀 lib ,去掉后缀 .so , .a ,如: libc.so -> c  

2.2 使用makefile生成静态库

 • ar 是 gnu 归档工具, rc 表示 (replace and create)

• t: 列出静态库中的文件

• v:verbose 详细信息 

 三.动态库

• 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

• 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码

• 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中, 这个过程称为动态链接(dynamic  linking)

• 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

3.1动态库生成

初始文件

生成.o时要用fPIC: 

注意动态库和静态库不同,这里不需要用归档工具ar,直接用gcc就行,带上选项-shared: 

makefile也就只需要修改一点: 

 • shared:表示生成共享库格式 

• fPIC:产生位置无关码(position  independent  code) 

• 库名规则:libxxx.so

3.2动态库使用

动态库的使用和静态库的使用是一样的:

但是当我运行的时候:

虽然我们在上面的指令中带了选项指明我们的动态库的位置,这个“指明”是给gcc指明。静态库能运行是因为静态库直接在编译时直接链接到可执行文件中的库。而动态库是在运行时才被加载的,即可执行文件依赖于动态库的存在。

这里查看也是not found:

3.3库运行搜索路径

• 拷贝 .so 文件到系统共享库路径下, 一般指 /usr/lib、/usr/local/lib、/lib64 或者开篇指明的库路径等

• 向系统共享库路径下建立同名软连接

• 更改环境变量: LD_LIBRARY_PATH 

• ldconfig方案:配置/ etc/ld.so.conf.d/ ,ldconfig更新

第一种比较简单,看第二种:

之后别忘了解除: 

第三种是环境变量:

 

把我们动态库的路径加上去: 

 

这里就有了: 

 还有第四种,更改系统的配置文件:

在这里面创建一个文件:

往里面写上我们动态库的地址:

但还是找不到: 

最后一步重新加载搜索路径:

此时就有了。

结论

四.目标文件

编译和链接这两个步骤,在Windows下被我们的IDE封装的很完美,我们一般都是一键构建非常方便, 但一旦遇到错误的时候呢,尤其是链接相关的错误,很多⼈就束手无策了。

编译的过程其实就是将我们程序的源代码翻译成CPU能够直接运行的机器 代码。 比如:在一个源文件 hello.c 里便简单输出"hello world!",并且调用一个run函数,而这个函数被 定义在另一个原文件 code.c 中。这里我们就可以调用 gcc -c 来分别编译这两个原文件。

在编译之后会生成两个扩展名为 .o 的文件,它们被称作目标文件。要注意的是如果我们修改了一个原文件,那么只需要单独编译它这一个,而不需要浪费时间重新编译整个工程。目标文件是一个二进制的文件,文件的格式是 ELF ,是对二进制代码的一种封装。

五.ELF文件

要理解编译链链接的细节,我们不得不了解一下ELF文件。其实有以下四种文件其实都是ELF文件: 

• 可重定位文件(Relocatable File) :即xxx.o⽂件。包含适合于与其他目标文件链接来创建可执行文件或者共享,目标文件的代码和数据。

• 可执行文件(Executable File) :即可执行程序。

• 共享目标文件(Shared Object File) :即 xxx.so文件。

• 内核转储(core dumps) ,存放当前进程的执行上下文,用于dump信号触发。

一个ELF文件由以下四部分组成: 

• ELF头(ELF header) :描述文件的主要特性。其位于文件的开始位置,它的主要目的是定位文件的其他部分。

• 程序头表(Program header table) :列举了所有有效的段(segments)和他们的属性。表里记着每个段的开始的位置和位移(offset)、长度,毕竟这些段,都是紧密的放在二进制文件中, 需要段表的描述信息,才能把他们每个段分割开。

• 节头表(Section header table) :包含对节(sections)的描述。 

• 节(Section ):ELF文件中的基本组成单位,包含了特定类型的数据。ELF文件的各种信息和 数据都存储在不同的节中,如代码节存储了可执行代码,数据节存储了全局变量和静态数据等。  

最常见的节:

• 代码节(.text):用于保存机器指令,是程序的主要执行部分。

• 数据节(.data):保存已初始化的全局变量和局部静态变量。

六.ELF从形成到加载轮廓

6.1ELF形成可执行

• step-1:将多份 C/C++ 源代码,翻译成为目标 .o 文件

• step-2:将多份 .o 文件section进行合并

实际合并是在链接时进行的,但是并不是这么简单的合并,也会涉及对库合并。

6.2 ELF可执行文件加载

• 一个ELF会有多种不同的Section,在加载到内存的时候,也会进行Section合并,形成segment 

• 合并原则:相同属性,比如:可读,可写,可执行,需要加载时申请空间等. 

• 这样,即便是不同的Section,在加载到内存中,可能会以segment的形式,加载到一起 

• 很显然,这个合并工作也已经在形成ELF的时候,合并方式已经确定了,具体合并原则被记录在了 ELF的程序头表(Program header table) 中

 下面这个读的是ls命令的section headers

下面是读的 program headers

 上面的0-8就是我们可以合并的段,比如.data  .bss就合并到一起了。

 为什么要将section合并成为segment 

• Section合并的主要原因是为了减少页面碎片,提高内存使用效率。如果不进行合并, 假设页面大小为4096字节(磁盘中8个扇区合并后的块大小)(内存块基本大小,加载,管理的基本单位),如果.text部分 为4097字节,.init部分为512字节,那么它们将占用3个页面,而合并后,它们只需2个页面。

• 此外,操作系统在加载程序时,会将具有相同属性的section合并成一个大的 segment,这样就可以实现不同的访问权限,从而优化内存管理和权限访问控制。

对于 程序头表 和 节头表 又有什么用呢,其实ELF 文件提供 2 个不同的视图/视角来让我们理解这 两个部分:

• 链接视图(Linking view) - 对应节头表 Section header table

◦ 文件结构的粒度更细,将文件按功能模块的差异进行划分,静态链接分析的时候一般关注的是链接视图,能够理解 ELF 文件中包含的各个部分的信息。

◦ 为了空间布局上的效率,将来在链接目标文件时,链接器会把很多节(section)合并,规整成可执行的段(segment)、可读写的段、只读段等。合并了后,空间利用率就高了,否则,很小的很小的一段,未来物理内存页浪费太大(物理内存页分配一般都是整数倍一块给你,比如4k),所以,链接器趁着链接就把小块们都合并了。

• 执行视图(execution view) -对应程序头表 Program header table 

◦ 告诉操作系统,如何加载可执行文件,完成进程内存的初始化。一个可执行程序的格式中, 一定有 program header table 。

• 说白了就是:一个在链接时作用,一个在运行加载时作用。

 

从 链接视图 来看: 

• 命令 readelf -S hello.o 可以帮助查看ELF文件的 节头表。

• .text节 :是保存了程序代码指令的代码节。

• .data节 :保存了初始化的全局变量和局部静态变量等数据。

• .rodata节 :保存了只读的数据,如一行C语言代码中的字符串。由于.rodata节是只读的,所以只能存在于一个可执行文件的只读段中。因此,只能是在text段(不是data段)中找到.rodata 节。

• .BSS节 :为未初始化的全局变量和局部静态变量预留位置

• .symtab节 : Symbol Table符号表,就是源码里面那些函数名、变量名和代码的对应关系。

• .got.plt节 (全局偏移表-过程链接表):.got节保存了全局偏移表。.got节和.plt节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改。对于GOT的理解

        ◦ 使用 readelf 命令查看 .so 文件可以看到该节。

从 执行视图 来看: 

• 告诉操作系统哪些模块可以被加载进内存。

• 加载进内存之后哪些分段是可读可写,哪些分段是只读,哪些分段是可执行的。 

我们可以在 ELF头 中找到文件的基本信息,以及可以看到ELF头是如何定位程序头表和节头表的。例 如我们查看下ls的主要信息:

七.理解链接和加载

7.1静态链接

• 无论是自己的.o, 还是静态库中的.o,本质都是把.o文件进行链接的过程

• 所以:研究静态链接,本质就是研究.o是如何链接的

首先一个目录里有,main.exe是两个.o文件链接之后的:

 

 在两个.o文件在进行链接之前,先看一下它们的汇编代码:

• objdump -d 命令:将代码段(.text)进行反汇编查看

• hello.o 中的 main 函数不认识 printf和run 函数  

上面可以看出,在每单个的文件中调用函数的e8后面的地址跟的都是全0,其实就是在编译 hello.c 的时候,编译器是完全不知道 printf 和 run 函数的存在的,比如他们位于内存的哪个区块,代码长什么样都是不知道的。因此,编辑器只能将这两个函数的跳转地址先暂 时设为0

查看它们的符号表也是UND未定义:

当它们链接之后,main.s(就有了地址): 

查看main.exe的符号表: 

最终: 1. 两个.o的代码段合并到了一起,并进行了统一的编址

2. 链接的时候,会修改.o中没有确定的函数地址,在合并完成之后,进行相关call地址,完成代码调用(所以叫做可重定位目标文件!!

静态链接就是把库中的.o进行合并,和上述过程⼀样 

所以链接其实就是将编译之后的所有目标文件连同用到的一些静态库运行时库组合,拼装成一个独立的可执行文件。其中就包括我们之前提到的地址修正,当所有模块组合在一起之后,链接器会根据我们的.o文件或者静态库中的重定位表找到那些需要被重定位的函数全局变量,从而修正它们的地址。这 其实就是静态链接的过程。 

 7.2ELF加载与进程地址空间

7.2.1虚拟地址/逻辑地址

• 一个ELF程序,在没有被加载到内存的时候,本来就有地址,当代计算机工作的时候,都采用"平坦 模式"进行工作。所以也要求ELF对自己的代码和数据进行统一编址,下面是 objdump -S 反汇编 之后的代码

最左侧的就是ELF的虚拟地址,其实,严格意义上应该叫做逻辑地址(起始地址+偏移量),但是我们 认为起始地址是0.也就是说,其实虚拟地址在我们的程序还没有加载到内存的时候,就已经把可执 行程序进行统一编址了.

• 进程mm_struct、vm_area_struct在进程刚刚创建的时候,初始化数据从哪里来的?从ELF各个 segment来,每个segment有自己的起始地址和自己的长度,用来初始化内核结构中的[start, end] 等范围数据,另外在用详细地址,填充页表. 

所以:虚拟地址机制,不光光OS要支持,编译器也要支持.

 7.2.2重新理解进程虚拟地址空间

ELF 在被编译好之后,会把自己未来程序的入口地址记录在ELF header的Entry字段中:

 EIP存的是起始地址就是上面的Entry字段里面的地址,MMU是页表的起始地址。

 7.3动态链接与动态库加载

7.3.1进程如何看到动态库

7.3.2进程间共享库 

 7.3.3动态链接

7.3.3.1概要

动态链接其实远比静态链接要常用得多。比如我们查看下 main.exe 这个可执行程序依赖的动态库,会发现它就用到了一个c动态链接库:

这里的libc.so是C语言的运行时库,里面提供了常用的标准输入输出文件字符串处理等等这些功能。

那为什么编译器默认不使用静态链接呢?静态链接会将编译产生的所有目标⽂件,连同用到的各种 库,合并形成一个独立的可执行文件,它不需要额外的依赖就可以运行。照理来说应该更加方便才对是吧?

静态链接最大的问题在于生成的文件体积大,并且相当耗费内存资源。随着软件复杂度的提升,我们的操作系统也越来越臃肿,不同的软件就有可能都包含了相同的功能和代码,显然会浪费大量的硬盘空间。

这个时候,动态链接的优势就体现出来了,我们可以将需要共享的代码单独提取出来,保存成⼀个独立的动态链接库,等到程序运行的时候再将它们加载到内存,这样不但可以节省空间,因为同一个模块在内存中只需要保留一份副本,可以被不同的进程所共享。 

动态链接到底是如何工作的?? 首先要交代一个结论,动态链接实际上将链接的整个过程推迟到了程序加载的时候。比如我们去运行一个程序,操作系统会首先将程序的数据代码连同它用到的一系列动态库先加载到内存,其中每个动态库的加载地址都是不固定的,操作系统会根据当前地址空间的使用情况为它们动态分配一段内存。 当动态库被加载到内存以后,一旦它的内存地址被确定,我们就可以去修正动态库中的那些函数跳转 地址了。

7.3.3.2我们的可执行程序被编译器动了手脚

 在C/C++程序中,当程序开始执行时,它首先并不会直接跳转到 main 函数。实际上,程序的入口点是 _start ,这是一个由C运行时库(通常是glibc)或链接器(如ld)提供的特殊函数。

在 _start 函数中,会执行一系列初始化操作,这些操作包括:

1. 设置堆栈:为程序创建一个初始的堆栈环境。

2. 初始化数据段:将程序的数据段(如全局变量和静态变量)从初始化数据段复制到相应的内存位 置,并清零未初始化的数据段。

3. 动态链接:这是关键的一步, _start 函数会调用动态链接器的代码来解析和加载程序所依赖的 动态库(shared  libraries)。动态链接器会处理所有的符号解析和重定位,确保程序中的函数调 用和变量访问能够正确地映射到动态库中的实际地址。

动态链接器:

◦ 动态链接器(如ld-linux.so)负责在程序运行时加载动态库。

◦ 当程序启动时,动态链接器会解析程序中的动态库依赖,并加载这些库到内存中。

环境变量和配置文件:

◦ Linux系统通过环境变量(如LD_LIBRARY_PATH)和配置文件(如/etc/ld.so.conf及其子配置文件)来指定动态库的搜索路径。

◦ 这些路径会被动态链接器在加载动态库时搜索。

缓存文件:

◦ 为了提高动态库的加载效率,Linux系统会维护一个名为/etc/ld.so.cache的缓存文件。 

◦ 该文件包含了系统中所有已知动态库的路径和相关信息,动态链接器在加载动态库时会首先搜索这个缓存文件。

4. 调用 __libc_start_main :一旦动态链接完成, _start 函数会调用 __libc_start_main (这是glibc提供的一个函数)。 __libc_start_main 函数负责执行一些额外的初始化工作,比如设置信号处理函数、初始化线程库(如果使用了线程)等。

5. 调用 main 函数:最后, __libc_start_main 函数会调用程序的 main 函数,此时程序的执行控制权才正式交给用户编写的代码。

6. 处理 main 函数的返回值:当 main 函数返回时, __libc_start_main 会负责处理这个返回 值,并最终调用_exit 函数来终止程序(退出码)。

 7.3.3.3动态库中的相对地址

动态库为了随时进行加载,为了支持并映射到任意进程的任意位置,对动态库中的方法,统一编址, 采用相对编址的方案进行编制的(其实可执行程序也一样,都要遵守平坦模式,只不过exe是直接加载的)。

7.3.3.4我们的程序,怎么和库具体映射起来的

• 动态库也是一个文件,要访问也是要被先加载,要加载也是要被打开的

• 让我们的进程找到动态库的本质:也是文件操作,不过我们访问库函数,通过虚拟地址进行跳转访问的,所以需要把动态库映射到进程的地址空间中

7.3.3.5我们的程序,怎么进行库函数调用

• 库已经被我们映射到了当前进程的地址空间中

• 库的虚拟起始地址我们也已经知道了

• 库中每一个方法的偏移量地址我们也知道

• 所以:访问库中任意方法,只需要知道库的起始虚拟地址+方法偏移量即可定位库中的方法

• 而且:整个调用过程,是从代码区跳转到共享区,调用完毕在返回到代码区,整个过程完 全在进程地址空间中进行的. 

 

7.3.3.6全局偏移量表GOT(global  offset  table) 

• 也就是说,我们的程序运行之前,先把所有库加载并映射,所有库的起始虚拟地址都应该提前知道

• 然后对我们加载到内存中的程序的库函数调用进行地址修改,在内存中二次完成地址设置 (这个叫做加载地址重定位) 

• 等等,修改的是代码区?不是说代码区在进程中是只读的吗?怎么修改?能修改吗?

 所以:动态链接采用的做法是在 .data (可执行程序或者库自己)中专门预留一片区域来存放函数 的跳转地址,它也被叫做全局偏移表GOT,表中每一项都是本运行模块要引用的一个全局变量或函数的地址。

• 因为.data区域是可读写的,所以可以支持动态进行修改

# .got在加载的时候,会和.data合并成为一个segment,然后加载在一起

1. 由于代码段只读,我们不能直接修改代码段。但有了GOT表,代码便可以被所有进程共享。但在不同进程的地址空间中,各动态库的绝对地址、相对位置都不同。反映到GOT表上,就是每个进程的每个动态库都有独立的GOT表,所以进程间不能共享GOT表。

2. 在单个.so下,由于GOT表与 .text 的相对位置是固定的,我们完全可以利用CPU的相对寻址来找到GOT表

3. 在调用函数的时候会首先查表,然后根据表中的地址来进行跳转,这些地址在动态库加载的时候会被修改为真正的地址。

4. 这种方式实现的动态链接就被叫做 PIC 地址无关代码 。换句话说,我们的动态库不需要做任何修改,被加载到任意内存地址都能够正常运行,并且能够被所有进程共享,这也是为什么之前我们给编译器指定-fPIC参数的原因,PIC=相对编址+GOT。

7.3.3.7库间依赖 

plt是什么 

• 不仅仅有可执行程序调用库

• 库也会调用其他库!!库之间是有依赖的,如何做到库和库之间互相调用也是与地址无关的呢??

• 库中也有.GOT,和可执行一样!这也就是为什么大家为什么都是ELF的格式!

• 由于动态链接在程序加载的时候需要对大量函数进行重定位,这一步显然是非常耗时的。为了进一步降低开销,我们的操作系统还做了一些其他的优化,比如延迟绑定,或者也叫PLT(过程连接表 (Procedure  Linkage  Table))。与其在程序一开始就对所有函数进行重定位,不如将这个过程推迟到函数第一次被调用的时候,因为绝大多数动态库中的函数可能在程序运行期间一次都不会被使用到。

思路是:GOT中的跳转地址默认会指向一段辅助代码,它也被叫做桩代码/stup。在我们第一次调用函数的时候,这段代码会负责查询真正函数的跳转地址,并且去更新GOT表。于是我们再次调用函数的时候,就会直接跳转到动态库中真正的函数实现。 

总而言之,动态链接实际上将链接的整个过程,比如符号查询、地址的重定位从编译时推迟到了程序的运行时,它虽然牺牲了一定的性能和程序加载时间,但绝对是物有所值的。因为动态链接能够更有效的利用磁盘空间和内存资源,以极大方便了代码的更新和维护,更关键的是,它实现了二进制级别的代码复用。 

7.3.4总结

• 静态链接的出现,提高了程序的模块化水平。对于一个大的项目,不同的人可以独立地测试和开发自己的模块。通过静态链接,生成最终的可执行文件。

• 我们知道静态链接会将编译产生的所有目标文件,和用到的各种库合并成一个独立的可执行文件, 其中我们会去修正模块间函数的跳转地址,也被叫做编译重定位(也叫做静态重定位)。

• 而动态链接实际上将链接的整个过程推迟到了程序加载的时候。比如我们去运行一个程序,操作系统会首先将程序的数据代码连同它用到的一系列动态库先加载到内存,其中每个动态库的加载地址都是不固定的,但是无论加载到什么地方,都要映射到进程对应的地址空间,然后通过.GOT方式进行调用(运行重定位,也叫做动态地址重定位)。

相关文章:

Linux库制作与原理:【静态库】【动态库】【目标文件】【ELF文件】【ELF从形成到假造轮廓】【理解链接和加载】

目录 一.什么是库 二.静态库 2.1创建静态库 我们在之前的路径下新建lib使用我们自己的库 2.2 使用makefile生成静态库 三.动态库 3.1动态库生成 3.2动态库使用 3.3库运行搜索路径 四.目标文件 五.ELF文件 六.ELF从形成到加载轮廓 6.1ELF形成可执行 6.2 ELF可执行文…...

项目BUG

项目BUG 前言 我创作这篇博客的目的是记录学习技术过程中的笔记。希望通过分享自己的学习经历,能够帮助到那些对相关领域感兴趣或者正在学习的人们。 项目BUG 1.低频率信号(100k或 200K以下)可以直接用一根导线焊接出几根导线来分几路,高频率信号只能…...

wordpress部署nginx版的

一、通过nginx部署wordpress 1、用yum源安装nginx yum install -y nginx 2、安装php相关软件 前提安装webtatic rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm 通过yum源安装php相关软件 yum -y install php72w php72w-pdo php72w-mysqlnd php72w…...

【鸿蒙Next】优秀鸿蒙博客集锦

鸿蒙基础开发:多文件压缩上传及断点续传_鸿蒙 断点续传-CSDN博客...

【第2章:神经网络基础与实现——2.1 前馈神经网络的结构与工作原理】

老铁们好!今天我们要来一场长达两万字的超详细技术探险,我会像拆解乐高积木一样把前馈神经网络(Feedforward Neural Network)的每个零件摆在台面上,用最接地气的方式让你彻底搞懂这个深度学习基石的工作原理。准备好了吗?我们开始吧! 第一章:神经网络的 “乐高积木” 1…...

python-leetcode-阶乘后的零

172. 阶乘后的零 - 力扣(LeetCode) class Solution:def trailingZeroes(self, n: int) -> int:count 0while n > 5:n // 5count nreturn count...

Python:学生管理系统(继承性、多态性)。

输出样例如图: 题目内容: 利用继承、多态性等面向对象程序功能编写程序,实现学生管理系统,并包含以下内容: 第一,基类为学生类,并以此派生出本科生类、研究生类。 第二,本科生类包含…...

网络安全RSA加密

网络安全课相关知识: RSA预备知识 1.1 快速幂算法 顾名思义,快速幂就是快速算底数的$n$次幂。其时间复杂度为${\rm{O(log n)}}$,与朴素的$O\left( n \right)$相比,效率有了极大的提高。具体可以参考百度百科:快速幂。…...

Vue学习笔记4

Vue学习笔记 一、自定义创建项目 基于VueCli自定义创建项目架子 二、vuex基本认知 1、vuex概述 是什么:是vue的状态管理工具(插件),状态就是数据 大白话:vuex是一个插件,可以帮助我们管理vue通用的数…...

mariadb数据库的安装与部署

1、通过yum源安装mariadb数据库 yum -y install mariadb-server 2、启动mariadb数据库服务 systemctl start mariadb.service 3、配置mariadb数据库全局环境变量 systemctl enable mariadb.service 4、修改mariadb数据库默认密码,数据库默认密码为空 执行…...

单调队列与栈

一.题 1. 思路&#xff1a; 构建小压大的单调递减栈&#xff0c;对于每个栈的元素都进行处理并加到结果上 class Solution { public:int sumSubarrayMins(vector<int>& arr) {int stk[10000000],top 0;long long ans 0;for(int i 0;i<arr.size();i){while(top…...

Matlab 多项式曲线拟合(三维)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 对于高维空间曲线的拟合,参数化是一种非常好的方式,可以让我们很容易得到我们想要的目标曲线。 假设给定一组数据点 ( u i ​ , x i ​ ) 、 ( u i ​...

机器翻译同样的文本,是从英语翻译成日语更准确还是中文翻译成日语更准确

在大多数情况下&#xff0c;从英语翻译成日语会比从中文翻译成日语更准确&#xff0c;原因如下&#xff1a; 1. 语言结构的相似性 英语和日语的句子结构更接近&#xff0c;特别是在语法、从句使用、定语位置等方面。例如&#xff0c;日语和英语都使用 SVO 结构&#xff08;主…...

MAC 系统关屏幕后电量消耗极快 Wake Requests

日志为 Wake Requests [*processdasd requestSleepService…info"com.apple.alarm.user-invisible-com.apple.calaccessd… 本人有效方法为&#xff1a; sudo pmset -a hibernatemode 25 sudo pmset -a standby 0 sudo pmset -a autopoweroff 0 会导致hibernatemode 25是…...

golangAPI调用deepseek

目录 1.deepseek官方API调用文档1.访问格式2.curl组装 2.go代码1. config 配置2.模型相关3.错误处理4.deepseekAPI接口实现5. 调用使用 3.响应实例 1.deepseek官方API调用文档 1.访问格式 现在我们来解析这个curl 2.curl组装 // 这是请求头要加的参数-H "Content-Type:…...

提供可传递的易受攻击的依赖项

问题如图所示&#xff1a; 原因&#xff1a;okhttp3.version 3.14.9 版本存在部分漏洞&#xff0c;在 maven 仓库是可以看到的 maven 地址&#xff1a; maven 下图中 Vulnerabilities 即为漏洞 处理&#xff1a;换一个无漏洞的版本即可...

2.14学习记录

Web flag直接读取不就行了&#xff1f; 代码审计&#xff1a; <?php highlight_file(index.php); # 我把flag藏在一个secret文件夹里面了&#xff0c;所以要学会遍历啊~ error_reporting(0); $J1ng $_POST[J]; $Hong $_POST[H]; $Keng $_GET[K]; $Wang $_GET[W]; $d…...

xpath定位--鼠标悬停显示的按钮

UI自动化定位界面元素的过程中&#xff0c;会遇到鼠标悬停才会显示的按钮&#xff0c;鼠标移开就不显示了&#xff0c;无法通过点击它直接定位到元素位置 搜索到这篇文档&#xff0c;办法很好用&#xff0c;特此记录下&#xff1a;chrome调试鼠标悬停后出现的元素_控制台元素调…...

鸿蒙Harmony打包脚本使用整理

最近整理鸿蒙打包相关事宜&#xff0c;遇到很多文档描述不清晰的问题&#xff0c;好在都通过鸿蒙团队的技术支持解决掉了。这里整理一下。 command-line-tools的命令官网基本都有&#xff0c;这里整理几个常用的&#xff0c;还有就是遇到的问题。 hvigorw位置&#xff1a;/comm…...

【C语言】C语言 停车场管理系统的设计与实现(源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 系列文章目录 目录 系列文章目录一、设计要求二、设…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...