版权声明:本文为 Jiawei Xu 于2019年5月16日所写,未经允许不得转载。
Linux网络编程
socketaddr
socktaddr_in
socketaddr_un
参考资料:
Linux C中内联汇编 Inline Assembly
>可见,我们可以为每个output operand指定其约束。 > >4)input operands > 该字段为可选项,用以指明输入操作数,其典型格式为: > `: "constraints" (in_var)` > 其中,constraints可以是gcc支持的各种约束方式,in_var通常为C语言提供的输入变量。 > 与output operands类似,当有多个input时,典型格式为: > `: "constraints1" (in_var1), "constraints2" (in_var2), "constraints3" (in_var3), …` > 当然,input operands + output operands的总数通常是有限制的,考虑到每种指令集体系结构对其涉及到的指令支持的最多操作数通常也有限制,此处的操作数限制也不难理解。此处具体的上限为max(10, max_in_instruction),其中max_in_instruction为ISA中拥有最多操作数的那条指令包含的操作数数目。 > 需要明确的是,在指明input operands的情况下,即使指令不会产生output operands,其:也需要给出。例如asm ("sidt %0n" : :"m"(loc)); 该指令即使没有具体的output operands也要将:写全,因为有后面跟着: input operands字段。 > > > >5)list of clobbered registers > 该字段为可选项,用于列出指令中涉及到的且没出现在output operands字段及input operands字段的那些寄存器。若寄存器被列入clobber-list,则等于是告诉gcc,这些寄存器可能会被内联汇编命令改写。因此,执行内联汇编的过程中,这些寄存器就不会被gcc分配给其它进程或命令使用。
参考资料:
常用非易失内存编程指令
clflush(Cache Line Flush,缓存行刷回)
在处理器缓存层次结构(数据与指令)的所有级别中,使包含源操作数指定的线性地址的缓存线失效。失效会在整个缓存一致性域中传播。如果缓存层次结构中任何级别的缓存线与内存不一致(污损),则在使之失效之前将它写入内存。源操作数是字节内存位置。
CLFLUSHOPT(Optimized CLFLUSH,优化的缓存行刷回)
作用与 CLFLUSH 相似,但其之间的指令级并行度更高,比如在访问不同 CacheLine 时,CLFLUHOPT 可以乱序执行。
CLWB(Cache Line Write Back,缓存行写回)
作用与 CLFLUSHOPT 相似,但在将缓存行中的数据写回之后,该缓存行仍将呈现为未被修改过的状态;支持现状
NT STORES(NonTemporal stores)
NT STORES 是一系列用于存储不同字长数据的指令,其包括 MOVNTDQ等。NT Stores 指令在传输数据时能够绕过缓存,而直接将数据写入主存。
FENCE
FENCE 指令,也称内存屏障(Memory Barrier),起着约束其前后访存指令之间相对顺序的作用。其包括 LFENCE(约束 Load 指令), MFENCE(约束 L/S 指令), SFENCE(约束 Store 指令)。
参考资料:
计算机存储器,典型访问时间,典型容量:
- 寄存器:1ns,几十~几百B
- 一级Cache:5~10ns,几十~几百KB
- 二级Cache:40~60ns,几百KB~几MB
- 内存:100~150ns,几百MB~几GB
- 硬盘:3~15ms,几百GB~几TB
Cache:高速缓冲存储器,为了更好的利用局部性原理,减少CPU访问主存的次数。
Cache分成多个组,每个组分成多个行,line size是cache的基本单位,从主存向cache迁移数据都是按照line size为单位替换的。比如line size是32Byte,那么迁移必须一次迁移32Byte到cache。同个cache的line size总是相同的。
8路相连,8-way set associative,每个组有8个行。
主存中的地址和cache的映射关系:拿到一个地址,首先是映射到一个组里面去。cache总大小32KB,8路组相连(每组有8个line),每个line的大小line size为64Byte。一共有32K/8/64=64个组。对于32位的内存地址,每个line有2^6=64Byte,所以[0,5]区分line中的那个字节,一个64个组,我们取内存地址中间6位来hash查找地址属于哪个组。即内存地址的[6,11]位来确定属于64组的哪一个组。组确定了以后,[12,31]的内存地址与组中8个line挨个对比,如果[12,31]位与某个line一致,并且这个line位有效,那么缓存命中。
cache分成三类:
- 直接映射高速缓存(direct-mapped),每个组只有一个line,选中组之后不需要和组中的每个line比对,因为只有一个line。
- 组相连高速缓存(set-associative),S个组,每个组E个line
- 全相连高速缓存(fully-associative),只有一个组,不用hash来确定组,直接挨个比对高位地址,来确定是否命中,这种方式不适合大的缓存。
内存地址: 假设内存容量为M,内存地址为m位:那么寻址范围为000…00~FFF…F(m位)
cache的逻辑结构:
将此图与上图做对比,可以得出各参数如下:
B = 2^b
S = 2^s
现在来解释一下各个参数的意义:
一个cache被分为S个组,每个组有E个cacheline,而一个cacheline中,有B个存储单元,现代处理器中,这个存储单元一般是以字节(通常8个位)为单位的,也是最小的寻址单元。因此,在一个内存地址中,中间的s位决定了该单元被映射到哪一组,而最低的b位决定了该单元在cacheline中的偏移量。valid通常是一位,代表该cacheline是否是有效的(当该cacheline不存在内存映射时,当然是无效的)。tag就是内存地址的高t位,因为可能会有多个内存地址映射到同一个cacheline中,所以该位是用来校验该cacheline是否是CPU要访问的内存单元。
当tag和valid校验成功是,我们称为cache命中,这时只要将cache中的单元取出,放入CPU寄存器中即可。
当tag或valid校验失败的时候,就说明要访问的内存单元(也可能是连续的一些单元,如int占4个字节,double占8个字节)并不在cache中,这时就需要去内存中取了,这就是cache不命中的情况(cache miss)。当不命中的情况发生时,系统就会从内存中取得该单元,将其装入cache中,与此同时也放入CPU寄存器中,等待下一步处理。