并发式平均存储访问时间模型
APC
侧重于测量方法的研究, 给出了并发存储的测量方法和尺度。
在 APC 中, 周期是存储活动周期 (memory
active cycle), 不是通用的 CPU 周期,所以 APC 也叫 APMAC( 存储活动周期平均访问数, access per
memory active cycle)。
同时 APC 采用重叠 (overlapping) 的访存时间统计方法 : 在有两个或多个存储访
问同时进行时, 周期只增加一次。
侧重于测量方法的研究, 给出了并发存储的测量方法和尺度。
在 APC 中, 周期是存储活动周期 (memory
active cycle), 不是通用的 CPU 周期,所以 APC 也叫 APMAC( 存储活动周期平均访问数, access per
memory active cycle)。
同时 APC 采用重叠 (overlapping) 的访存时间统计方法 : 在有两个或多个存储访
问同时进行时, 周期只增加一次。
HPL.dat file detailed explanation
1 | HPLinpack benchmark input file |
https://plotly.com/python/builtin-colorscales/
same in matplotlib
使用Dash: A web application framework for your data., 默认部署在localhost:8050
端口
本地机器打通ssh隧道
1 | ssh -L 8050:127.0.0.1:8050 -vN -f -l shaojiemike 202.38.72.23 |
In scientific research plotting, it’s always bar charts instead of line charts.
Possible reasons:
font things, Attention, global settings have higher priority to get work
1 |
|
1 |
1 | # mpl |
1 | # mpl: Adjust the left margin to make room for the legend, left & right chart vertical line position from [0,1] |
1 | # mpl: |
1 | # mpl |
1 | # mpl ? |
1 | # mpl: Create a bar chart with bold outlines |
1 | # mpl: |
1 | # mpl: white hugo hatch with black bar edge. |
To draw symmetry chart, we need to special highlight the overflow bar number.
If the ancher point locate in the plot box, it’s easy to show text above the ceil line using textposition="bottom"
like option. In the opposite scenario, plotly and mathplotlib all will hide the out-box text.
1 | # plotly |
But mlb can write text out box.
1 | ax.text(1, -1.6, 'Increasing', ha="center") |
mathplotlib(mpl) can achieve this using ref, but there are few blogs about plotly.
1 | # mpl: from 1*1 size full-graph (0.5,0.2) to point (1,0.8) |
1 | # mpl: |
如果防火墙是关闭的,你可以直接部署在external address上。使用docker也是可行的办法
1 | app.run_server(debug=True, host='202.38.72.23') |
1 | import matplotlib.pyplot as plt |
在柱状图中,用于表示上下浮动的元素通常被称为“误差条”(Error Bars)。误差条是用于显示数据点或柱状图中的不确定性或误差范围的线条或线段。它们在柱状图中以垂直方向延伸,可以显示上下浮动的范围,提供了一种可视化的方式来表示数据的变化或不确定性。误差条通常通过标准差、标准误差、置信区间或其他统计指标来计算和表示数据的浮动范围。
Errorbars + StackedBars stacked 的过程中由于向上的error线的会被后面的Bar遮盖,然后下面的error线由于arrayminus=[i-j for i,j in zip(sumList,down_error)]
导致大部分时间说负值,也不会显示。
1 | fig = go.Figure() |
类似股票上下跳动的浮标被称为”Candlestick”(蜡烛图)或”OHLC”(开盘-最高-最低-收盘)图表。
暂无
暂无
上面回答部分来自ChatGPT-3.5,暂时没有校验其可靠性(看上去貌似说得通)。
[1] Saket, B., Endert, A. and Demiralp, Ç., 2018. Task-based effectiveness of basic visualizations.IEEE transactions on visualization and computer graphics,25(7), pp.2505-2512.
对比如下:
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
是否有序 | 无序 | 有序,消息在传输过程中可能会乱序,TCP 会重新排序 |
传输速度 | 快 | 慢 |
连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
适用场景 | 适用于实时应用(IP电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
总结:
TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:
TCP主要提供了检验和、序列号/确认应答、超时重传、滑动窗口、拥塞控制和 流量控制等方法实现了可靠性传输。
TCP 一共使用了四种算法来实现拥塞控制:
三次握手机制:
seq = x
作为初始序列号,ack = x + 1
,同时选择一个随机数 seq = y
作为初始序列号,ack = y + 1
,序列号为 seq = x + 1
,理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
主要有三个原因:
防止已过期的连接请求报文突然又传送到服务器,因而产生错误和资源浪费。
在双方两次握手即可建立连接的情况下,假设客户端发送 A 报文段请求建立连接,由于网络原因造成 A 暂时无法到达服务器,服务器接收不到请求报文段就不会返回确认报文段。
客户端在长时间得不到应答的情况下重新发送请求报文段 B,这次 B 顺利到达服务器,服务器随即返回确认报文并进入 ESTABLISHED 状态,客户端在收到 确认报文后也进入 ESTABLISHED 状态,双方建立连接并传输数据,之后正常断开连接。
此时姗姗来迟的 A 报文段才到达服务器,服务器随即返回确认报文并进入 ESTABLISHED 状态,但是已经进入 CLOSED 状态的客户端无法再接受确认报文段,更无法进入 ESTABLISHED 状态,这将导致服务器长时间单方面等待,造成资源浪费。
三次握手才能让双方均确认自己和对方的发送和接收能力都正常。
第一次握手:客户端只是发送处请求报文段,什么都无法确认,而服务器可以确认自己的接收能力和对方的发送能力正常;
第二次握手:客户端可以确认自己发送能力和接收能力正常,对方发送能力和接收能力正常;
第三次握手:服务器可以确认自己发送能力和接收能力正常,对方发送能力和接收能力正常;
可见三次握手才能让双方都确认自己和对方的发送和接收能力全部正常,这样就可以愉快地进行通信了。
告知对方自己的初始序号值,并确认收到对方的初始序号值。
TCP 实现了可靠的数据传输,原因之一就是 TCP 报文段中维护了序号字段和确认序号字段,通过这两个字段双方都可以知道在自己发出的数据中,哪些是已经被对方确认接收的。这两个字段的值会在初始序号值得基础递增,如果是两次握手,只有发起方的初始序号可以得到确认,而另一方的初始序号则得不到确认。
因为三次握手已经可以确认双方的发送接收能力正常,双方都知道彼此已经准备好,而且也可以完成对双方初始序号值得确认,也就无需再第四次握手了。
SYN洪泛攻击属于 DOS 攻击的一种,它利用 TCP 协议缺陷,通过发送大量的半连接请求,耗费 CPU 和内存资源。
原理:
[SYN/ACK]
包(第二个包)之后、收到客户端的 [ACK]
包(第三个包)之前的 TCP 连接称为半连接(half-open connect),SYN_RECV
(等待客户端响应)状态。如果接收到客户端的 [ACK]
,则 TCP 连接成功,[SYN]
包,服务器回复 [SYN/ACK]
包,并等待客户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时。[SYN]
包将长时间占用未连接队列,影响了正常的 SYN,导致目标系统运行缓慢、网络堵塞甚至系统瘫痪。检测:当在服务器上看到大量的半连接状态时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN 攻击。
防范:
服务端:
客户端:
客户端认为这个连接已经建立,如果客户端向服务端发送数据,服务端将以RST包(Reset,标示复位,用于异常的关闭连接)响应。此时,客户端知道第三次握手失败。
第一次挥手:客户端向服务端发送连接释放报文(FIN=1,ACK=1),主动关闭连接,同时等待服务端的确认。
第二次挥手:服务端收到连接释放报文后,立即发出确认报文(ACK=1),序列号 seq = k,确认号 ack = u + 1。
这时 TCP 连接处于半关闭状态,即客户端到服务端的连接已经释放了,但是服务端到客户端的连接还未释放。这表示客户端已经没有数据发送了,但是服务端可能还要给客户端发送数据。
第三次挥手:服务端向客户端发送连接释放报文(FIN=1,ACK=1),主动关闭连接,同时等待 A 的确认。
第四次挥手:客户端收到服务端的连接释放报文后,立即发出确认报文(ACK=1),序列号 seq = u + 1,确认号为 ack = w + 1。
此时,客户端就进入了 TIME-WAIT
状态。注意此时客户端到 TCP 连接还没有释放,必须经过 2*MSL(最长报文段寿命)的时间后,才进入 CLOSED
状态。而服务端只要收到客户端发出的确认,就立即进入 CLOSED
状态。可以看到,服务端结束 TCP 连接的时间要比客户端早一些。
服务器在收到客户端的 FIN 报文段后,可能还有一些数据要传输,所以不能马上关闭连接,但是会做出应答,返回 ACK 报文段.
接下来可能会继续发送数据,在数据发送完后,服务器会向客户单发送 FIN 报文,表示数据已经发送完毕,请求关闭连接。服务器的ACK和FIN一般都会分开发送,从而导致多了一次,因此一共需要四次挥手。
主要有两个原因:
确保 ACK 报文能够到达服务端,从而使服务端正常关闭连接。
第四次挥手时,客户端第四次挥手的 ACK 报文不一定会到达服务端。服务端会超时重传 FIN/ACK 报文,此时如果客户端已经断开了连接,那么就无法响应服务端的二次请求,这样服务端迟迟收不到 FIN/ACK 报文的确认,就无法正常断开连接。
MSL 是报文段在网络上存活的最长时间。客户端等待 2MSL 时间,即「客户端 ACK 报文 1MSL 超时 + 服务端 FIN 报文 1MSL 传输」,就能够收到服务端重传的 FIN/ACK 报文,然后客户端重传一次 ACK 报文,并重新启动 2MSL 计时器。如此保证服务端能够正常关闭。
如果服务端重发的 FIN 没有成功地在 2MSL 时间里传给客户端,服务端则会继续超时重试直到断开连接。
防止已失效的连接请求报文段出现在之后的连接中。
TCP 要求在 2MSL 内不使用相同的序列号。客户端在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。或者即使收到这些过时的报文,也可以不处理它。
或者说,如果三次握手阶段、四次挥手阶段的包丢失了怎么办?如“服务端重发 FIN丢失”的问题。
简而言之,通过定时器 + 超时重试机制,尝试获取确认,直到最后会自动断开连接。
具体而言,TCP 设有一个保活计时器。服务器每收到一次客户端的数据,都会重新复位这个计时器,时间通常是设置为 2 小时。若 2 小时还没有收到客户端的任何数据,服务器就开始重试:每隔 75 分钟发送一个探测报文段,若一连发送 10 个探测报文后客户端依然没有回应,那么服务器就认为连接已经断开了。
从服务器来讲,短时间内关闭了大量的Client连接,就会造成服务器上出现大量的TIME_WAIT连接,严重消耗着服务器的资源,此时部分客户端就会显示连接不上。
从客户端来讲,客户端TIME_WAIT过多,就会导致端口资源被占用,因为端口就65536个,被占满就会导致无法创建新的连接。
解决办法:
服务器可以设置 SO_REUSEADDR 套接字选项来避免 TIME_WAIT状态,此套接字选项告诉内核,即使此端口正忙(处于
TIME_WAIT状态),也请继续并重用它。
调整系统内核参数,修改/etc/sysctl.conf文件,即修改net.ipv4.tcp_tw_reuse 和 tcp_timestamps
1 | net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; |
强制关闭,发送 RST 包越过TIME_WAIT状态,直接进入CLOSED状态。
TIME_WAIT 是主动断开连接的一方会进入的状态,一般情况下,都是客户端所处的状态;服务器端一般设置不主动关闭连接。
TIME_WAIT 需要等待 2MSL,在大量短连接的情况下,TIME_WAIT会太多,这也会消耗很多系统资源。对于服务器来说,在 HTTP 协议里指定 KeepAlive(浏览器重用一个 TCP 连接来处理多个 HTTP 请求),由浏览器来主动断开连接,可以一定程度上减少服务器的这个问题。
现在有一个数据部分长度为8192B的数据需要通过UDP在以太网上传播,经过分片化为多个IP数据报片,这些片中的数据部分长度都有哪些?(假设按照最大长度分片), 计网习题
暂无
暂无
GCC Compiler Option 1 : Optimization Options
全体选项其中一部分是Optimize-Options
1 | # 会列出可选项 |
编译时最好按照其分类有效组织, 例子如下:
1 | g++ |
-Wxxx
对 xxx 启动warning, -fxxx
启动xxx的编译器功能。-fno-xxx
关闭对应选项???-gxxx
debug 相关-mxxx
特定机器架构的选项名称 | 含义 |
---|---|
-Wall | 打开常见的所有warning选项 |
-Werror | 把warning当成error |
-std= | C or C++ language standard. eg ‘c++11’ == ‘c++0x’ ‘c++17’ == ‘c++1z’, which ‘c++0x’,’c++17’ is develop codename |
-Wunknown-pragmas | 未知的pragma会报错(-Wno-unknown-pragmas 应该是相反的) |
-fomit-frame-pointer | 不生成栈帧指针,属于-O1优化 |
-Wstack-protector | 没有防止堆栈崩溃的函数时warning (-fno-stack-protector) |
-MMD | only user header files, not system header files. |
-fexceptions | Enable exception handling. |
-funwind-tables | Unwind tables contain debug frame information which is also necessary for the handling of such exceptions |
-fasynchronous-unwind-tables | Generate unwind table in DWARF format. so it can be used for stack unwinding from asynchronous events |
-fabi-version=n | Use version n of the C++ ABI. The default is version 0.(Version 2 is the version of the C++ ABI that first appeared in G++ 3.4, and was the default through G++ 4.9.) ABI: an application binary interface (ABI) is an interface between two binary program modules. Often, one of these modules is a library or operating system facility, and the other is a program that is being run by a user. |
-fno-rtti | Disable generation of information about every class with virtual functions for use by the C++ run-time type identification features (dynamic_cast and typeid). If you don’t use those parts of the language, you can save some space by using this flag |
-faligned-new | Enable support for C++17 new of types that require more alignment than void* ::operator new(std::size_t) provides. A numeric argument such as -faligned-new=32 can be used to specify how much alignment (in bytes) is provided by that function, but few users will need to override the default of alignof(std::max_align_t) . This flag is enabled by default for -std=c++17 . |
-Wl, xxx | pass xxx option to linker, e.g., -Wl,-R/staff/shaojiemike/github/MultiPIM_icarus0/common/libconfig/lib specify a runtime library search path for dynamic libraries (shared libraries) during the linking process. |
-O3 turns on all optimizations specified by -O2
and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-loop-vectorize, -ftree-loop-distribute-patterns, -ftree-slp-vectorize, -fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone options
允许使用浮点计算获得更高的性能,但可能会略微降低精度。
更快但是有保证正确
(仅限 GNU)链接时优化,当程序链接时检查文件之间的函数调用的步骤。该标志必须用于编译和链接时。使用此标志的编译时间很长,但是根据应用程序,当与 -O* 标志结合使用时,可能会有明显的性能改进。这个标志和任何优化标志都必须传递给链接器,并且应该调用 gcc/g++/gfortran 进行链接而不是直接调用 ld。
此标志对特定处理器类型进行额外调整,但它不会生成额外的 SIMD 指令,因此不存在体系结构兼容性问题。调整将涉及对处理器缓存大小、首选指令顺序等的优化。
在 AMD Bulldozer 节点上使用的值为 bdver1,在 AMD Epyc 节点上使用的值为 znver2。是zen ver2的简称。
-fprefetch-loop-arrays
-Os
禁用https://zhuanlan.zhihu.com/p/496435946
下面没有特别指明都是O3,默认开启
-ftree-loop-distribution
-ftree-loop-distribute-patterns
-floop-interchange
-floop-unroll-and-jam
(不是计算访问的数据)
-falign-functions=n:m:n2:m2
-freorder-blocks
Unroll loops whose number of iterations can be determined at compile time or upon entry to the loop. -funroll-loops
implies -frerun-cse-after-loop
. This option makes code larger, and may or may not make it run faster.
Unroll all loops, even if their number of iterations is uncertain when the loop is entered. This usually makes programs run more slowly. -funroll-all-loops
implies the same options as -funroll-loops
,
The maximum number of instructions that a loop should have if that loop is unrolled, and if the loop is unrolled, it determines how many times the loop code is unrolled.
如果循环被展开,则循环应具有的最大指令数,如果循环被展开,则它确定循环代码被展开的次数。
The maximum number of instructions biased by probabilities of their execution that a loop should have if that loop is unrolled, and if the loop is unrolled, it determines how many times the loop code is unrolled.
如果一个循环被展开,则根据其执行概率偏置的最大指令数,如果该循环被展开,则确定循环代码被展开的次数。
The maximum number of unrollings of a single loop.
单个循环的最大展开次数。
会自动检测,但有可能检测不对。
这将为特定架构生成 SIMD 指令并应用 -mtune 优化。 arch 的有用值与上面的 -mtune 标志相同。
1 | g++ -march=native -m32 ... -Q --help=target |
position-independent code(PIC)
暂无
暂无
TODO:[5] please fix all in free time
重复了和另一篇
编译是指编译器读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码。
#include
语句以及一些宏插入程序文本中,得到main.i
和sum.i
文件。main.i
和sum.i
编译成文本文件main.s
和sum.c
的汇编语言程序。main.s
和sum.s
翻译成机器语言的二进制指令,并打包成一种叫做可重定位目标程序的格式,并将结果保存在main.o和sum.o两个文件中。这种文件格式就比较接近elf格式了。main.o
和sum.o
,得到可执行目标文件,就是elf格式文件。目标文件有三种形式:
.c
文件转化成 .i
文件.gcc –E filename.cpp -o filename.i
-E Preprocess only; do not compile, assemble or link.
-C
能保留头文件里的注释,如gcc -E -C circle.c -o circle.c
gcc -save-temps -c -o main.o main.c
cpp filename.cpp -o filename.i
命令linemarkers
类似# linenum filename flags
的注释,这些注释是为了让编译器能够定位到源文件的行号,以便于编译器能够在编译错误时给出正确的行号。flags
meaning除开注释被替换成空格,包括代码里的预处理命令:
#error "text"
的作用是在编译时生成一个错误消息,它会导致编译过程中断。 同理有#warning
#define a b
对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。#ifdef SNIPER
,#if defined SNIPER && SNIPER == 0
,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉-DSNIPER=5
#include "FileName"
或者#include 等。LINE
标识将被解释为当前行号(十进制数),FILE
则被解释为当前被编译的C源程序的名称。#include ""
vs #include <>
区别在于前者会在文件的当前目录寻找,但是后者只会在编译器编译的official路径寻找
通常的搜索顺序是:
包含指定源文件的目录(对于在 #include
命令中以引号包括的文件名)。
采用-iquote
选项指定的目录,依照出现在命令行中的顺序进行搜索。只对 #include
命令中采用引号的头文件名进行搜索。
所有header file的搜寻会从-I
开始, 依照出现在命令行中的顺序进行搜索。(可以使用-I/path/file
只添加一个头文件,尤其是在编译的兼容性修改时)
采用环境变量 CPATH 指定的目录。
采用-isystem
选项指定的目录,依照出现在命令行中的顺序进行搜索。
然后找环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
指定的路径
再找系统默认目录(/usr/include、/usr/local/include、/usr/lib/gcc-lib/i386-linux/2.95.2/include......)
通过如下命令可以查看头文件搜索目录 gcc -xc -E -v - < /dev/null
或者 g++ -xc++ -E -v - < /dev/null
*. 如果想改,需要重新编译gcc
或者在编译出错时,g++ -H -v
查看是不是项目下的同名头文件优先级高于sys-head-file
.c/.h
或者.i
文件转换成.s
文件,gcc –S filename.cpp -o filename.s
,对应于-S Compile only; do not assemble or link.
gcc –S filename.i -o filename.s
也是可行的。但是我遇到头文件冲突的问题error: declaration for parameter ‘__u_char’ but no such parameter
cc –S filename.cpp -o filename.s
cc1
命令-O3
)如果想把 C 语言变量的名称作为汇编语言语句中的注释,可以加上 -fverbose-asm
选项:
1 | gcc -S -O3 -fverbose-asm ../src/pivot.c -o pivot_O1.s |
请阅读 GNU assembly file一文
汇编器:将.s 文件转化成 .o文件,
gcc –c
,-c Compile and assemble, but do not link.
as
;objdump -Sd ../build/bin/pivot > pivot1.s
-S
以汇编代码的形式显示C++原程序代码,如果有debug信息,会显示源代码。nm file.o
查看目标文件中的符号表注意,这时候的目标文件里的使用的函数可能没定义,需要链接其他目标文件.a .so .o .dll
(Dynamic Link Library的缩写,Windows动态链接库)
List symbol names in object files.
/lib
,/usr/lib
,/lib64
(在64位系统上),/usr/lib64
(在64位系统上)遍历 LD_LIBRARY_PATH
中的每个目录,并查找包括软链接在内的所有 .so 文件。
1 | IFS=':' dirs="$LD_LIBRARY_PATH" |
ldconfig 命令用于配置动态链接器的运行时绑定。你可以使用它来查询系统上已知的库文件的位置()。
ldconfig 会扫描
/lib
和 /usr/lib
,以及 /etc/ld.so.conf
中列出的目录),查找共享库文件(.so 文件),/etc/ld.so.cache
。这个缓存文件会被动态链接器(ld.so 或 ld-linux.so)使用,以加快共享库的查找速度。1 | # 查看所有是path 的库 |
ldd
会显示动态库的链接关系,中间的nm
为U
没关系,只需要最终.so
对应符号是T
即可。ldd
时避免对不可信的可执行文件运行,因为它可能会执行恶意代码。readelf -d
或 objdump -p
来查看库依赖。通过使用ld
命令,将编译好的目标文件连接成一个可执行文件或动态库。
Foo::bar(int,long)
会变成bar__3Fooil
。其中3是名字字符数见 Linux Executable file: Structure & Running
undefined reference to
一旦链接器完成了符号解析这一步,就把代码中的每个符号引用和正好一个符号定义(即它的一个输入目标模块中的一个符号表条目)关联起来。此时,链接器就知道它的输入目标模块中的代码节和数据节的确切大小。现在就可以开始重定位步骤了,在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。重定位由两步组成:
.data
节被全部合并成一个节,这个节成为输出的可执行目标文件的.data
节。当汇编器生成一个目标模块时,它并不知道数据和代码最终将放在内存中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到对最终位置未知的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。
代码的重定位条目放在 .rel.text
中。已初始化数据的重定位条目放在 .rel.data
中。
下面 展示了 ELF 重定位条目的格式。
R_X86_64_PC32
。重定位一个使用 32 位 PC 相对地址的引用。回想一下 3.6.3 节,一个 PC 相对地址就是距程序计数器(PC)的当前运行时值的偏移量。当 CPU 执行一条使用 PC 相对寻址的指令时,它就将在指令中编码的 32 位值加上 PC 的当前运行时值,得到有效地址(如 call 指令的目标),PC 值通常是下一条指令在内存中的地址。(将 PC 压入栈中来使用)R_X86_64_32
。重定位一个使用 32 位绝对地址的引用。通过绝对寻址,CPU 直接使用在指令中编码的 32 位值作为有效地址,不需要进一步修改。1 | typedef struct { |
链接器通常从左到右解析依赖项,这意味着如果库 A 依赖于库 B,那么库 B 应该在库 A 之前被链接。
静态库static library就是将相关的目标模块打包形成的单独的文件。使用ar命令。
静态库的优点在于:
问题:
深入理解计算机系统P477,静态库例子
1 | gcc -static -o prog2c main2.o -L. -lvector |
图 7-8 概括了链接器的行为。-static
参数告诉编译器驱动程序,链接器应该构建一个完全链接的可执行目标文件,它可以加载到内存并运行,在加载时无须更进一步的链接。-lvector
参数是 libvector.a
的缩写,-L. 参数告诉链接器在当前目录下查找 libvector.a。
共享库是以两种不同的方式来“共享”的:
如上创建了一个可执行目标文件 prog2l,而此文件的形式使得它在运行时可以和 libvector.so 链接。基本的思路是:
dlopen()
interface.情况:在应用程序被加载后执行前时,动态链接器加载和链接共享库的情景。
核心思想:由动态链接器接管,加载管理和关闭共享库(比如,如果没有其他共享库还在使用这个共享库,dlclose函数就卸载该共享库。)。
.interp
节,这一节包含动态链接器的路径名,动态链接器本身就是一个共享目标(如在 Linux 系统上的 ld-linux.so). 加载器不会像它通常所做地那样将控制传递给应用,而是加载和运行这个动态链接器。然后,动态链接器通过执行下面的重定位完成链接任务:最后,动态链接器将控制传递给应用程序。从这个时刻开始,共享库的位置就固定了,并且在程序执行的过程中都不会改变。
情况:应用程序在运行时要求动态链接器加载和链接某个共享库,而无需在编译时将那些库链接到应用。
实际应用情况:
思路是将每个生成动态内容的函数打包在共享库中。
编译器yasm
的参数-DPIE
如果同一份代码可能被加载到进程空间的任意虚拟地址上执行(如共享库和动态加载代码),那么就需要使用-fPIC生成位置无关代码。
问题:多个进程是如何共享程序的一个副本的呢?
问题。
可以加载而无需重定位的代码称为位置无关代码(Position-Independent Code,PIC)
在一个 x86-64 系统中,对同一个目标模块中符号的引用是不需要特殊处理使之成为 PIC。可以用 PC 相对寻址来编译这些引用,构造目标文件时由静态链接器重定位。
然而,对共享模块定义的外部过程和对全局变量的引用需要一些特殊的技巧,接下来我们会谈到。
解决方法:延迟绑定(lazy binding),将过程地址的绑定推迟到第一次调用该过程时。
动机:使用延迟绑定的动机是对于一个像 libc.so 这样的共享库输出的成百上千个函数中,一个典型的应用程序只会使用其中很少的一部分。把函数地址的解析推迟到它实际被调用的地方,能避免动态链接器在加载时进行成百上千个其实并不需要的重定位。
结果:第一次调用过程的运行时开销很大,但是其后的每次调用都只会花费一条指令和一个间接的内存引用。
实现:延迟绑定是通过两个数据结构之间简洁但又有些复杂的交互来实现的,这两个数据结构是:GOT 和过程链接表(Procedure Linkage Table,PLT)。如果一个目标模块调用定义在共享库中的任何函数,那么它就有自己的 GOT 和 PLT。GOT 是数据段的一部分,而 PLT 是代码段的一部分。
首先,让我们介绍这两个表的内容。
PLT[0]
是一个特殊条目,它跳转到动态链接器中。PLT[1]
(图中未显示)调用系统启动函数(__libc_start_main
),它初始化执行环境,调用 main 函数并处理其返回值从 PLT[2] 开始的条目调用用户代码调用的函数。在我们的例子中,PLT[2] 调用 addvec,PLT[3](图中未显示)调用 printf。上图a 展示了 GOT 和 PLT 如何协同工作,在 addvec 被第一次调用时,延迟解析它的运行时地址:
上图b 给出的是后续再调用 addvec 时的控制流:
静态库
动态库
1 | shaojiemike@snode6 /lib/modules/5.4.0-107-generic/build [06:32:26] |
加载器:将可执行程序加载到内存并进行执行,loader和ld-linux.so。
将可执行文件加载运行
命令 | 描述 |
---|---|
ar | 创建静态库,插入、删除、列出和提取成员; |
stringd | 列出目标文件中所有可以打印的字符串; |
strip | 从目标文件中删除符号表信息; |
nm | 列出目标文件符号表中定义的符号; |
size | 列出目标文件中节的名字和大小; |
readelf | 显示一个目标文件的完整结构,包括ELF 头中编码的所有信息。 |
objdump | 显示目标文件的所有信息,最有用的功能是反汇编.text节中的二进制指令。 |
ldd | 列出可执行文件在运行时需要的共享库。 |
ltrace 跟踪进程调用库函数过程
strace 系统调用的追踪或信号产生的情况
Relyze 图形化收费试用
-g
选项,可以生成调试信息,这样在gdb中可以查看源代码。1 | objdump -g <archive_file>.a |
gcc -E -g testBigExe.cpp -o testDebug.i
相对于无-g
的命令,只会多一行信息# 1 "/staff/shaojiemike/test/OS//"
gcc -S -g testBigExe.cpp -o testDebug.s
,对比之前的汇编文件,由72行变成9760行。具体解析参考 GNU assembly file一文简单的#pragma omp for
,编译后多出汇编代码如下。当前可以创建多少个线程默认汇编并没有显示的汇编指令。
1 | call omp_get_num_threads@PLT |
某些atomic的导语会变成对应汇编
暂无
基础不牢,地动山摇。ya 了。
https://www.cnblogs.com/LiuYanYGZ/p/5574601.html
https://hansimov.gitbook.io/csapp/part2/ch07-linking/7.5-symbols-and-symbol-tables
Old Pintool Upgrade with newest pin
常见的问题:
主要原因是头文件的include的使用不同,还有一些接口的改变。
1 | $ make obj-intel64/inscount0.so |
对应的makefile规则在source/tools/Config/makefile.default.rules
1 | # Build the intermediate object file. |
UINT64
undefined bug: inscount0.cpp
include pin.H
which includes types_foundation.PH
由于old pintool 基于 pin2.14。作为对比也分析inscount0.so
的编译过程
1 | g++ |
同时multipim 的scons的编译细节如下,去除与pin无关的参数:
1 | g++ |
对比后,pin3.28 相对 pin2.14 编译时,
-DPIN_CRT=1
1 | // pin/extras/crt/include/freebsd/3rd-party/include/elf.h |
First apply the two change to old pintool
暂无
暂无
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
1 | tmux new -t $NAME |
<prefix> +
maximizes the current pane to a new window
1 | sudo yum install -y \ |
1 | git clone https://github.com/tmux/tmux.git |