摘要
- 搜集的关于项目构建,编译等的面试题及答案
静态链接库与动态链接库
静态链接库:
- 在Linux系统中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中.
- 存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置.存档文件名由后缀
.a
标识. - 创建静态库需要用到一个工具:
AR
动态链接库:
- 共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来,这个过程称为动态链接(dynamic linking),是由一个叫做**动态链接器(dynamic linker)**的程序来执行的
- 共享库也称为共享目标(share object),在Linux系统中通常用
.so
后缀来表示;微软的操作系统大量地使用了共享库,它们称为DLL(动态链接库)
静态链接库与动态链接库的区别
静态链接库和动态链接库是两种不同类型的库文件,它们在软件开发中扮演着不同的角色,并且具有一些显著的区别:
静态链接库(Static Linking Library):
链接方式:
- 编译时链接:静态链接库在编译时被链接到可执行文件中,将库的代码和可执行文件合并为一个单独的可执行文件。
文件格式:
- 文件大小:相对较大,因为它包含了被调用的库的完整副本。
- 扩展名:通常是
.lib
(在Windows平台)或.a
(在Unix/Linux平台)。
运行时:
- 独立性:生成的可执行文件独立运行,不需要外部依赖。
- 移植性:可能导致较大的可执行文件,并且需要在每个使用相同静态库的应用程序中重新链接和更新。
动态链接库(Dynamic Linking Library):
链接方式:
- 运行时链接:动态链接库在程序运行时被加载到内存中,并且可以被多个程序共享使用。
文件格式:
- 文件大小:相对较小,因为它包含了可共享的代码和数据,被多个程序共享使用。
- 扩展名:通常是
.dll
(在Windows平台)或.so
(在Unix/Linux平台)。
运行时:
- 依赖性:程序在运行时需要正确的动态链接库存在,否则会出现运行错误。
- 更新和维护:修改动态链接库可能影响多个程序,但是可以通过单独更新库文件来修复或升级功能,不需要重新编译整个程序。
总结区别:
- 静态链接库在编译时将代码和数据合并到可执行文件中,使得程序独立运行,但增加了可执行文件的大小和每个应用的维护负担。
- 动态链接库在运行时被加载到内存中,多个程序可以共享它,减少了磁盘空间和内存的占用,但程序需要确保相应的库在运行时存在。
在实际开发中,通常会根据具体需求和考虑因素来选择使用静态链接库或动态链接库。
编译和链接
- 预编译(预处理) : 预编译过程主要处理哪些源代码文件中以 “#” 开始的预编译指令,例如 #include, #define等,生成.i 或者.ii文件
- 编译:把预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生产相应的汇编代码文件(.s文件)
- 汇编:将汇编代码转变机器可以执行的指令(机器码),生成.o文件
- 链接:链接器进行地址和空间分配,符号决议,重定位等步骤,生成.out文件
程序的内存布局
- 一般来讲,应用程序使用的内存空间里有如下默认区域:
- 栈:栈用于维护函数调用的上下文。由操作系统自动分配释放,一般包含一下几个方面
- 函数的返回地址和参数
- 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量
- 保存上下文:包括函数调用前后需要保持不变的寄存器
- 堆:堆是用来容纳应用程序动态分配的内存区域。由程序员分配释放,当程序使用malloc或者new分配内存时,得到的内存来自堆里
- 可执行文件映像:存储着可执行文件在内存里的映像,由装载器在装载时将可执行文件的内存读取或映射到这里
- .data : 静态区,存放全局变量和局部静态变量
- .bss : 存放未初始化的全局变量和局部静态变量
- .text : 代码区,存放C语言编译后的机器代码,不可在运行期间修改
- 保留区:保留区并不是一个单一的内存区域,而是对内存中收到保护而禁止访问的内存区域的总称。例如通常C语言将无效指针赋值为0(NULL),因此0地址正常情况下不可能有有效的访问数据
- 栈:栈用于维护函数调用的上下文。由操作系统自动分配释放,一般包含一下几个方面
段错误 : 程序出现段错误(segment fault)或者非法操作,该内存地址不能read/write的错误信息,是什么原因
- 这是典型非法指针解引用造成的错误。当指针指向一个不允许读或者写的内存地址,而程序却试图利用指针来读或者写该地址的时候,就会出现这个错误。可能的段错误发生的时机如下:
- 指针没有初始化或者初始化未nullptr,之后没有给他一个合理的值就开始使用指针
- 使用野指针(指向一个已经删除的对象或者未申请访问受限内存区域的指针)
- 指向常量的指针试图修改相关内容
编译型语言 VS 解释型语言
有的编程语言要求必须提前将所有原地阿玛一次性转换成二进制指令,也就是生成一个可执行程序(Windows下的exe),例如C语言,C++,Golang,Pascal,汇编等,这些编程语言成为编译型语言,使用的转换工具称为编译器
有的编程语言可以一边执行一边转换,需要那些源代码就转换哪些源代码,不会生成可执行程序,例如Python,JavaScript,PHP,MATLAB等,这种编程语言称为解释型语言,使用的转换工具称为解释器
内存泄漏定位 详解
内存泄漏是指程序在运行过程中动态分配的内存空间没有被正确释放,导致系统中的可用内存不断减少,最终耗尽系统内存资源。内存泄漏可能导致程序性能下降、系统稳定性降低甚至系统崩溃。以下是定位和解决内存泄漏问题的一般步骤:
1. 监控和识别内存泄漏:
- 使用内存监控工具:使用工具监控程序的内存使用情况,识别内存泄漏的迹象,如持续增长的内存占用或内存波动异常等。
- 分析崩溃报告:如果程序崩溃,分析崩溃报告中的内存相关信息,确定是否是因为内存泄漏导致的。
2. 定位内存泄漏:
- 代码审查:仔细审查程序的代码,查找可能导致内存泄漏的地方,如未释放的动态分配内存、循环引用等。
- 内存分析工具:使用内存分析工具,如Valgrind(针对C/C++)、MAT(Memory Analyzer Tool,针对Java)等,定位内存泄漏的具体位置和原因。
3. 解决内存泄漏:
- 释放未使用的内存:确定未使用的内存并及时释放,避免不必要的内存占用。
- 修复代码缺陷:修改代码,修复导致内存泄漏的问题,如添加缺失的内存释放操作、优化内存管理逻辑等。
- 避免循环引用:在使用动态内存分配时,避免出现循环引用,确保可以正确释放对象的内存。
4. 进行测试和验证:
- 单元测试:编写单元测试用例验证修复后的代码,确保修复内存泄漏的问题。
- 性能测试:进行性能测试,验证修复内存泄漏后程序的性能和稳定性是否得到改善。
5. 持续监控和优化:
- 定期审查代码:定期审查代码,避免新的内存泄漏问题的出现。
- 持续优化内存管理:优化内存分配和释放逻辑,减少内存占用和泄漏的可能性。
综上所述,定位和解决内存泄漏需要通过监控、分析、定位、解决和验证等一系列步骤,确保程序能够有效地管理和利用内存资源,避免内存泄漏导致的性能和稳定性问题。
linux 内存过高会怎么样
当Linux系统的内存使用过高时,可能会导致以下几种情况和问题:
性能下降:当系统内存使用过高时,可能会导致系统性能下降,因为操作系统需要频繁地进行内存页的交换,从而增加了CPU和硬盘的负载。
内存压缩和交换:当物理内存不足以满足系统的需求时,Linux系统会将部分内存页面压缩并存储在交换空间(硬盘)中,这会导致磁盘I/O操作增加,进而影响系统的响应速度。
OOM(Out of Memory)异常:如果系统内存耗尽,但又无法从交换空间中分配更多的内存时,Linux内核会触发OOM Killer机制,选择性地终止某些进程,以释放内存资源。这可能导致重要进程被异常终止,从而导致系统服务中断或崩溃。
应用程序崩溃:当系统内存使用过高时,可能会导致运行在系统上的应用程序因内存不足而崩溃或异常退出。
系统不稳定:内存使用过高可能会导致系统不稳定,出现死机、进程僵死等异常情况,影响系统的正常运行。
因此,为了确保系统的稳定性和性能,建议及时监控系统的内存使用情况,合理规划和管理系统内存,及时优化应用程序和系统配置,以避免内存使用过高导致的问题。
程序性能调优有哪些方式
程序性能调优是优化程序以提高其执行效率和资源利用率的过程。下面是一些常见的程序性能调优方式:
分析和测量: 首先要对程序进行分析和测量,找出性能瓶颈所在。可以使用性能分析工具如
gprof
、perf
或Valgrind
等进行程序分析和性能测试,以确定程序的热点代码和性能瓶颈。算法优化: 通过改进算法来减少程序的时间复杂度,从而提高程序的执行效率。选择合适的数据结构和算法,尽量减少不必要的计算和数据操作。
代码优化: 通过改进代码结构和优化代码实现,减少不必要的计算、内存访问和函数调用等开销。可以通过简化算法、减少循环嵌套、提取公共部分等方式来优化代码。
并行和并发: 利用多线程、多进程和并行计算等技术来提高程序的并发性和并行性,充分利用多核处理器和分布式系统的计算资源。
IO优化: 减少IO操作的次数和开销,使用缓存、异步IO和批处理等技术来提高IO性能。
内存优化: 减少内存使用量,尽量减少内存分配和释放的次数,避免内存泄漏和内存碎片等问题。
编译优化: 使用编译器提供的优化选项来优化代码生成和执行效率,如
-O3
、-flto
等。硬件优化: 针对特定硬件平台进行优化,利用硬件加速器如GPU、FPGA等来加速计算。
缓存优化: 充分利用CPU缓存和其他级别的缓存,减少缓存失效和数据访问延迟,提高程序的局部性和访问速度。
定位和解决性能瓶颈: 通过持续的性能测试和监控,及时发现和解决程序的性能瓶颈,优化程序的关键路径和性能热点。
综合利用以上各种调优方式,可以显著提高程序的执行效率和性能,并更好地满足实际需求。
C++ perf工具 详解
perf
是一个性能分析工具,用于在 Linux 系统上对程序的性能进行分析和测量。它可以用来收集各种性能数据,包括 CPU 使用率、内存访问、函数调用图等,并提供了丰富的选项和功能来帮助用户分析程序的性能瓶颈和优化方向。
下面是 perf
工具的一些主要特点和用法:
收集性能数据:
perf
可以收集各种性能数据,包括 CPU 指令、缓存命中率、内存访问、函数调用次数和耗时、线程和进程的运行状态等。多种分析模式:
perf
提供了多种分析模式,包括 CPU 指令级分析、函数级分析、事件采样分析、事件计数器分析等,可以根据不同的需求选择合适的分析模式。动态追踪:
perf
支持动态追踪,可以在程序执行过程中进行性能分析,而无需修改程序代码或重新编译程序。交互式界面:
perf
提供了交互式命令行界面,用户可以通过简单的命令进行性能分析和数据查看,包括调用图、火焰图、热点函数等。可视化工具: 除了命令行界面,
perf
还支持与其他工具(如perf report
、perf annotate
、perf record
等)配合使用,进行性能数据的可视化分析和展示。灵活配置:
perf
提供了丰富的选项和配置功能,可以根据具体需求定制性能分析的参数和方式,以便更好地理解程序的性能特征和瓶颈。
perf
是 Linux 系统自带的性能分析工具,可以通过在命令行中直接输入 perf
加上相应的选项来使用。通过 perf
工具,开发者可以快速、方便地对程序的性能进行评估和优化,从而提高程序的执行效率和性能。
cmake perf配置
在 CMake 中配置使用 perf
工具进行性能分析并不直接支持,因为 perf
是 Linux 系统自带的性能分析工具,与 CMake 的构建系统没有直接关系。但是,你可以在 CMake 构建系统中集成 perf
工具的使用,以便在构建和运行程序时进行性能分析。
以下是一种在 CMake 中集成 perf
工具的简单方法:
配置编译选项: 在 CMakeLists.txt 文件中配置编译选项,以便在编译程序时开启性能分析支持。例如,可以通过设置
-g
选项开启调试信息,以便在分析时能够获取更多的调试信息。1
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
使用 perf 命令: 在构建和运行程序时,使用
perf
命令进行性能分析。你可以在 CMakeLists.txt 文件中定义自定义的构建和运行目标,并在其中使用perf
命令来执行性能分析。例如:1
2
3
4
5add_custom_target(run_perf
COMMAND perf record -o perf.data ./your_program
COMMAND perf report
DEPENDS your_program
)在这个例子中,
run_perf
目标定义了一个perf record
命令来收集性能数据,并使用perf report
命令来生成性能报告。DEPENDS
关键字用于指定run_perf
目标依赖于构建程序的完成。运行构建目标: 在使用
perf
工具时,可以通过运行相应的构建目标来进行性能分析。例如,在命令行中执行以下命令:1
cmake --build . --target run_perf
通过以上步骤,你可以在 CMake 构建系统中集成 perf
工具的使用,并在构建和运行程序时进行性能分析。需要注意的是,perf
工具的具体用法和选项可以根据实际需要进行调整和配置。
cmake gprof工具使用
在 CMake 中使用 gprof 工具来进行性能分析通常需要以下几个步骤:
编译时启用 gprof 支持: 首先,需要在编译时启用 gprof 支持。这可以通过在 CMakeLists.txt 文件中设置相应的编译选项来完成。通常,你需要使用
-pg
选项来启用 gprof 支持。你可以在CMAKE_C_FLAGS
和CMAKE_CXX_FLAGS
变量中添加-pg
选项,如下所示:1
2set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")重新编译你的项目: 一旦在 CMakeLists.txt 中设置了编译选项,你需要重新编译你的项目以确保 gprof 支持已经生效。
运行程序以生成 gmon.out 文件: 当你的程序运行时,gprof 会收集性能数据并将其存储在一个名为 gmon.out 的文件中。你需要确保在程序执行完毕后能够找到这个文件。你可以通过以下方式运行你的程序:
1
./your_executable
生成分析报告: 一旦 gmon.out 文件已经生成,你可以使用 gprof 工具来生成性能分析报告。使用以下命令来执行 gprof:
1
gprof ./your_executable gmon.out > analysis.txt
这将生成一个名为 analysis.txt 的文本文件,其中包含了程序的性能分析报告。
记住,使用 gprof 进行性能分析时,最好在 release 模式下编译你的代码,以便获得更准确的性能数据。此外,要确保你的代码中包含了足够的符号信息,以便 gprof 能够正确地分析函数调用关系。
这就是在 CMake 中使用 gprof 工具进行性能分析的基本步骤。