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

ARM交叉编译入门及交叉编译第三方库常见问题解析

1. 交叉编译是什么?

交叉编译简单说来,就是编译成果物的地儿不是你运行这个成果物的地儿。最常见的场景,就是我们要编译一个 ARM版本 的可执行程序,但我们编译这个 ARM版本 可执行程序的地方,是在一个 x86_x64 的平台上。

2. 为什么需要交叉编译?

绝大部分的原因,是目标平台不具备编译成果物的算力。具体说来,就是ARM平台早期是 并没有 编译代码所需的 算力相关空间 的。所以,不得不借助性能更高的平台来辅助进行编译成果物,然后ARM平台仅负责运行成果物即可。

3. 交叉编译只能在目标平台同一系统上吗?

虽然绝大多数的 ARM Linux系统中编译的成果物是在对应的 x86_x64平台的Linux系统中进行的,所以大多数时候使用Windows平台电脑需要安装一个虚拟机或者连接到某个x86_x64平台的Linux编译服务器中,但实际上这种搭配大多数情况是为了方便,也为了让编译者熟悉Linux环境,但这种搭配并不是唯一的解决方案。

ARM官网 上就能直接下载各种平台( LinuxWindows )的编译工具链,另外一个很常见的第三方工具链制作商,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生成的环境中还需要使用ARNMRANLIB等等,这些东西也需要使用对应的交叉编译工具链版本的程序。而上述的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中对应宏定义进行显式修改,包括但不限于CCARNMRANLIBCXX等等。

4.2.2 有些库压根就没有configure,需要用cmake

有些开源库就没给configure,但是能看到cmakelist的文件,那么这个时候,把cmakelist当做configure就好,但是它的指定写法会稍显不同。一般需要提供一个对应的cmake配置,对于我们交叉编译来说,需要指定对应的host系统名称CMAKE_SYSTEM_NAME和处理器名称CMAKE_SYSTEM_PROCESSOR,然后需要指定对应的编译链路径CMAKE_C_COMPILERCMAKE_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文件,接着makemake install就好了。

5. 总结

  • 绝大多数场景,交叉编译的时候configuremakemake install三部曲就好,与普通编译不一样的是需要指定host

  • 自依赖第三方库在不频繁迭代更新的条件下,建议安装到交叉编译链的sysroot目录中

  • 如果怎么编译都有点问题,建议仔细阅读configure --help或者README分析查找端倪

相关文章:

ARM交叉编译入门及交叉编译第三方库常见问题解析

1. 交叉编译是什么&#xff1f; 交叉编译简单说来&#xff0c;就是编译成果物的地儿不是你运行这个成果物的地儿。最常见的场景&#xff0c;就是我们要编译一个 ARM版本 的可执行程序&#xff0c;但我们编译这个 ARM版本 可执行程序的地方&#xff0c;是在一个 x86_x64 的平台…...

Ruby Web Service 应用 - SOAP4R

什么是 SOAP&#xff1f; 简单对象访问协议(SOAP,全写为Simple Object Access Protocol)是交换数据的一种协议规范。 SOAP 是一种简单的基于 XML 的协议&#xff0c;它使应用程序通过 HTTP 来交换信息。 简单对象访问协议是交换数据的一种协议规范&#xff0c;是一种轻量的、…...

HashMap底层实现原理概述

原文https://blog.csdn.net/fedorafrog/article/details/115478407 hashMap结构 常见问题 在理解了HashMap的整体架构的基础上&#xff0c;我们可以试着回答一下下面的几个问题&#xff0c;如果对其中的某几个问题还有疑惑&#xff0c;那就说明我们还需要深入代码&#xff0c…...

Linux驱动学习环境搭建

背景常识 一、程序分类 程序按其运行环境分为&#xff1a; 1. 裸机程序&#xff1a;直接运行在对应硬件上的程序 2. 应用程序&#xff1a;只能运行在对应操作系统上的程序 二、计算机系统的层次结构 所有智能设备其实都是计算机&#xff0c;机顶盒、路由器、冰箱、洗衣机、汽…...

Java基础之异常

目录1 异常1.1 异常的概述1.2 常见异常类型1.3 JVM的默认处理方案1.4 编译时异常的处理方式1.4.1 异常处理之 try ... catch ... [ktʃ]&#xff08;捕获异常&#xff09;1.4.2 异常处理之 throws&#xff08;抛出异常&#xff09;1.5 Throwable 的成员方法1.6 编译时异常和运行…...

感慨:大三了,未来该何去何从呢

笔者曾在十一月份通过了字节跳动的三次面试&#xff0c; 但是最终因为疫情原因不能满足公司的入职时间要求&#xff0c; 没有拿到offer。近期也是投递了大量大厂的实习岗&#xff0c; 但是要么已读不回&#xff0c; 要么明确告诉我学历至少要985硕士(天天被阿里cpu)。 说实话一…...

分账系统逻辑

一、说明 主体与业务关系方进行相关利益和支出的分配过程 使用场景&#xff1a; 在分销业务中&#xff0c;主营商户收到用户购买分销商品所支付的款项后&#xff0c;可以通过分账逻辑&#xff0c;与分销商进行佣金结算。在零售、餐饮等行业中&#xff0c;当销售人员完零售等…...

SpringCloud篇——什么是SpringCloud、有什么优缺点、学习顺序是什么

文章目录一、首先看官方解释二、Spring Cloud 的项目的位置三、Spring Cloud的子项目四、Spring Cloud 现状五、spring cloud 优缺点六、Spring Cloud 和 Dubbo 对比七、Spring Cloud 学习路线一、首先看官方解释 Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式…...

TCP核心机制之连接管理详解(三次握手,四次挥手)

目录 前言&#xff1a; 建立连接 建立连接主要两个TCP状态&#xff1a; 断开连接 断开连接的两个重要状态 小结&#xff1a; 前言&#xff1a; TCP是如何建立对端连接&#xff0c;如何断开连接&#xff0c;这篇文章会详细介绍。 建立连接 首先明确连接的概念&#xff1a…...

前端—环境配置

前端开发建议用 Google Chrome 浏览器 vscode https://code.visualstudio.com 1、open in browser 插件&#xff1a;可以在 vscode 中直接运行查看浏览器效果 2、Live Server 插件&#xff1a;可以使代码修改浏览器页面实时刷新。 用户代码片段 … JavaScript 与 TypeScri…...

大学生常用python变量和简单的数据类型、可迭代对象、for循环的3用法

文章目录变量和简单的数据类型下划线开头的对象删除内存中的对象列表与元组debug三酷猫钓鱼记录实际POS机小条打印使用循环找乌龟可迭代对象&#x1f4d7;理解一&#x1f4d8;理解二2️⃣什么是迭代器✔️注意3️⃣迭代器对象4️⃣有关迭代的函数for循环的3用法&#x1f338;I …...

Java集合:Map的使用

1.Map框架l----Map:双列数据&#xff0c;存储key-value对的数据 ---类似于高中的函数: y f(x)|----HashMap:作为Map的主要实现类&#xff0c; 线程不安全的&#xff0c;效率高&#xff1b;可以存储null的key和value|----LinkedHashMap:保证在遍历map元素时&#xff0c;可以按照…...

【Datawhale图机器学习】第一章图机器学习导论

图机器学习导论 学习路径与必读论文清单 斯坦福CS224W&#xff08;子豪兄中文精讲&#xff09;知识图谱实战DeepwalkNode2vecPageRankGNNGCNGragh-SAGEGINGATTrans-ETrans-R 图无处不在 图是描述关联数据的通用语言 举例 计算机网络新冠肺炎流行病学调查传播链食物链地铁图…...

window 配置深度学习环境GPU

CUDA 11.6 CUDNN Anaconda pytorch 参考网址&#xff1a;https://zhuanlan.zhihu.com/p/460806048 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 (aliyun.com) 电脑信息 RTX 2060 GPU0 1. CUDA 11.6 1.1 确认信息 C:\Users\thzn>nvidia-smi &#xff08;CUDA Versi…...

VS Code 用作嵌入式开发编辑器

使用 Keil MDK 进行嵌入式开发时&#xff0c;Keil 的编辑器相对于主流编辑器而言有些不方便&#xff0c;比如缺少暗色主题、缺少智能悬停感知&#xff08;鼠标停在一个宏上&#xff0c;能自动展开最终的宏结果&#xff09;、代码补全不好用等等&#xff0c;所以推荐使用 VS Cod…...

【Python】网络爬虫经验之谈

爬虫经验之谈对爬虫的认识网站分析技术选型JS逆向反爬机制结语近段时间&#xff0c;因为工作需要做一些爬虫的开发&#xff0c;分享一下走过的坑和实战的经验吧&#xff01;对爬虫的认识 F12查看的网络请求&#xff0c;找到相应的接口查看一下json数据来源和构造。我爬取的网站…...

数学建模美赛【LaTeX】公式、表格、图片

数学建模美赛【LaTeX】公式、表格、图片 1 宏包 \package{ } 就是在调用宏包&#xff0c;对计算机实在外行的同学姑且可以理解为工具箱。 每一个宏包里都定义了一些专门的命令&#xff0c;通过这些命令可以实现对于一类对象&#xff08;如数学公式等&#xff09;的统一排版&a…...

【大数据】YARN节点标签Node Label特性

简介 YARN 的 Node-label 特性能够将不同的机器类型进行分组调度&#xff0c;也可以根据不同的资源要求进行分区调度。运维人员可以根据节点的特性将其分为不同的分区来满足业务多维度的使用需求。YARN的Node-label功能将很好的试用于异构集群中&#xff0c;可以更好地管理和调…...

C# SolidWorks二次开发 API-命令标签页的切换与按钮错乱问题

这是一个网友咨询的问题&#xff0c;说他想控制默认打开文件之后solidworks上方工具栏的当前激活标签页。 之前我们提到过,制作Solidworks的插件也会在上面增加一个标签页&#xff0c;用来放自己开发的命令&#xff0c;经常开发的人肯定会遇到有时候更新版本&#xff0c;或者标…...

ElasticSearch 7.6.1

疑问 ES为什么这么快&#xff1f; 全文检索 听过一个程序扫描文本的每一个单词&#xff0c;针对单词建立索引&#xff0c;并保存该单词在文本中的位置&#xff0c;以及出现的次数。在检索查询时候&#xff0c;通过建立好的索引进行查询&#xff0c;将索引中单词对应的文本位…...

Linux系列 操作系统安装及服务控制(笔记)

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.操作系统 1.Linux系统三大类 &#xff08;1&#xff09;ubu…...

Linux基础 - NTP时间同步

‍‍&#x1f3e1;博客主页&#xff1a; Passerby_Wang的博客_CSDN博客-系统运维,云计算,Linux基础领域博主 &#x1f310;所属专栏&#xff1a;『Linux基础』 &#x1f30c;上期文章&#xff1a; Linux基础 - DNS服务进阶 &#x1f4f0;如觉得博主文章写的不错或对你有所帮助…...

golang 入门教程:迷你 Twitter 后端

请记住&#xff0c;这个项目主要是为了稍微熟悉下Golang&#xff0c;您可以复制架构&#xff0c;但该项目缺少适当的 ORM&#xff0c;没有适当的身份验证或授权&#xff0c;我完全无视中间件&#xff0c;也没有测试。 我将在其自己的部分中讨论所有这些问题&#xff0c;但重要的…...

CPP2022-30-期末模拟测试03

6-1 引用作函数形参交换两个整数 分数 5 全屏浏览题目 切换布局 作者 李廷元 单位 中国民用航空飞行学院 设计一个void类型的函数Swap&#xff0c;该函数有两个引用类型的参数&#xff0c;函数功能为实现两个整数交换的操作。 裁判测试程序样例&#xff1a; #include <…...

华为OD机试真题Python实现【最多等和不相交连续子序列】真题+解题思路+代码(20222023)

🔥系列专栏 华为OD机试(Python)真题目录汇总华为OD机试(JAVA)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出示例一输入输出说明示例二输入输出说明...

二叉搜索树

1.二叉搜索树 1.1.二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一颗空树&#xff0c;或者是具有一下性质的二叉树。 若它的左子树不为空&#xff0c;则左子树上的所有节点的值都小于根节点的值。若它的右子树不为空&#xff0c;则右子树上的所有节点的值都…...

数据结构(三):集合、字典、哈希表

数据结构&#xff08;三&#xff09;一、集合&#xff08;Set&#xff09;1.封装一个集合类2.集合常见的操作&#xff08;1&#xff09;并集&#xff08;2&#xff09;交集&#xff08;3&#xff09;差集&#xff08;4&#xff09;子集二、字典&#xff08;Map&#xff09;三、…...

Linux内核驱动开发(一)

Linux内核初探 linux操作系统历史 开发模式 git 分布式管理git clone 获取git push 提交git pull 更新 邮件组 mailing list patch 内核代码组成 Makfile arch 体系系统架构相关 block 块设备 crypto 加密算法 drivers 驱动&#xff08;85%&#xff09; atm 通信bluet…...

TCP/IP协议二十问

TCP/IP协议二十问 1. 什么是TCP网络分层&#xff1f; TCP网络分层一般分为五层&#xff1a; 应用层&#xff08;HTTP&#xff09;&#xff1a;组装数据包传输层&#xff08;TCP&#xff09;&#xff1a;增加TCP头部&#xff0c;包含端口号等信息网络互联层&#xff08;IP&am…...

常用Array数组操作方法

定义一个测试数组constplayers[{name:科比,num:24},{name:詹姆斯,num:23},{name:保罗,num:3},{name:威少,num:0},{name:杜兰特,num:35}]复制代码1、forEach参数代表含义item&#xff1a;遍历项index&#xff1a;遍历项的索引arr&#xff1a;数组本身Array.prototype.sx_forEach…...

网站建设找单/网站建设外包

sencha的前身就是Extjs了&#xff0c;sencha 框架是世界上第一个基于HTML5的Mobile App框架 那么何谓框架&#xff0c;传统软件工程对于库和框架的区分主要着眼于对应用运行流程的控制权&#xff0c;框架提供架构&#xff0c;控制运行流程&#xff0c;让开发者在合适的地方书写…...

wordpress 网站迁移/代刷网站推广链接免费

...

网站建设管理工作总结报告/软文文案案例

1.Android 5.0 删除ActionBar下面的阴影 于Android 5.0假设你发现的ActionBar下面出现了阴影&#xff0c;例如&#xff0c;下面的设置&#xff0c;以消除阴影&#xff1a; getActionBar().setElevation(0); Android 5.0之前能够用以下代码消除阴影&#xff1a; <item name&q…...

网站建设页面带声音/seo搜索优化技术

1.编译安装ipvsadm 首先从LVS官网下载tarball&#xff0c;解压后make && make install即可。 要注意的是LVS的依赖有&#xff1a;popt-static、libnl、kernel。如果yum源里有就直接安装&#xff0c;如果没有就自行下载&#xff0c;但一定要对应上已有软件如popt或Linux…...

网站开发阶段流程图/手机软文广告300字

C语言内存管理更多C/C学习资料&#xff0c;基础教程&#xff0c;请私信我“编程”&#xff0c;即可获取本文将讲解 C 中的动态内存管理。C 语言为内存的分配和管理提供了几个函数。这些函数可以在 头文件中找到。void *calloc(int num, int size);在内存中动态地分配 num 个长度…...

广州专业网站建设有哪些/百度网站提交收录入口

Web应用是通过url访问某个具体的HTML页面&#xff0c;每个url都对应一个资源。传统的Web应用中&#xff0c;浏览器通过url向服务器发送请求&#xff0c;服务器读取资源并把处理好的页面内容发送给浏览器&#xff0c;而在单页面应用中&#xff0c;所有url变化的处理都在浏览器端…...