ARM交叉编译入门及交叉编译第三方库常见问题解析
1. 交叉编译是什么?
交叉编译简单说来,就是编译成果物的地儿不是你运行这个成果物的地儿。最常见的场景,就是我们要编译一个 ARM版本 的可执行程序,但我们编译这个 ARM版本 可执行程序的地方,是在一个 x86_x64 的平台上。
2. 为什么需要交叉编译?
绝大部分的原因,是目标平台不具备编译成果物的算力。具体说来,就是ARM平台早期是 并没有 编译代码所需的 算力 和 相关空间 的。所以,不得不借助性能更高的平台来辅助进行编译成果物,然后ARM平台仅负责运行成果物即可。
3. 交叉编译只能在目标平台同一系统上吗?
虽然绝大多数的 ARM Linux系统中编译的成果物是在对应的 x86_x64平台的Linux系统中进行的,所以大多数时候使用Windows平台电脑需要安装一个虚拟机或者连接到某个x86_x64平台的Linux编译服务器中,但实际上这种搭配大多数情况是为了方便,也为了让编译者熟悉Linux环境,但这种搭配并不是唯一的解决方案。
ARM官网 上就能直接下载各种平台( Linux、Windows )的编译工具链,另外一个很常见的第三方工具链制作商,linaro也是对外直接提供可用版本的工具链,但不支持全平台, linaro gnu。
如果上述两个网站你都觉得不是很符合自己的开发板,那么你也可以自己动手做一个链子,符合目标平台的交叉编译链制作及简单分析。 这里主要使用crosstool-ng这个工具进行的制作,自己做链子的好处是你可以自己按需选择对应的gcc版本,以及glibc版本和一些其他重要基础库的版本。而上面现成的链子可能并不刚好是你需要的版本搭配。
4. 如何编译一个第三方开源库?
以下编译前提均假设编译人员已经获得了一个可以使用的交叉编译工具链,并且已经将编译工具链的可执行程序设置进环境变量。
4.1 最常见也是最简单的编译方式
大多数的简单第三方库,均可以尝试以下的方式。这里的简单,值得是不额外依赖一些其他第三方库的库。
configure --host=arm-linux-gnueabi --prefix=${PWD}/build
make
make install
一般验证自己是不是编译的正确的主要检查步骤就是,在make的时候查看对应输出打印,如果开头的的编译的确是对应编译工具链名称的开头,那么至少配置是对的,这里给个例子:
/bin/sh ../libtool --tag=CXX --mode=compile arm-at91-linux-gnueabi-g++ -std=gnu++11 -DHAVE_CONFIG_H -I. -I.. -I/data1/xiaoyanyi/work/snmp++/snmp++-3.5.0/include -pthread -I/data1/xiaoyanyi/work/openssllll/include -D_GNU_SOURCE -g -O2 -pthread -MT asn1.lo -MD -MP -MF .deps/asn1.Tpo -c -o asn1.lo asn1.cpp
libtool: compile: arm-at91-linux-gnueabi-g++ -std=gnu++11 -DHAVE_CONFIG_H -I. -I.. -I/data1/xiaoyanyi/work/snmp++/snmp++-3.5.0/include -pthread -I/data1/xiaoyanyi/work/openssllll/include -D_GNU_SOURCE -g -O2 -pthread -MT asn1.lo -MD -MP -MF .deps/asn1.Tpo -c asn1.cpp -fPIC -DPIC -o .libs/asn1.o
libtool: compile: arm-at91-linux-gnueabi-g++ -std=gnu++11 -DHAVE_CONFIG_H -I. -I.. -I/data1/xiaoyanyi/work/snmp++/snmp++-3.5.0/include -pthread -I/data1/xiaoyanyi/work/openssllll/include -D_GNU_SOURCE -g -O2 -pthread -MT asn1.lo -MD -MP -MF .deps/asn1.Tpo -c asn1.cpp -o asn1.o >/dev/null 2>&1
make的过程中,输出的打印里的确是以我们指定的host开头的,那么这步骤至少是没问题的了。
4.2.1 简单解释一下configure里面常见通用参数的含义
先简单的介绍一下 configure make make install三部曲每部分是做了什么,然后在展开介绍configure的通用参数。
configure: 通过配置生成makefile文件
make:使用configure生成的Makefile文件进行编译工作
make install:将make生成的成果物文件按照prefix的路径进行复制,有时候也会动态生成一些说明文档
所以大多数时候,configure配置对了,后面两步就一定能走通,99%的编译不过问题基本都是configure的时候配置不对。以下重点讲解configure相关的参数。
4.2.1.1 prefix
这里prefix中文解释就是安装路径,也就是make install的时候,最终这些库会放到哪里去。一般对于交叉编译而言,是需要指定的,因为默认的路径是 /usr/local/,但这路径实际上对于交叉编译而言一定是不行的。因为这个路径通常放的是本身系统的库,如果交叉编译的库放进去后,本身系统也会去检索这个路径下的库,名字虽然匹配上了,但是用不起来,后续会造成极大的麻烦。
这里还有一点需要额外注意的,后文会重新展开这个问题。这里先说结论:
如果你需要编译的库ABC依赖DEF,那么你先编译DEF的时候,最好把prefix设置成自己交叉编译链的
sysroot/usr中。
4.2.1.2 host
简单解释一下,交叉编译和普通的编译第三方库差异主要在于需要指定host这个变量。这里贴一段标准解释:
System types:--build=BUILD configure for building on BUILD [guessed]--host=HOST cross-compile to build programs to run on HOST [BUILD]
这里仅需要额外强调一点,host这个参数的指定逻辑和使用的目标编译工具链名称有关,假设你的编译工具链的gcc名字叫 arm-at91-linux-gnueabi-gcc,那么这里的host名字就是 arm-at91-linux-gnueabi。具体的逻辑就是把对应链子的 -gcc部分拆掉就是host的名字。其实configure脚本的逻辑也就是对应的反向在host后拼接一个 -gcc而已。
4.2.1.3 enable-xxxxx disable-xxxxx
这两个参数一般是在编译的时候,可配置的打开某些功能或者关闭某些功能的时候,会使用到。每个第三方库的特色不一样,这里推荐遇到编译不过的问题,首先就去看看:
./configure --help
有时候发现你编译的第三方库依赖了过多的其他库,而且这些功能对你并不需要的时候,可以尽可能的--disable-xxxxx和 --without-xxxx。这样,在后续make的时候,就不会出现某些依赖库找不到的报错了。
4.2.1.4 CXX CC CFLAGS
这些shell环境变量也会产生一定的效果,有时候你百度一些博客教程的时候,会搜到交叉编译的某些指导文档会这么写:
./configure CC=arm-linux-gnueabi-gcc --prefix=${PWD}/build
make
make install
或者写成这样:
export CC=arm-linux-gnueabi-gcc
./configure --prefix=${PWD}/build
make
make instal
这两种手法的思路一致的,都是利用./configure 脚本是可以阅读当前shell环境变量中的CC,并且将这个环境变量替换到脚本里,从而实现CC替换成对应链子的目的。相关解释同样可以在--help中看到
Some influential environment variables:CC C compiler commandCFLAGS C compiler flagsLDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in anonstandard directory <lib dir>
但需要注意的一点是:这种方法有一些隐患,主要在于在很多时候,交叉编译并不是把gcc变成arm-linux-gnueabi-gcc一切就完事了。我们可以在Makefile中和交叉编译链的bin路径中看到如下打印:
(base) xiaoyanyi@snmp++-3.5.0$ls ~/cross-tool/arm-at91-linux-gnueabi/bin
arm-at91-linux-gnueabi-addr2line arm-at91-linux-gnueabi-gcc-4.9.2 arm-at91-linux-gnueabi-nm
arm-at91-linux-gnueabi-ar arm-at91-linux-gnueabi-gcc-ar arm-at91-linux-gnueabi-objcopy
arm-at91-linux-gnueabi-as arm-at91-linux-gnueabi-gcc-nm arm-at91-linux-gnueabi-objdump
arm-at91-linux-gnueabi-c++ arm-at91-linux-gnueabi-gcc-ranlib arm-at91-linux-gnueabi-populate
arm-at91-linux-gnueabi-cc arm-at91-linux-gnueabi-gcov arm-at91-linux-gnueabi-ranlib
arm-at91-linux-gnueabi-c++filt arm-at91-linux-gnueabi-gdb arm-at91-linux-gnueabi-readelf
arm-at91-linux-gnueabi-cpp arm-at91-linux-gnueabi-gprof arm-at91-linux-gnueabi-size
arm-at91-linux-gnueabi-ct-ng.config arm-at91-linux-gnueabi-ld arm-at91-linux-gnueabi-strings
arm-at91-linux-gnueabi-elfedit arm-at91-linux-gnueabi-ld.bfd arm-at91-linux-gnueabi-strip
arm-at91-linux-gnueabi-g++ arm-at91-linux-gnueabi-ldd
arm-at91-linux-gnueabi-gcc arm-at91-linux-gnueabi-ld.gold
有些Makefile生成的环境中还需要使用AR,NM,RANLIB等等,这些东西也需要使用对应的交叉编译工具链版本的程序。而上述的CC手法仅仅替换了一个。所以为什么是存在风险和后续还会遇到问题的。
这里推荐,如果能用host,就优先使用host指定,这种方式相当于自动帮你设置了上述每一个需要替换的变量。
在极少数的时候,host不被confiugre支持,那么能用的方法只有这种了,但同时也是最不同推荐的。
4.2.2 对于编译自依赖的第三方库族的推荐方法
有时候,我们使用的第三方库有两层甚至多层,底层他们自己开发了一个基础库,然后在自己的基础库上,封了一层应用。例如protobuf和grpc,snmp++和agent++。这个时候,我们需要先编译基础库,然后在编译应用库。这一点很容易理解,从下到上,但对于交叉编译来说,又有一点需要注意的。主要和 prefix 有关。
当我们编译完基础库之后,一般make install之后,成果物大多情况是这样的[假设我们安装到了一个build目录]:
(base) xiaoyanyi@build$ls
bin include lib
bin: 目录一般是这个库的一些demo样例或者一些可执行程序。
include:目录一般就是这个第三方库的头文件
lib:目录一般就是这个第三方库的静态、动态库文件
那么在编译上层应用库的时候,有些教程会推荐按照configure中的指定底层库路径宏变量的方式,去显式指定对应路径。
例如agent++这个库,依赖snmp++,其中agent++的confiugre文件里有这样一个变量:
Some influential environment variables:PKG_CONFIG path to pkg-config utilityPKG_CONFIG_PATHdirectories to add to pkg-config's search pathPKG_CONFIG_LIBDIRpath overriding pkg-config's built-in search pathCXXCPP C++ preprocessorsnmp_CFLAGS C compiler flags for snmp, overriding pkg-configsnmp_LIBS linker flags for snmp, overriding pkg-configLT_SYS_LIBRARY_PATHUser-defined run-time library search path.
这里可以通过snmp_LIB这个变量显式指定去libsnmp++.so的位置。这种方式在表面上是能够通过编译并且不会出现什么太大问题的。但会买下一个隐患在于后续使用库或者维护的时候会出现问题。这里需要讲解造成这个隐患问题的另外两个文件。
4.2.2.1 *.la
一般编译完成的第三方库的lib文件夹下面,会有一个对应扩展名为la的文件。
例如:
(base) xiaoyanyi@lib$ls
libsnmp++.a libsnmp++.la libsnmp++.so libsnmp++.so.35 libsnmp++.so.35.0.0 pkgconfig
这个la文件实际上并不是一个静/动态库程序,而是一个配置文件,我们可以直接vim打开:
# libsnmp++.la - a libtool library file
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-14
#
# Please DO NOT delete this file!
# It is necessary for linking the library.# The name that we can dlopen(3).
dlname='libsnmp++.so.35'# Names of this library.
library_names='libsnmp++.so.35.0.0 libsnmp++.so.35 libsnmp++.so'# The name of the static archive.
old_library='libsnmp++.a'# Linker flags that cannot go in dependency_libs.
inherited_linker_flags=' -pthread'# Libraries that this one depends upon.
dependency_libs=' -lssl -lcrypto
/data1/xiaoyanyi/cross-tool/arm-at91-linux-gnueabi/arm-at91-linux-gnueabi/lib/libstdc++.la'# Names of additional weak libraries provided by this library
weak_library_names=''# Version information for libsnmp++.
current=35
age=0
revision=0# Is this an already installed library?
installed=yes# Should we warn about portability when linking against -modules?
shouldnotlink=no# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''# Directory that this library needs to be installed in:
libdir='/data1/xiaoyanyi/work/snmp++/snmp++-3.5.0/build/lib'
关键的问题就在最后的
libdir里,这个路径指明了这个动态库的安装路径,在连接的过程中,如果有动态库依赖动态库的情况,gcc的连接应用会顺着路径去查找随影目标文件。而如果我们安装的时候,就随意选一个build目录。最后即便是把这个路径放到了sysroot里去之后,这个la文件里面的内容依旧不会变,导致最后就找不到了。
4.2.2.2 pkgconfig *.pc
大多数库编完之后,除了上述的la,其实大家也可以在lib里面找到有一个pkgconfig文件夹,里面会有一个对应的pc文件。
这个文件和la文件的作用极为相似,也是一个配置文件,我们可以打开看看。
prefix=/data1/xiaoyanyi/work/snmp++/snmp++-3.5.0/build
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
modules=Name: snmp++
Version: 3.5.0
Description: SNMP C++ framework version 3
Requires:
Libs: -L${libdir} -lsnmp++
Libs.private: -lssl -lcrypto
Cflags: -I${includedir}
这里造成错误的原因是因为prefix这个变量也是会因为随意指定从而即便pc文件的位置移动也无法正确索引。pc文件主要是pkg-config这个应用为了编译的时候自动指定FLAGS和自动找库用的。
4.2.2.3 那么应该安装在哪里比较合适呢?
这里推荐的路径是安装在编译工具链的sysroot中。
一般用crosstool-ng做的交叉工具链,对应的sysroot是在
${host}/${host}/sysroot/
对于其他的链子,基本也都有一个sysroot,可以自行查找。而我们安装一般的第三方库,一般推荐放在sysroot中的usr中,因为不是系统库,而是用户自己制作的。
这个路径下的,例如我做了一个链子,host是 arm-at91-linux-gnueabi,那么对应的sysroot/usr路径是:
arm-at91-linux-gnueabi/arm-at91-linux-gnueabi/sysroot/usr/
所以绝大部分的时候,将第三方库的prefix指定为上述路径,可以直接将第三方库安装到编译工具链中,从而达到后续编译其他库的时候,链子会自动索引已经安装过的库,不需要显示指定对应路径的目的。
但这里同样有一个风险点:如果对应第三方库的更新较为频繁,那么就可能存在要编译的新库但sysroot里有个老库的场景。这里推荐是更新较为频繁的库不要放到sysroot里去。
4.2 一些不常见但是常用的第三方库可能的编译方式
4.1里介绍的手法基本上能处理绝大多数的第三方库,但仍旧有某些库的configure写的比较奇特,按照常规三部曲无法解决的。一般通用的处理思路还是好好阅读configure --help,同时这里重点解释两个场景。
4.2.1 常见开源加密算法库OpenSSL
这个动态库的交叉编译就不合适上述的情况,configure中并没有指定host的能力,这里推荐的建议是这样,先说结果:
./Configure linux-armv4 no-asm shared
这个库为什么会这么输入,我们又是如何知道的呢?其实还是看configure --help就能发现端倪。
(base) xiaoyanyi@openssl-1.0.2t$./Configure --help
Configuring for
Usage: Configure [no-<cipher> ...] [enable-<cipher> ...] [experimental-<cipher> ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-dso] [no-krb5] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--test-sanity] os/compiler[:flags]pick os/compiler from:
BC-32 BS2000-OSD BSD-generic32 BSD-generic64 BSD-ia64 BSD-sparc64 BSD-sparcv8
BSD-x86 BSD-x86-elf BSD-x86_64 Cygwin Cygwin-x86_64 DJGPP MPE/iX-gcc OS2-EMX
OS390-Unix QNX6 QNX6-i386 ReliantUNIX SINIX SINIX-N UWIN VC-CE VC-WIN32
VC-WIN64A VC-WIN64I aix-cc aix-gcc aix3-cc aix64-cc aix64-gcc android
android-armv7 android-mips android-x86 android64-aarch64 aux3-gcc
beos-x86-bone beos-x86-r5 bsdi-elf-gcc cc cray-j90 cray-t3e darwin-i386-cc
darwin-ppc-cc darwin64-ppc-cc darwin64-x86_64-cc dgux-R3-gcc dgux-R4-gcc
dgux-R4-x86-gcc dist gcc hpux-cc hpux-gcc hpux-ia64-cc hpux-ia64-gcc
hpux-parisc-cc hpux-parisc-cc-o4 hpux-parisc-gcc hpux-parisc1_1-cc
hpux-parisc1_1-gcc hpux-parisc2-cc hpux-parisc2-gcc hpux64-ia64-cc
hpux64-ia64-gcc hpux64-parisc2-cc hpux64-parisc2-gcc hurd-x86 iphoneos-cross
irix-cc irix-gcc irix-mips3-cc irix-mips3-gcc irix64-mips4-cc irix64-mips4-gcc
linux-aarch64 linux-alpha+bwx-ccc linux-alpha+bwx-gcc linux-alpha-ccc
linux-alpha-gcc linux-aout linux-armv4 linux-elf linux-generic32
linux-generic64 linux-ia32-icc linux-ia64 linux-ia64-icc linux-mips32
linux-mips64 linux-ppc linux-ppc64 linux-ppc64le linux-sparcv8 linux-sparcv9
linux-x32 linux-x86_64 linux-x86_64-clang linux-x86_64-icc linux32-s390x
linux64-mips64 linux64-s390x linux64-sparcv9 mingw mingw64 ncr-scde
netware-clib netware-clib-bsdsock netware-clib-bsdsock-gcc netware-clib-gcc
netware-libc netware-libc-bsdsock netware-libc-bsdsock-gcc netware-libc-gcc
newsos4-gcc nextstep nextstep3.3 osf1-alpha-cc osf1-alpha-gcc purify qnx4
rhapsody-ppc-cc sco5-cc sco5-gcc solaris-sparcv7-cc solaris-sparcv7-gcc
solaris-sparcv8-cc solaris-sparcv8-gcc solaris-sparcv9-cc solaris-sparcv9-gcc
solaris-x86-cc solaris-x86-gcc solaris64-sparcv9-cc solaris64-sparcv9-gcc
solaris64-x86_64-cc solaris64-x86_64-gcc sunos-gcc tandem-c89 tru64-alpha-cc
uClinux-dist uClinux-dist64 ultrix-cc ultrix-gcc unixware-2.0 unixware-2.1
unixware-7 unixware-7-gcc vos-gcc vxworks-mips vxworks-ppc405 vxworks-ppc60x
vxworks-ppc750 vxworks-ppc750-debug vxworks-ppc860 vxworks-ppcgen
vxworks-simlinux debug debug-BSD-x86-elf debug-VC-WIN32 debug-VC-WIN64A
debug-VC-WIN64I debug-ben debug-ben-darwin64 debug-ben-debug
debug-ben-debug-64 debug-ben-debug-64-clang debug-ben-macos
debug-ben-macos-gcc46 debug-ben-no-opt debug-ben-openbsd
debug-ben-openbsd-debug debug-ben-strict debug-bodo debug-darwin-i386-cc
debug-darwin-ppc-cc debug-darwin64-x86_64-cc debug-geoff32 debug-geoff64
debug-levitte-linux-elf debug-levitte-linux-elf-extreme
debug-levitte-linux-noasm debug-levitte-linux-noasm-extreme debug-linux-elf
debug-linux-elf-noefence debug-linux-generic32 debug-linux-generic64
debug-linux-ia32-aes debug-linux-pentium debug-linux-ppro debug-linux-x86_64
debug-linux-x86_64-clang debug-rse debug-solaris-sparcv8-cc
debug-solaris-sparcv8-gcc debug-solaris-sparcv9-cc debug-solaris-sparcv9-gcc
debug-steve-opt debug-steve32 debug-steve64 debug-vos-gccNOTE: If in doubt, on Unix-ish systems use './config'.
这个库显式指定了所有它能支持的平台种类,所以我们只能按照这个指定了。这样指定完之后,需要把生成的Makefile中对应宏定义进行显式修改,包括但不限于CC、AR、NM、RANLIB、CXX等等。
4.2.2 有些库压根就没有configure,需要用cmake
有些开源库就没给configure,但是能看到cmakelist的文件,那么这个时候,把cmakelist当做configure就好,但是它的指定写法会稍显不同。一般需要提供一个对应的cmake配置,对于我们交叉编译来说,需要指定对应的host系统名称CMAKE_SYSTEM_NAME和处理器名称CMAKE_SYSTEM_PROCESSOR,然后需要指定对应的编译链路径CMAKE_C_COMPILER和CMAKE_CXX_COMPILER。
这里给出一个样例:
#arm.cmakeset(CMAKE_SYSTEM_NAME Linux)set(CMAKE_SYSTEM_PROCESSOR arm)set(tools /data1/xiaoyanyi/cross-tool/arm-imx6ul-linux-gnueabihf/bin/arm-imx6ul-linux-gnueabihf-)set(CMAKE_C_COMPILER ${tools}gcc)set(CMAKE_CXX_COMPILER ${tools}g++)
cmake文件准备好后,就可以直接cmake了,机理是和configure一样的:
cmake CMakeLists.txt -DCMAKE_TOOLCHAIN_FILE=./arm.cmake
上述命令执行完后,最终会生成一个Makefile文件,接着make,make install就好了。
5. 总结
-
绝大多数场景,交叉编译的时候
configure,make,make install三部曲就好,与普通编译不一样的是需要指定host -
自依赖第三方库在不频繁迭代更新的条件下,建议安装到交叉编译链的sysroot目录中
-
如果怎么编译都有点问题,建议仔细阅读
configure --help或者README分析查找端倪
相关文章:
ARM交叉编译入门及交叉编译第三方库常见问题解析
1. 交叉编译是什么? 交叉编译简单说来,就是编译成果物的地儿不是你运行这个成果物的地儿。最常见的场景,就是我们要编译一个 ARM版本 的可执行程序,但我们编译这个 ARM版本 可执行程序的地方,是在一个 x86_x64 的平台…...
Ruby Web Service 应用 - SOAP4R
什么是 SOAP? 简单对象访问协议(SOAP,全写为Simple Object Access Protocol)是交换数据的一种协议规范。 SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息。 简单对象访问协议是交换数据的一种协议规范,是一种轻量的、…...
HashMap底层实现原理概述
原文https://blog.csdn.net/fedorafrog/article/details/115478407 hashMap结构 常见问题 在理解了HashMap的整体架构的基础上,我们可以试着回答一下下面的几个问题,如果对其中的某几个问题还有疑惑,那就说明我们还需要深入代码,…...
Linux驱动学习环境搭建
背景常识 一、程序分类 程序按其运行环境分为: 1. 裸机程序:直接运行在对应硬件上的程序 2. 应用程序:只能运行在对应操作系统上的程序 二、计算机系统的层次结构 所有智能设备其实都是计算机,机顶盒、路由器、冰箱、洗衣机、汽…...
Java基础之异常
目录1 异常1.1 异常的概述1.2 常见异常类型1.3 JVM的默认处理方案1.4 编译时异常的处理方式1.4.1 异常处理之 try ... catch ... [ktʃ](捕获异常)1.4.2 异常处理之 throws(抛出异常)1.5 Throwable 的成员方法1.6 编译时异常和运行…...
感慨:大三了,未来该何去何从呢
笔者曾在十一月份通过了字节跳动的三次面试, 但是最终因为疫情原因不能满足公司的入职时间要求, 没有拿到offer。近期也是投递了大量大厂的实习岗, 但是要么已读不回, 要么明确告诉我学历至少要985硕士(天天被阿里cpu)。 说实话一…...
分账系统逻辑
一、说明 主体与业务关系方进行相关利益和支出的分配过程 使用场景: 在分销业务中,主营商户收到用户购买分销商品所支付的款项后,可以通过分账逻辑,与分销商进行佣金结算。在零售、餐饮等行业中,当销售人员完零售等…...
SpringCloud篇——什么是SpringCloud、有什么优缺点、学习顺序是什么
文章目录一、首先看官方解释二、Spring Cloud 的项目的位置三、Spring Cloud的子项目四、Spring Cloud 现状五、spring cloud 优缺点六、Spring Cloud 和 Dubbo 对比七、Spring Cloud 学习路线一、首先看官方解释 Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式…...
TCP核心机制之连接管理详解(三次握手,四次挥手)
目录 前言: 建立连接 建立连接主要两个TCP状态: 断开连接 断开连接的两个重要状态 小结: 前言: TCP是如何建立对端连接,如何断开连接,这篇文章会详细介绍。 建立连接 首先明确连接的概念:…...
前端—环境配置
前端开发建议用 Google Chrome 浏览器 vscode https://code.visualstudio.com 1、open in browser 插件:可以在 vscode 中直接运行查看浏览器效果 2、Live Server 插件:可以使代码修改浏览器页面实时刷新。 用户代码片段 … JavaScript 与 TypeScri…...
大学生常用python变量和简单的数据类型、可迭代对象、for循环的3用法
文章目录变量和简单的数据类型下划线开头的对象删除内存中的对象列表与元组debug三酷猫钓鱼记录实际POS机小条打印使用循环找乌龟可迭代对象📗理解一📘理解二2️⃣什么是迭代器✔️注意3️⃣迭代器对象4️⃣有关迭代的函数for循环的3用法🌸I …...
Java集合:Map的使用
1.Map框架l----Map:双列数据,存储key-value对的数据 ---类似于高中的函数: y f(x)|----HashMap:作为Map的主要实现类, 线程不安全的,效率高;可以存储null的key和value|----LinkedHashMap:保证在遍历map元素时,可以按照…...
【Datawhale图机器学习】第一章图机器学习导论
图机器学习导论 学习路径与必读论文清单 斯坦福CS224W(子豪兄中文精讲)知识图谱实战DeepwalkNode2vecPageRankGNNGCNGragh-SAGEGINGATTrans-ETrans-R 图无处不在 图是描述关联数据的通用语言 举例 计算机网络新冠肺炎流行病学调查传播链食物链地铁图…...
window 配置深度学习环境GPU
CUDA 11.6 CUDNN Anaconda pytorch 参考网址:https://zhuanlan.zhihu.com/p/460806048 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 (aliyun.com) 电脑信息 RTX 2060 GPU0 1. CUDA 11.6 1.1 确认信息 C:\Users\thzn>nvidia-smi (CUDA Versi…...
VS Code 用作嵌入式开发编辑器
使用 Keil MDK 进行嵌入式开发时,Keil 的编辑器相对于主流编辑器而言有些不方便,比如缺少暗色主题、缺少智能悬停感知(鼠标停在一个宏上,能自动展开最终的宏结果)、代码补全不好用等等,所以推荐使用 VS Cod…...
【Python】网络爬虫经验之谈
爬虫经验之谈对爬虫的认识网站分析技术选型JS逆向反爬机制结语近段时间,因为工作需要做一些爬虫的开发,分享一下走过的坑和实战的经验吧!对爬虫的认识 F12查看的网络请求,找到相应的接口查看一下json数据来源和构造。我爬取的网站…...
数学建模美赛【LaTeX】公式、表格、图片
数学建模美赛【LaTeX】公式、表格、图片 1 宏包 \package{ } 就是在调用宏包,对计算机实在外行的同学姑且可以理解为工具箱。 每一个宏包里都定义了一些专门的命令,通过这些命令可以实现对于一类对象(如数学公式等)的统一排版&a…...
【大数据】YARN节点标签Node Label特性
简介 YARN 的 Node-label 特性能够将不同的机器类型进行分组调度,也可以根据不同的资源要求进行分区调度。运维人员可以根据节点的特性将其分为不同的分区来满足业务多维度的使用需求。YARN的Node-label功能将很好的试用于异构集群中,可以更好地管理和调…...
C# SolidWorks二次开发 API-命令标签页的切换与按钮错乱问题
这是一个网友咨询的问题,说他想控制默认打开文件之后solidworks上方工具栏的当前激活标签页。 之前我们提到过,制作Solidworks的插件也会在上面增加一个标签页,用来放自己开发的命令,经常开发的人肯定会遇到有时候更新版本,或者标…...
ElasticSearch 7.6.1
疑问 ES为什么这么快? 全文检索 听过一个程序扫描文本的每一个单词,针对单词建立索引,并保存该单词在文本中的位置,以及出现的次数。在检索查询时候,通过建立好的索引进行查询,将索引中单词对应的文本位…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
Axure Rp 11 安装、汉化、授权
Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接:https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...
