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

GCC 同名符号冲突解决办法

一、绪论

作为 C/C++ 的开发者,大多数都会清楚课本上动态库以及静态库的优缺点,在教科书上谈及到动态库的一个优点是可以节约磁盘和内存的空间,多个可执行程序通过动态库加载的方式共用一段代码段 ;而时至今日,再看看上面这一句,更多则是调侃,说的都对,就是很难实施;可能连 GNU 当初都没有意识到,后来动态库一个的用途竟然是处理同名符号冲突的问题(只有动态库才能处理同名符号问题,静态库是不行的)。

本文着重讲解的内容可以分为:

  • 同名符号冲突的两种形式
  • 同名符号的解决策略

在这个过程中,也会讲述到排查同名符号冲突的有效手段,以及与解决与同名符号相关 GNU 链接选项的作用

为了更为直观而且具有可复现操作性,本文还专门构建了一个用于演示的 Demo,本文的所有结论均以理论和实践的形式给出,为此也会花一点时间去讲解整一个 Demo 示例的组成(Demo项目为一个未提交的 git 仓库,每一条 commit 均有其对应的价值,项目可直接编译)。

二、同名符号的两种形式

在讲解同名符号之前,我觉得首先得对符号有一个清楚的认知,即符号是什么,谁会使用到符号?

符号(symbol)一词来自于GNU的ELF(Executable and Linking Format),而 ELF 文件大致上可以分为动静态库可执行程序两类,通过链接动静态库完成可执行程序的构建,所以符号是针对于链接而言的,符号冲突跟编译一毛钱关系都没有,符号冲突只跟链接有关系

1. 示例项目1

在这里插入图片描述
下面这个部分如果不敢兴趣可以跳过,这里讲解的是如果在一个 cmake 工程中实现这种比较 “鬼畜” 的操作:
整一个的项目目录如下图所示:

├── cmake
│   ├── strong_symbol.cmake
│   └── weak_symbol.cmake
├── CMakeLists.txt
├── lib
│   ├── lib_strong_shared_symbol.so
│   ├── lib_strong_static_symbol.a
│   ├── libstrong_symbol.a
│   ├── libstrong_symbol.so
│   ├── lib_weak_shared_symbol.so
│   ├── lib_weak_static_symbol.a
│   ├── libweak_symbol.a
│   └── libweak_symbol.so
├── main.cpp
└── src├── strong_symbol.cpp├── symbol.h└── weak_symbol.cpp

其中 strong_symbol.cmake 生成了 libstrong_symbol.aweak_symbol.cmake 则生成了 libweak_symbol,a, 而 CmakeLists.txt 则在生成 symbol 可执行程序时同时链接到了 libstrong_symbol.alibweak_symbol.a

要想在一个 cmake 工程时实现这种 “鬼畜” 的做法,需要注意的就是控制每一个环节的 *.o 文件,不妨通过下面三个文件的实现自己感受一下:

(1)strong_symbol.cmake

set(SRCS ${CMAKE_SOURCE_DIR}/src/symbol.h${CMAKE_SOURCE_DIR}/src/strong_symbol.cpp)SET(CMAKE_C_FLAGS "-fPIC")
SET(CMAKE_CXX_FLAGS "-fPIC")ADD_LIBRARY(${STRONG_SYMBOL_LIB} SHARED ${SRCS})

(2)weak_symbol.cmake

set(SRCS ${CMAKE_SOURCE_DIR}/src/symbol.h${CMAKE_SOURCE_DIR}/src/weak_symbol.cpp)SET(CMAKE_C_FLAGS "-fPIC ")
SET(CMAKE_CXX_FLAGS "-fPIC ")ADD_LIBRARY(${WEAK_SYMBOL_LIB} SHARED ${SRCS})

(3)CMakeLists.txt

cmake_minimum_required (VERSION 3.5)project(symbol)set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)# 动态库名称
set(WEAK_SYMBOL_LIB "weak_symbol")
set(STRONG_SYMBOL_LIB "strong_symbol")include(${CMAKE_SOURCE_DIR}/cmake/weak_symbol.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/strong_symbol.cmake)link_directories(${CMAKE_SOURCE_DIR}/lib)set(SRCS ${CMAKE_SOURCE_DIR}/src/symbol.h${CMAKE_SOURCE_DIR}/main.cpp)
add_executable(${PROJECT_NAME} ${SRCS})
target_link_libraries(${PROJECT_NAME} ${WEAK_SYMBOL_LIB} ${STRONG_SYMBOL_LIB})

(4)symbol符号(函数)的两种实现

// strong_symbol.cpp
#include "symbol.h"#include <iostream>void symbol()
{std::cout << "strong symbol" << std::endl;
}// weak_symbol.cpp
#include "symbol.h"#include <iostream>void symbol()
{std::cout << "weak symbol" << std::endl;
}

还是挺有意思的一种实现,可以了解一下,这种技术在完成C++单元测试Mock测试时也会被广泛使用到。

2. 符号冲突类型

GNU 的符号冲突可以分类两类,一类是显式符号冲突,另一类则是隐式符号冲突。

(1)显式符号冲突

显式符号冲突会在编译阶段直接报错,而隐式符号冲突则在编译阶段不会有任何问题;隐式符号冲突会导致程序运行时跳转至错误的符号,进而执行错误的代码段,造成不可预知的后果。

以上文演示的 示例项目1 为例,在编译时通过 gcc 传递 -Wl,-–verbose 给 ld,可以看到链接的详细过程,可以看到以下内容:

试图打开 CMakeFiles/symbol.dir/main.cpp.o 成功
CMakeFiles/symbol.dir/main.cpp.o
试图打开 ../lib/libweak_symbol.a 成功
(../lib/libweak_symbol.a)weak_symbol.cpp.o
试图打开 ../lib/libstrong_symbol.a 成功
// 这里是空着的,请注意
试图打开 /home/haorui/haorui/symbol/lib/libstdc++.so 失败
试图打开 /home/haorui/haorui/symbol/lib/libstdc++.a 失败

仔细观察可以看到链接器在打开 libweak_symbol.a 从其中读取了 weak_symbol.cpp.o,而 libstrong_symbol.a 仅仅只是被打开了,并没有读取任何 .o 文件;这是因为在链接过程中,存在符号抢占的问题,即链接器对于默认的符号加载会优先保留第一个加载的符号,忽略后续的同名符号并且持续服用第一个加载的同名符号,所以最后可执行程序的输出结果为 weak symbol .

GNU 的 ld 还提供了一个指令能够让我们实现强制归档,该指令能够强制导入静态库的符号,无论需要与否,可以简单的将 CMakeLists.txt 进行下面的修改:

// from
target_link_libraries(${PROJECT_NAME}  ${WEAK_SYMBOL_LIB} ${STRONG_SYMBOL_LIB} )
// to
target_link_libraries(${PROJECT_NAME} -Wl,--whole-archive ${WEAK_SYMBOL_LIB} ${STRONG_SYMBOL_LIB} -Wl,--no-whole-archive)

再一次观看 ld 的链接过程,可以发现链接报错,出现显式符号冲突的现象,两个静态库所包含的 .o 文件都被包含进来了:

试图打开 CMakeFiles/symbol.dir/main.cpp.o 成功
CMakeFiles/symbol.dir/main.cpp.o
试图打开 ../lib/libweak_symbol.a 成功
(../lib/libweak_symbol.a)weak_symbol.cpp.o
试图打开 ../lib/libstrong_symbol.a 成功
// 注意这个
(../lib/libstrong_symbol.a)strong_symbol.cpp.o
试图打开 /home/haorui/haorui/symbol/lib/libstdc++.so 失败
// ...
// 显式符号冲突
/usr/bin/ld: ../lib/libstrong_symbol.a(strong_symbol.cpp.o): in function `symbol()':
strong_symbol.cpp:(.text+0x0): multiple definition of `symbol()'; ../lib/libweak_symbol.a(weak_symbol.cpp.o):weak_symbol.cpp:(.text+0x0): first defined here

三、同名符号的解决策略

同名符号的解决思路有且只有两种方式,它们的原理是不相同的:

  • 利用 ELF 中的符号的强弱特性
  • 利用动态库运行时加载的特性,提供重复加载的机制

值得一提的是,无论是何种解决方式,最终的解决方式一定都是以动态库的形式给出(静态库不能解决符号冲突的原因稍后也会揭示;

1. 示例项目 2

为了能够演示这种复杂的案例,需要将 示例项目1 进行一些改动,演化为 示例项目2,它的项目视图如下:
在这里插入图片描述
项目的解释以及构建可以参考示例项目1,这里就不在赘述。

2. 利用 ELF 中的符号的强弱特性

在解释如何利用 ELF 符号的强弱特性来解决同名符号冲突之前,首先还是得回到强弱符号是针对于谁而言的,以及强弱的定义。

ELF 的强弱符号与GCC 动态库符号导出技术息息相关,在本文最前面曾经提到过符号是针对于ld 链接而言的,故强弱符号肯定也是在这上面做文章;我们知道动态库有一个特性就是运行时加载,换句话说就是可执行程序中并不包含动态库内的代码段,链接器要的只是动态库的入口符号,即直接可执行程序直接调用的接口;我们知道函数的一个特性就是封装、嵌套调用,动态库对可执行程序直接暴露的接口即为入口符号,而这些实现这些入口符号所调用符号则对链接器而言是无用,因为可执行程序中既不需要调用这些符号,也不需要这些符号的实现,这些符号是可以设置为弱符号的。

故我们可以简单的将强符号理解为对链接器可见的符号,弱符号则理解为对链接器不可见的符号;另一个方式理解则是动态库符号导出的符号即为强符号,反之则为弱符号。

如何查看动/静态库的某一符号的强弱

在 GNU 平台上动态库和静态库都是 ELF 格式的,符号的相关描述查看 ELF 即可知道,linux 下面查看的工具大致上有两个 readelfobjdump,下文以 objdump

作为演示,演示的案例使用 示例项目 2 :

_symbol 符号为 global

# objdump -x libstrong_symbol1.so
# 段内偏移 | 符号作用域 | 符号类型 | 符号所在段 | 符号对应的对象占据的内存空间大小 | 符号名
# 符号作用域 : g -> global , l -> local
# Hint : _Z13strong_symbolv 、_Z7_symbolv
0000000000001145 g     F .text  000000000000000c              _Z13strong_symbolv
0000000000000000       F *UND*  0000000000000000              __cxa_atexit@@GLIBC_2.2.5
00000000000011af g     F .text  0000000000000032              _Z7_symbolv
0000000000000000       F *UND*  0000000000000000              _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4
0000000000000000       F *UND*  0000000000000000              _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
0000000000000000       O *UND*  0000000000000000              _ZSt4cout@@GLIBCXX_3.4
0000000000000000       F *UND*  0000000000000000              _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
0000000000000000  w      *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000  w      *UND*  0000000000000000              __gmon_start__
0000000000000000  w      *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000       F *UND*  0000000000000000              _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4

_symbol 符号为 local

# objdump -x libstrong_symbol2.so
# 段内偏移 | 符号作用域 | 符号类型 | 符号所在段 | 符号对应的对象占据的内存空间大小 | 符号名
# 符号作用域 : g -> global , l -> local
# Hint : _Z13strong_symbolv 、_Z7_symbolv
00000000000011af l     F .text  000000000000004d              _Z7_symbolv
0000000000001000 l     F .init  0000000000000000              _init
0000000000003da8 l     O .dynamic       0000000000000000              _DYNAMIC
0000000000004048 l     O .data  0000000000000000              __TMC_END__
0000000000004000 l     O .got.plt       0000000000000000              _GLOBAL_OFFSET_TABLE_
0000000000000000       F *UND*  0000000000000000              printf@@GLIBC_2.2.5
0000000000000000  w    F *UND*  0000000000000000              __cxa_finalize@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
0000000000001145 g     F .text  000000000000000c              _Z13strong_symbolv
0000000000000000       F *UND*  0000000000000000              __cxa_atexit@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4
0000000000000000       F *UND*  0000000000000000              _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
0000000000000000       O *UND*  0000000000000000              _ZSt4cout@@GLIBCXX_3.4
0000000000000000       F *UND*  0000000000000000              _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
0000000000000000  w      *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000  w      *UND*  0000000000000000              __gmon_start__
0000000000000000  w      *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000       F *UND*  0000000000000000              _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4

总结

先说明一下背景,在 libstrong_symbolx.so 中的 _symbol 即为同名函数 (与 libweak_symbol.so 中的 _symbol 冲突),strong_symbol 和 weak_symbol 同时都调用了 _symbol 符号,

但是它们各自 _symbol 的实现是不同的。
主程序代码如下:

int main()
{strong_symbol();weak_symbol();return 0;
}

在 libstrong_symbol1.so 的情况下输出:

strong symbol address is 0x7f5b582a01af
strong symbol
strong symbol address is 0x7f5b582a01af
strong symbol

在 libstrong_symbol2.so 的情况下输出:

strong symbol address is 0x7fc4904a01af
strong symbol
weak symbol address is 0x7fc49049b1af
weak symbol

(2)符号隐藏的两种方式

-fvisibility=hidden
这个选项是传递给编译器的,添加这个选项之后,动态库导出符号的默认行为都为本地符号,即弱符号;在程序中针对需要导出的符号需要显式添加 __attribute__((visibility("default")))关键字。

这里有一点需要注意的地方是 -fvisibility=hidden 的适用范围是 *.o 文件,即 *.cpp , 动态库链接静态库时也会将静态库中的符号导入动态库,这些符号的可见性是不受 -fvisibility=hidden 的影响的。

-Wl,–exclude-libs=ALL
这个选项是传递给链接器的,并且具有明确的使用场景,生成动态库导入静态库符号时将静态库的符号的可见性全部设置为不可见(这句话有点绕,但是需要好好理解)。
在这里插入图片描述
以上图为例,lib_weak_symbol.a 存在 symbol ,符号可见性为显性; 在生成 lib_weak_symbol.so 时,需要将 lib_weak_symbol.a 的代码段拉进 libweak_symbol.so , 在这个时候 lib_weak_symbol.a

中的 _symbol 符号也会被导入,默认是强符号;添加 -Wl,–exclude-libs=ALL 即可把 _symbol 符号的可见性设置为不可见,即弱符号;从这个案例中可以看出,-Wl,–exclude-libs=ALL 的使用场景是使用

静态库去生成动态库的过程中生效的,例如使用静态库生成静态库,那么这种情况下就不适用了。

静态库为何处理不了同名符号

其实回答这个问题很简单,对于静态库而言,没有所谓的强弱符号的说法,因为静态库的所谓代码段都会被导入到可执行程序,真要说的话那么静态库的符号只有强符号一种(也可以从另外一种角度去理解,

静态库的本质就是 *.o 文件的归档,再退一步就是 *.cpp );因为静态库的全部符号(直接使用到的或者间接使用到的)均会导入到可执行程序,同一个代码段是不允许出现同名符号的。

动态库通过一些针对性的操作之所以能够实现同名函数的兼容,实际上是通过符号隐藏,让两个同名函数放置在不同的动态库内,本质上是没有违背同一代码段不能出现同名符号的规则的。

3. 利用动态库运行时加载的特性,提供重复加载的机制

这个机制非常暴力,利用的是链接器的一个可选配置 -Wl,-Bsymbolic

正常情况下,在linux平台上(不使用-Bsymbolic),加载的目标文件中第一次出现的符号将在程序中一直被使用,不论是定义在静态可执行部分,还是在动态目标文件中

这是通过**符号抢占(symbol preemption)**来实现的。动态加载器构建符号表,所有的动态符号根据该符号表被决议。所以正常情况下,如果一个符号实例出现在动态库(DSO)中,但是已经在静态可执行文件或者之前加载的动态库中被定义,那么以前的定义也将被用于当前的动态库中。

-Bsymbolic 通过关闭DOS中的符号抢占来改变这种行为,也就是关闭了上述这种机制,使用 Bsymbolic 之后,无论是已经加载过还是没有加载过,目标符号都会被重复

加载,也就是 -Bsymbolic 是通过将共享行为变更为拷贝行为来实现同名符号兼容。

正如本文最初的调侃,动态库的一个优点是可以节约磁盘和内存的空间,多个可执行程序通过动态库加载的方式共用一段代码段,在当下为了同一个库的不同版本问题已经慢慢失去了其共享的价值了。

相关文章:

GCC 同名符号冲突解决办法

一、绪论 作为 C/C 的开发者&#xff0c;大多数都会清楚课本上动态库以及静态库的优缺点&#xff0c;在教科书上谈及到动态库的一个优点是可以节约磁盘和内存的空间&#xff0c;多个可执行程序通过动态库加载的方式共用一段代码段 &#xff1b;而时至今日&#xff0c;再看看上…...

下一代视频编码技术2023

下一代视频编码技术 下面将从这两个角度来介绍华为云视频在下一代视频编码技术上的一些工作。这些技术得益于华为2012 媒体技术院全力支持。 2.1 下一代视频编码标准技术 从上图可以看出&#xff0c;下一代的视频编码标准大概分为三个阵营或者三个类型&#xff1a; 国际标准…...

最新最全中小微企业研究数据:海量创业公司信息与获取投资信息(1985-2021年)

一、企业获取投资名单&资方信息 数据来源&#xff1a;搜企网、企查查、天眼查 时间跨度&#xff1a;1985年8月-2021年9月 区域范围&#xff1a;全国范围 数据字段&#xff1a;企业名称、时间、获得投资金额以及投资方信息 部分数据&#xff1a; DateCompany_nameUnit…...

springboot数据源浅析

DataSourceAutoConfiguration分析 SpringBoot有一个自动配置DataSourceAutoConfiguration 为数据源配置 /META-INF/spring.factories文件找到DataSourceAutoConfiguration配置类 一、先来看下DataSourceAutoConfiguration配置类生效的时机&#xff0c;观察源码发现 Configura…...

2022黑马Redis跟学笔记.实战篇(七)

2022黑马Redis跟学笔记.实战篇 七4.11.附近的店铺功能4.11.1. GEO数据结构的基本用法1. 附近商户-导入店铺数据到GEO4.11.2. 获取附近的店铺1. 附近商户-实现附近商户功能4.9. 签到功能4.9.1.BitMap原理1. 用户签到-BitMap功能演示4.9.2.实现签到功能4.9.3.实现补签功能4.9.4.统…...

QT mp3音乐播放器实现框架,Qt鼠标事件,网络编程,QSqlite,Json解析,HTTP请求等

QT mp3音乐播放器实现框架&#xff0c;Qt鼠标事件&#xff0c;网络编程&#xff0c;QSqlite,Json解析&#xff0c;HTTP请求等框架搭建UI设计mp3.hmp3.cpp隐藏窗口标题 最大化 最小化 关闭框架搭建 .pro添加 # 网络 添加多媒体 数据库 QT network multimedia sql添加头…...

硬件学习 软件Cadence day04 PCB 封装绘制

1.文章内容&#xff1a; 1. 贴片式电容 PCB 封装绘制 &#xff08;型号 c0603 &#xff09; 2. 贴片式电阻 PCB 封装绘制 &#xff08;型号 r0603 &#xff09; 3. 安规式电容 PCB 封装绘制 &#xff08;这个就是 有一个电容&#xff0c;插入一个搞好的孔里面 …...

【Java】yield()和join()区别

一、java 线程调度的背景 java虚拟机要求在多线程中实现 preemptive和priority-based调度&#xff0c;这意味着java中每一个线程被分配了特定的优先级&#xff0c;正整数在定义好的范围内不断减。优先级可以通过开发者改变但是java虚拟机从不改变线程的优先级&#xff0c;即使…...

【MySQL】Java连接MySQL数据库(封装版只需会MySQL)

一、准备普通项目如果创建的是普通的Java项目&#xff0c;我们需要去maven仓库下载jdbc驱动包然导入项目中就能使用&#xff0c;具体步骤详见MySQL数据库之Java中如何使用数据库【JDBC编程】maven项目如果创建的项目是maven项目&#xff0c;我们只需在pom.xml文件里引入一组依赖…...

【java基础】运算符

运算符 operator 运算符优先级 Operators 操作员Precedence 优先级postfix 后缀expr expr--unary 一元的expr --expr expr -expr ~ !multiplicative 〔数〕乘法的 / %additive 添加剂 -shift 移动<< >> >>>relational 关系的< > < > insta…...

带噪学习-概述

在实际应用的时候&#xff0c;我们的样本不会是完全干净的&#xff0c;即存在噪声样本。那使用存在噪声的样本时&#xff0c;我们如何更有效的进行模型学习呢&#xff1f;Label Dependent Nose样本选择&#xff08;Sample Selection&#xff09;第一种很直接的想法&#xff0c;…...

Scratch少儿编程案例-多彩打地鼠

专栏分享 点击跳转=>Unity3D特效百例点击跳转=>案例项目实战源码点击跳转=>游戏脚本-辅助自动化点击跳转=>Android控件全解手册点击跳转=>Scratch编程案例👉关于作者...

为什么拔掉计算机网线还能ping通127.0.0.1?

前言 当我们在计算机上拔掉网线之后&#xff0c;发现我们仍然可以使用ping命令来ping通本机的IP地址127.0.0.1&#xff0c;这让很多人感到困惑&#xff0c;认为拔掉网线后计算机就无法与外界通信了&#xff0c;为什么还能ping通本机的IP地址呢&#xff1f; 本文的目的是通过对…...

Android kotlin 内、外部存储根目录及测试(可以实现仿微信未读消息数提示数字)

<<返回总目录 文章目录 一、内部存储与外部存储三、外部存储的写读测试(可以实现仿微信未读消息数提示数字)一、内部存储与外部存储 所有Android设备都有两个文件存储区域:内部存储空间(internal Storage)和外部存储空间(external Storage)。所以,Android系统从逻…...

Android 7.0 OTA升级(高通)

文章目录1. Full OTA 方式升级介绍1.1 Full OTA 制作第一步&#xff1a;生成 msm89xx-target_files-eng.XXX.zip1.2 Full OTA 制作第二步&#xff1a;Modem 等非 HLOS 加入升级包的方法1.3 Full OTA 制作第三步&#xff1a;生成 update.zip 升级包2. Incremental OTA 方式升级介…...

工作负载之DeployMent

DeployMent 无状态工作负载&#xff08;Deployment&#xff09;&#xff1a;即kubernetes中的“Deployment”&#xff0c;无状态工作负载支持弹性伸缩与滚动升级&#xff0c;适用于实例完全独立、功能相同的场景&#xff0c;如&#xff1a;nginx、wordpress等。 也是公司中应…...

淘宝tmall页面数据获取,API接口对接程序

item_get-获得淘宝商品详情请求参数请求参数&#xff1a;num_iid652874751412&is_promotion1参数说明&#xff1a;num_iid:淘宝商品IDis_promotion:是否获取取促销价响应参数Version: Date:2022-04-04名称类型必须示例值描述itemitem[]1宝贝详情数据num_iidBigint152081325…...

基于粒子群优化算法的电动汽车充放电V2G研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

java并发编程原理2 (AQS, ReentrantLock,线程池)

一、AQS&#xff1a; 1.1 AQS是什么&#xff1f; AQS就是一个抽象队列同步器&#xff0c;abstract queued sychronizer&#xff0c;本质就是一个抽象类。 AQS中有一个核心属性state&#xff0c;其次还有一个双向链表以及一个单项链表。 首先state是基于volatile修饰&#x…...

研报精选230219

目录 【行业230219山西证券】煤炭行业周报&#xff1a;复工改善&#xff0c;港口价格企稳反弹【行业230219中航证券】农林牧渔行业周观点&#xff1a;一号文件落地&#xff0c;生物育种超势不改【行业230219华西证券】汽车行业周报&#xff1a;新车密集上市 自主转型提速【个股…...

【PPPoE】PPPoE拨号流程

简介 PPPoE&#xff08;Point-to-Point Protocol over Ethernet&#xff09;是一种在以太网上封装PPP协议的方式&#xff0c;常用于在宽带接入中进行拨号。 PPPoE的拨号原理如下&#xff1a; 客户端发起PPPoE Active Discovery Initiation (PADI)报文&#xff0c;广播到网络…...

django项目实战(django+bootstrap实现增删改查)

目录 一、创建django项目 二、修改默认配置 三、配置数据库连接 四、创建表结构 五、在app当中创建静态文件 六、页面实战-部门管理 1、实现一个部门列表页面 2、实现新增部门页面 3、实现删除部门 4、实现部门编辑功能 七、模版的继承 1、创建模板layout.html 1&…...

Lesson4---Python语言基础(2)

4.1 内置数据结构 4.1.1 序列数据结构&#xff08;sequence&#xff09; 成员是有序排列的每个元素的位置称为下标或索引通过索引访问序列中的成员Python中的序列数据类型有字符串、列表、元组 “abc” ≠ “bac” 4.1.1.1 创建列表和元组 Python中的列表和元组&#xff0c…...

NCHW - NHWC - CHWN 排列

TensorFlow有两种数据格式NHWC和NCHW,默认的数据格式是NHWC,可以通过参数data_format指定数据格式。这个参数规定了 input Tensor 和 output Tensor 的排列方式。 1、data_format 设置为 “NHWC” 时,排列顺序为 [batch, height, width, channels] 设置为 “NCHW” 时,排…...

2019蓝桥杯真题矩阵切割(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小明有一些矩形的材料&#xff0c;他要从这些矩形材料中切割出一些正方形。 当他面对一块矩形材料时&#xff0c;他总是从中间切割一刀&#xff0c;切出一块最大的正…...

Java线程池的创建以及原理

一、为什么要使用线程池 在外面的日常开发中&#xff0c;也使用了不少池化技术&#xff0c;比如线程池、数据库连接池、HTTP连接池等等都是对这个思想的应用。 池化技术的思想主要是为了减少每次获取资源的消耗&#xff0c;提高对资源的利用率。 线程池提供了一种限制和管理资…...

Java集合学习之Map

1.什么是Map Java里的Map接口是一个集合根接口&#xff0c;表示一个 键值对&#xff08;Key-Value&#xff09; 的映射。 简单来说就是键和值是一对的&#xff0c;每一个 Key都有唯一确定的 Value对应。 其中要求 键&#xff08;Key&#xff09; 唯一&#xff0c;因为是按照…...

java 基于maven多模块合并打包部署

项目环境 jdk 1.8spring 2.7.xmaven 3.6 项目结构 模块功能 client – 对外service common – 共用工具切面等 main – 启动类 goods – 子模块具体实现 模块间依赖关系 client – 无依赖 common – 无依赖 main – client、common、goods goods – client、common 具体P…...

Kubernetes是个什么东东?

Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态&#xff0c;其服务、支持和工具的使用范围相当广泛。 Kubernetes 这个名字源于希腊语&#xff0c;意…...

【go语言grpc之client端源码分析三】

go语言grpc之server端源码分析三newClientStreamnewAttemptLocked上一篇在介绍了grpc.Dial之后&#xff0c;然后再介绍一下后面的 //创建RPC客户端client : pb.NewGreetsClient(conn)//设置超时时间_, cancel : context.WithTimeout(context.Background(), time.Second)defer c…...

非遗网页设计作品欣赏/重庆网站seo公司

在IIS中可以通过IP地址域名设置来控制拒绝或允许特定范围内的IP对网站的访问权限&#xff0c;下面简单介绍如何在IIS7.5中设置&#xff0c;如下图&#xff0c;是IIS7.5的主界面 一、安装“IP地址和域限制”功能 选定一个网站&#xff0c;在右边的功能图中有一项“IP地址和域限制…...

网络推广怎么推广/百度搜索引擎优化公司哪家强

一、简介 HashTable是线程安全的HashMap,两个实现原理都是一样的&#xff0c;只是HashTable集合的所有方法都是synchronized方法&#xff0c;而ConcurrentHashMap就不一样了&#xff0c;他最底层的存储使用的也是和HashMap一样的但是&#xff0c;在线程安全处理上有很大区别&a…...

asp企业网站模板/常见的网络营销方式有哪几种

浏览器地址栏输入URL到显示页面发生了什么&#xff1f; 1.用户输入 2.卸载原页面并重定向到新页面 3.处理Service Worker 4.网络请求 5.服务端响应 6.浏览器渲染详细流程 这个过程分为两个部分&#xff1a;网络通信页面渲染 步骤1&#xff1a;DNS 域名解析&#xff08;域名解析…...

yanderedev.wordpress/地推接单正规平台

这里是水果店早读课&#xff0c;水果店主同行交流圈子&#xff0c;持续分享&#xff0c;帮助新手入门。 水果店的水果去定价是比较复杂的事情&#xff0c;根据不同的情况去定价是常有的事情&#xff0c;有哪些情况需要去区别定价呢。 1、不同时期定价策略不同 同质量水果时&…...

武汉汉阳建设局官方网站/温州seo排名公司

原文&#xff1a;http://coolketang.com/staticPhotoshop/5a98d43c17d009003595ee49.html 1. 本节课程将为您演示&#xff0c;[曝光度]命令的使用。依次点击[图像 > 调整 > 曝光度]命令&#xff0c;弹出[曝光度]窗口。 2. 3. [曝光度]是用来控制图片的色调强弱的工具。跟…...

教育校园网站建设方案/推广效果最好的平台

做软件到一定层次了&#xff0c;就要考虑到设计了&#xff0c;设计了很久&#xff0c;就是不系统&#xff0c;系统的设计需要一个记录&#xff0c;记录就用文档&#xff0c;那么对项目所有包括技术上的设计都记录下来&#xff0c;我们就可以理解为软件的概要设计了。 在需求明确…...