cef异常处理_c++ 异常处理(2)

前面一篇博文简单介绍了 c++ 异常处理的流程,但在一些细节上一带而过了,比如,_Unwind_RaiseException 是怎样重建函数现场的,Personality routine 是怎样清理栈上变量的等,这些细节涉及到很多与语言层面无关的东西,本文尝试介绍一下这些细节的具体实现。

相关的数据结构

如前所述,unwind 的进行需要编译器生成一定的数据来支持,这些数据保存了与每个可能抛异常的函数相关的信息以供运行时查找,那么,编译器都保存了哪些信息呢?根据 Itanium ABI 的定义,主要包括以下三类:

1)unwind table,这个表记录了与函数相关的信息,共三个字段:函数的起始地址,函数的结束地址,一个 info block 指针。

2)unwind descriptor table,这个列表用于描述函数中需要 unwind 的区域的相关信息。

3)语言相关的数据(language specific data area),用于上层语言内部的处理。

以上数据结构的描述来自 Itanium ABI 的标准定义,但在具体实现时,这些数据是怎么组织以及放到了哪里则是由编译器来决定的,对于 GCC 来说,所有与 unwind 相关的数据都放到了 .eh_frame 及 .gcc_except_table 这两个 section 里面了,而且它的格式与内容和标准的定义稍稍有些不同。

.eh_frame区域

.eh_frame 的格式与 .debug_frame 是很相似的(不完全相同),属于 DWARF 标准中的一部分。所有由 GCC 编译生成的需要支持异常处理的程序都包含了 DWARF 格式的数据与字节码,这些数据与字节码的主要作用有两个:

1)描述函数调用栈的结构(layout)

2)异常发生后,指导 unwinder 怎么进行 unwind。

DWARF 字节码功能很强大,它是图灵完备的,这意味着仅仅通过 DWARF 就可以做几乎任何事情(therotically)。但是从数据的组织上来看,DWARF 实在略显复杂晦涩,因此很少有人愿意去碰,本文也只是简单介绍其中与异常处理相关的东西。本质上来说,eh_frame 像是一张表,它用于描述怎样根据程序中某一条指令来设置相应的寄存器,从而返回到当前函数的调用函数中去,它的作用可以用如下表格来形象地描述。

program counter

CFA

ebp

ebx

eax

return address

0xfff0003001

rsp+32

*(cfa-16)

*(cfa-24)

eax=edi

*(cfa-8)

0xfff0003002

rsp+32

*(cfa-16)

eax=edi

*(cfa-8)

0xfff0003003

rsp+32

*(cfa-16)

*(cfa-32)

eax=edi

*(cfa-8

上表中,CFA(canonical frame address) 表示一个基地址,用于作为当前函数中的其它地址的起始地址,使得其它地址可以用与该基地址的偏移来表示,由于这个表可能要覆盖很多程序指令,因此这个表的体积有可能是很大的,甚至比程序本身的代码量还要大。而在实际中,为了减少这个表的体积,GCC 通常会对它进行压缩编码,以及尽可能减少要覆盖的指令的数量,比如,只对会抛异常的函数里的特定区域指令进行记录。

具体的实现上,eh_frame 由一个CIE (Common Information Entry) 及多个 FDE (Frame Description Entry) 组成,它们在内存中是连续存放的:

CIE 及 FDE 格式的定义可以参看如下:

CIE结构:

Length

Required

Extended Length

Optional

CIE ID

Required

Version

Required

Augmentation String

Required

EH Data

Optional

Code Alignment Factor

Required

Data Alignment Factor

Required

Return Address Register

Required

Augmentation Data Length

Optional

Augmentation Data

Optional

Initial Instructions

Required

Padding

FDE结构:

Length

Required

Extended Length

Optional

CIE Pointer

Required

PC Begin

Required

PC Range

Required

Augmentation Data Length

Optional

Augmentation Data

Optional

Call Frame Instructions

Required

Padding

注意其中标注红色的字段:

1)Initial Instructions,Call Frame Instructions 这两字段里放的就是所谓的 DWARF 字节码,比如:DW_CFA_def_cfa R OFF,表示通过寄存器 R 及位移 OFF 来计算 CFA,其功能类似于前面的表格中第二列指明的内容。

2)PC begin,PC range,这两个字段联合起来表示该 FDE 所能覆盖的指令的范围,eh_frame 中所有的 FDE 最后会按照 pc begin 排序进行存放。

3)如果 CIE 中的 Augmentation String 中包含有字母 "P",则相应的 Augmentation Data 中包含有指向 personality routine的指针。

4)如果 CIE 中的 Augmentation String 中包含有有字母“L”,则 FDE 中 Aumentation Data 包含有 language specific data的指针。

对一个elf文件通过如下命令:readelf -Wwf xxx,可以读取其中关于 .eh_frame 的数据:

The section .eh_frame contains:00000000 0000001c 00000000CIE

Version:1Augmentation:"zPL"Code alignment factor:1Data alignment factor:-8Return address column:16Augmentation data:00 d8 09 40 00 00 00 00 00 00DW_CFA_def_cfa: r7 ofs8 ##以下为字节码DW_CFA_offset: r16 at cfa-8

00000020 0000002c 00000024 FDE cie=00000000 pc=00400ac8..00400bd8

Augmentation data:00 00 00 00 00 00 00 00#以下为字节码

DW_CFA_advance_loc:1to 00400ac9

DW_CFA_def_cfa_offset:16DW_CFA_offset: r6 at cfa-16DW_CFA_advance_loc:3to 00400acc

DW_CFA_def_cfa_reg: r6

DW_CFA_nop

DW_CFA_nop

DW_CFA_nop

对于由 GCC 编译出来的程序来说,CIE, FDE 是其在 unwind 过程中恢复现场时所依赖的全部东西,而且是完备的,这里所说的恢复现场指的是恢复调用当前函数的函数的现场,比如,func1 调用 func2,然后我们可以在 func2 里通过查询 CIE,FDE 恢复 func1 的现场。CIE,FDE 存在于每一个需要处理异常的 ELF 文件中,当异常发生时,runtime 根据当前 PC 值调用 dl_iterate_phdr() 函数就可以把当前程序所加载的所有模块轮询一遍,从而找到该 PC 所在模块的 eh_frame。

for (n = info->dlpi_phnum; --n >= 0; phdr++)

{if (phdr->p_type ==PT_LOAD)

{

_Unwind_Ptr vaddr= phdr->p_vaddr +load_base;if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)

match= 1;

}else if (phdr->p_type ==PT_GNU_EH_FRAME)

p_eh_frame_hdr=phdr;else if (phdr->p_type ==PT_DYNAMIC)

p_dynamic=phdr;

}

找到 eh_frame 也就找到 CIE,找到了 CIE 也就可以去搜索相应的 FDE,找到FDE及CIE后,就可以从这两数据表中提取相关的信息,并执行DWARF 字节码,从而得到当前函数的调用函数的现场,参看如下用于重建函数帧的函数:

static_Unwind_Reason_Code

uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)

{struct dwarf_fde *fde;struct dwarf_cie *cie;const unsigned char *aug, *insn, *end;

memset (fs,0, sizeof (*fs));

context->args_size = 0;

context->lsda = 0;//根据context查找FDE。

fde = _Unwind_Find_FDE (context->ra - 1, &context->bases);if (fde ==NULL)

{/*Couldn't find frame unwind info for this function. Try a

target-specific fallback mechanism. This will necessarily

not provide a personality routine or LSDA.*/#ifdef MD_FALLBACK_FRAME_STATE_FOR

MD_FALLBACK_FRAME_STATE_FOR (context, fs, success);return_URC_END_OF_STACK;

success:return_URC_NO_REASON;#else

return_URC_END_OF_STACK;#endif}

fs->pc = context->bases.func;//获取对应的CIE.

cie =get_cie (fde);//提取出CIE中的信息,如personality routine的地址。

insn =extract_cie_info (cie, context, fs);if (insn ==NULL)/*CIE contained unknown augmentation.*/

return_URC_FATAL_PHASE1_ERROR;/*First decode all the insns in the CIE.*/end= (unsigned char *) next_fde ((struct dwarf_fde *) cie);//执行dwarf字节码,从而恢复相应的寄存器的值。

execute_cfa_program (insn, end, context, fs);//定位到fde的相关数据

/*Locate augmentation for the fde.*/aug= (unsigned char *) fde + sizeof (*fde);

aug+= 2 * size_of_encoded_value (fs->fde_encoding);

insn=NULL;if (fs->saw_z)

{

_Unwind_Word i;

aug= read_uleb128 (aug, &i);

insn= aug +i;

}//读取language specific data的指针

if (fs->lsda_encoding !=DW_EH_PE_omit)

aug= read_encoded_value (context, fs->lsda_encoding, aug,

(_Unwind_Ptr*) &context->lsda);/*Then the insns in the FDE up to our target PC.*/

if (insn ==NULL)

insn=aug;

end= (unsigned char *) next_fde (fde);//执行FDE中的字节码。

execute_cfa_program (insn, end, context, fs);return_URC_NO_REASON;

}

通过如上的操作,unwinder 就已经把调用函数的现场给重建起来了,这些现场信息包括:

struct_Unwind_Context

{void *reg[DWARF_FRAME_REGISTERS+1]; //必要的寄存器。

void *cfa; //canoniacl frame address, 前面提到过,基地址。

void *ra;//返回地址。

void *lsda;//该函数对应的language specific data,如果存在的话。

structdwarf_eh_bases bases;

_Unwind_Word args_size;

};

实现 Personality routine

Peronality routine 的作用主要有两个:

1)检查当前函数是否有相应的 catch 语句。

2)清理当前函数中的局部变量。

十分不巧,这两件事情仅仅依靠运行时也是没法完成的,必须依靠编译器在编译时建立起相关的数据进行协助。对于 GCC 来说,这些与抛异常的函数具体相关的信息全部放在 .gcc_except_table 区域里去了,这些信息会作为Itanium ABI 接口中所谓的 language specific data 在 unwinder 与 c++ ABI 之间传递,根据前面的介绍,我们知道在 FDE 中保存有指向 language specific data 的指针,因此 unwinder 在重建现场的时候就已经把这些数据读取了出来,c++ 的 ABI 只要调用 _Unwind_GetLanguageSpecificData() 就可以得到指向该数据的指针。

关于 GCC 下 language specific data 的格式,在网上几乎找不到什么权威的文档,我只在 llvm 的官网上找到一个相关的链接,这个文档对 gcc_except_table 作了很详细的说明,我对比了一下 GCC 源码里的 personality routine 的相关实现,发现两者还是有些许出入,因此本文接下来的介绍主要基于对 GCC 相关源码的个人解读,如有错误欢迎指正。

下图来源于网络,展示了gcc_except_table 及 language specific data 的格式:

由上图所示,LSDA 主要由一个表头,及其后紧跟着的三张表组成。

1.LSDA Header:

该表头主要用来保存接下来三张表的相关信息,如编码,及表的位移等,该表头主要包含六个域:

1)Landing pad 起始地址的编码方式,长度为一个字节。

2)landing pad 起始地址,这是可选的,只有当前面指明的编码方式不等于 DW_EH_PE_omit 时,这个字段才存在,此时读取这个字段就需要根据前面指定的编码方式进行读取,长度不固定,如果这个字段不存在,则 landing pad 的起始地址需要通过调用 _Unwind_GetRegionStart() 来获得,得到其实就是当前模块加载的起始地址,这是最常见的形式。

3)type table 的编码方式,长度为一个字节。

4)type table 的位移,类型为 unsigned LEB128,这个字段是可选的,只有3)中编码方式不等于 DW_EH_PE_omit 时,这个才存在。

5)call site table 的编码方式,长度为一个字节。

6)call site table 的长度,一个 unsigned LEB128 的值。

2.call site table

LSDA 表头之后紧跟着的是 call site table,该表用于记录程序中哪些指令有可能会抛异常,表中每条记录共有4个字段:

1)可能会抛异常的指令的地址,该地址是距 Landing pad 起始地址的偏移,编码方式由 LSDA 表头中第一个字段指明。

2)可能抛异常的指令的区域长度,该字段与 1)一起表示一系列连续的指令,编码方式与 1)相同。

3)用于处理上述指令的 Landing pad 的位移,这个值如果为 0 则表示不存在相应的 landing pad。

4)指明要采取哪些 action,这是一个 unsigned LEB128 的值,该值减1后作为下标获取 action table 中相应记录。

call site table 中的记录按第一个字段也就是指令起始地址进行排序存放,因此 unwind 的时候可以加快对该表的搜索,unwind 的过程中,如果当前 pc 的值不在 call site table 覆盖的范围内的话,搜索就会返回,然后就调用std::terminate() 结束程序,这通常来说是不正常的行为。

如果在 call site table 中有对应的处理,但 landing pad 的位移却是 0 的话,表明当前函数既不存在 catch 语句,也不需要清理局部变量,这是一种正常情况,unwinder 应该继续向上 unwind,而如果 landing pad 不为0,则表明该函数中有 catch 语句,但是这些 catch 能否处理抛出的异常则还要结合 action 字段,到 type table 中去进一步加以判断:

1)如果 action 字段为 0,则表明当前函数没有 catch 语句,但有局部变量需要清理。

2)如果 action 字段不为 0,则表明当前函数中存在 catch 语句,又因为 catch 是可能存在多个的,怎么知道哪个能够 catch 当前的异常呢?因此需要去检查 action table 中的表项。

3. Action table

action table 中每一条记录是一个二元组,表示一个 catch 语句所对应的异常,或者表示当前函数所允许抛出的异常 (exception specification),该列表每条记录包含两个字段:

1)filter type,这是一个 unsigned LEB128 的数值,用于指向 type table 中的记录,该值有可能是负数。

2)指向下一个 action table 中的下一条记录,这是当函数中有多个 catch 或 exception specification 有多个时,将各个 action 记录链接起来。

4. Type Table

type table 中存放的是异常类型的指针:

std::type_info* type_tables[];

这个表被分成两部分,一部分是各个 catch 所对应的异常的类型,另一部分是该函数允许抛出的异常类型:

void func() throw(int, string)

{

}

type table中这两部分分别通过正负下标来进行索引:

有了如上这些数据,personality routine 只需要根据当前的 pc 值及当前的异常类型,不断在上述表中查找,最后就能找到当前函数是否有 landing pad,如果有则返回 _URC_INSTALL_CONTEXT,指示 unwinder 跳过去执行相应的代码。

什么是 Landing pad

在前面一篇博文里,我们简单提到了Landing pad:指的是能够 catch 当前异常的 catch 语句。这个说法其实不确切,准确来说,landing pad 指的是 unwinder 之外的“用户代码”:

1)用于 catch 相应的 exception,对于一个函数来说,如果该函数中有 catch 语句,且能够处理当前的异常,则该 catch 就是 landing pad。

2)如果当前函数没有 catch 或者 catch 不能处理当前 exception,则意味着异常还要从当前函数继续往上抛,因而 unwind 当前函数时有可能要进行相应的清理,此时这些清理局部变量的代码就是 landing pad。

从名字上来看,顾名思议,landing pad 指的是程序的执行流程在进入当前函数后,最后要转到这里去,很恰当的描述。当 landing pad 是 catch 语句时,这个比较好理解,前面我们一直说清理局部变量的代码,这是什么意思呢?这些清理代码又放在哪里?为了说明这个问题,我们看一下如下代码:

#include #include

using namespacestd;classcs

{public:explicit cs(int i) :i_(i) { cout << "cs constructor:" << i <

};voidtest_func3()

{

cs c(33);

cs c2(332);throw 3;

cs c3(333);

cout<< "test func3" <

}voidtest_func3_2()

{

cs c(32);

cs c2(322);

test_func3();

cs c3(323);

test_func3();

}voidtest_func2()

{

cs c(22);

cout<< "test func2" <

test_func3_2();

cs c2(222);

}catch (int)

{

cout<< "catch 2" <

}

}voidtest_func1()

{

cout<< "test func1" <

test_func2();

}catch(...)

{

cout<< "catch 1" <

}

}intmain()

{

test_func1();return 0;

}

对于函数 test_func3_2() 来说,当 test_func3() 抛出异常后,在 unwind 的第二阶段,我们知道 test_func3_2() 中的局部变量 c 及 c2 是需要清理的,而 c3 则不用,那么编译器是怎么生成代码来完成这件事情的呢?当异常发生时,运行时是没有办法知道当前哪些变量是需要清理的,因为这个原因编译器在生成代码的时候,在函数的末尾设置了多个出口,使得当异常发生时,可以直接跳到某一段代码就能清理相应的局部变量,我们看看 test_func3_2() 编译后生成的对应的汇编代码:

void test_func3_2()

{

400ca4: 55 push%rbp

400ca5: 48 89 e5 mov%rsp,%rbp

400ca8: 53 push%rbx

400ca9: 48 83 ec 48 sub$0x48,%rsp

cs c(32); 400cad: 48 8d 7d e0 lea0xffffffffffffffe0(%rbp),%rdi

400cb1: be 20 00 00 00 mov$0x20,%esi

400cb6: e8 9f 02 00 00 callq 400f5a<_zn2csc1ei>

cs c2(322); 400cbb: 48 8d 7d d0 lea0xffffffffffffffd0(%rbp),%rdi

400cbf: be 42 01 00 00 mov$0x142,%esi

400cc4: e8 91 02 00 00 callq 400f5a<_zn2csc1ei>

test_func3(); 400cc9: e8 5a ff ff ff callq 400c28<_z10test_func3v>

cs c3(323); 400cce: 48 8d 7d c0 lea0xffffffffffffffc0(%rbp),%rdi

400cd2: be 43 01 00 00 mov$0x143,%esi

400cd7: e8 7e 02 00 00 callq 400f5a<_zn2csc1ei>

test_func3(); 400cdc: e8 47 ff ff ff callq 400c28<_z10test_func3v>

400ce1: eb 17 jmp 400cfa<_z12test_func3_2v>

400ce3: 48 89 45 b8 mov%rax,0xffffffffffffffb8(%rbp)

400ce7: 48 8b 5d b8 mov0xffffffffffffffb8(%rbp),%rbx

400ceb: 48 8d 7d c0 lea0xffffffffffffffc0(%rbp),%rdi #c3的this指针

400cef: e8 2e 02 00 00 callq 400f22<_zn2csd1ev>

400cf4: 48 89 5d b8 mov%rbx,0xffffffffffffffb8(%rbp)

400cf8: eb 0f jmp 400d09<_z12test_func3_2v>

400cfa: 48 8d 7d c0 lea0xffffffffffffffc0(%rbp),%rdi #c3的this指针

400cfe: e8 1f 02 00 00 callq 400f22<_zn2csd1ev>

400d03: eb 17 jmp 400d1c<_z12test_func3_2v>

400d05: 48 89 45 b8 mov%rax,0xffffffffffffffb8(%rbp)

400d09: 48 8b 5d b8 mov0xffffffffffffffb8(%rbp),%rbx

400d0d: 48 8d 7d d0 lea0xffffffffffffffd0(%rbp),%rdi #c2的this指针

400d11: e8 0c 02 00 00 callq 400f22<_zn2csd1ev>

400d16: 48 89 5d b8 mov%rbx,0xffffffffffffffb8(%rbp)

400d1a: eb 0f jmp 400d2b<_z12test_func3_2v>

400d1c: 48 8d 7d d0 lea0xffffffffffffffd0(%rbp),%rdi #c2的this指针

400d20: e8 fd 01 00 00 callq 400f22<_zn2csd1ev>

400d25: eb 1e jmp 400d45<_z12test_func3_2v>

400d27: 48 89 45 b8 mov%rax,0xffffffffffffffb8(%rbp)

400d2b: 48 8b 5d b8 mov0xffffffffffffffb8(%rbp),%rbx

400d2f: 48 8d 7d e0 lea0xffffffffffffffe0(%rbp),%rdi #c的this指针

400d33: e8 ea 01 00 00 callq 400f22<_zn2csd1ev>

400d38: 48 89 5d b8 mov%rbx,0xffffffffffffffb8(%rbp)

400d3c: 48 8b 7d b8 mov0xffffffffffffffb8(%rbp),%rdi

400d40: e8 b3 fc ff ff callq 4009f8<_unwind_resume> #c的this指针

400d45: 48 8d 7d e0 lea0xffffffffffffffe0(%rbp),%rdi

400d49: e8 d4 01 00 00 callq 400f22<_zn2csd1ev>

}

400d4e: 48 83 c4 48 add$0x48,%rsp

400d52: 5b pop%rbx

400d53: c9 leaveq

400d54: c3 retq

400d55: 90 nop

注意其中标红色的代码,_ZN2csD1Ev 即是类 cs 的析构函数,_Unwind_Resume() 则是当清理完成时,用来从 landing pad 返回的代码。test_func3_2() 中只有 3 个 cs 对象,但调用析构函数的代码却出现了 6 次。这里其实就是设置了多个出口函数,分别对应不同情况下,处理各个局部变量的析构,对于我们上面的代码来说,test_func3_2() 函数中的 landing pad 就是从地址:400d09 开始的,这些代码做了如下事情:

1)先析构 c2,然后 jump 到 400d2b 析构 c.

2)最后调用 _Unwind_Resume()

由此可见当程序中有多个可能抛异常的地方时,landing pad 也相应地会有多个,该函数的出口将更复杂,这也算是异常处理的一个 overhead 了。

总结

至此,关于 GCC 处理异常的具体流程及方式,各个细节都已写完,涉及很多比较琐碎的东西,只有反复阅读源码及相关文档才能搞明白,也不容易,只是古人说的好,纸上得来终觉浅,为了加深印象及验证所学的内容,我根据前面了解的这些知识,简单仿着 GCC 写了一个简化版的 c++ ABI,代码放到了 github 上这里,有兴趣的读者们可以参考一下,原本是打算把 unwinder 也写一遍的,但 DWARF 的格式实在太过复杂,已经超出了异常处理这个范围,就作罢了。

【引用】:

http://www.intel.com/content/dam/www/public/us/en/documents/guides/itanium-software-runtime-architecture-guide.pdf

http://mentorembedded.github.io/cxx-abi/abi-eh.html

http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html

https://www.opensource.apple.com/source/gcc/gcc-5341/gcc/

http://www.cs.dartmouth.edu/~sergey/battleaxe/hackito_2011_oakley_bratus.pdf

http://mentorembedded.github.io/cxx-abi/exceptions.pdf

http://www.airs.com/blog/archives/464

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/345284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021年春季学期期末统一考试 组织行为学 试题

试卷代号&#xff1a;1070 组织行为学 试题 2021年7月 注意事项 一、将你的学号、姓名及分校&#xff08;工作站&#xff09;名称填写在答题纸的规定栏内。考试结束后&#xff0c;把试卷和答题纸放在桌上。试卷和答题纸均不得带出考场。监考人收完考卷和答题纸后才可离开考场。…

conference、symposium、workshop等概念的区别

整理概况了[1]的观念&#xff0c;并加入一些自己的理解。 1 Conference 一般指正式的会议&#xff0c;规模较大&#xff0c;一群有共同话题的人关于一个预先定好的主题进行讨论。 举例&#xff1a;the climate conference in copenhagen 哥本哈根召开的气候大会 2 Symposiu…

【渝粤题库】国家开放大学2021春2511幼儿园课程论题目

试卷代号&#xff1a;2511 2021年春季学期期末统一考试 幼儿园课程论试题 2021年7月 一、填空题&#xff08;10小空&#xff0c;每空2分&#xff0c;共20分&#xff09; 1.蒙台梭利课程的教育内容由________、、和________这4方面组成。教师通过创设环境、提供蒙台梭利教具&…

app inventor调用图像识别_+AI场景,3步懂图像识别产品

本文重点讲述基于成熟的第三方AI平台阿里、某度、腾讯、谷歌等等中选择一个可供使用的SDK基于Python语言实现图像识别功能的GUI界面&#xff0c;从而一观图像识别车型、图像识别文本产品的实现过程。我们正处于各种图像识别的场景里&#xff0c;你去某些生鲜超市不用手机可以实…

【渝粤题库】国家开放大学2021春2681煤矿采掘技术题目

试卷代号&#xff1a; 2681 2021年春季学期期末统一考试 煤矿采掘技术 试题 2021年7月 一、单项选择题&#xff08;本题型共10小题&#xff0c;每小题3分&#xff0c;共30分。以下各题每题只有一个正确答案&#xff0c;将正确答案的代号填入题中的括号内&#xff09; 1.( )是矿…

Memory,Cache,Buffer的区别

无论是内存&#xff0c;缓存&#xff0c;缓冲本质上都是为解决不同设备读写速度不匹配的问题。而CPU读取数据的顺序是先缓存Cache后内存Memory。 一、内存 Memory Memory&#xff0c;内存处理器&#xff0c;作用是用于暂时存放CPU中的运算数据&#xff0c;以及与硬盘等外部存…

适用于Java EE / Jakarta EE开发人员的Micronaut

城镇中有一个名为Micronaut的新微服务框架。 在本文中&#xff0c;我将从Java EE / Jakarta EE的角度讨论如何冒险使用Micronaut框架。 我是Java EE开发人员&#xff0c;因此使用诸如Eclipse MicroProfile之类的解决方案开发微服务更接近我的专业知识&#xff0c;但是Micronaut…

【渝粤题库】国家开放大学2021春2745农村经济管理题目

试卷代号&#xff1a;2745 2021春季学期期末统一考试 农村经济管理 试题 2021年7月 一、单项选择题&#xff08;每题3分&#xff0c;共30分&#xff09; 1.研究经济学是因为资源的( )特点。 A.充裕性 B.稀缺性 C.公益性 D.国有性 2.( )是威胁我国农业产业安全的重要因素。 A.外…

塔罗牌第五张是什么牌_塔罗牌四季牌阵:冬至将至,快来看看未来这三个月运势如何?...

塔罗牌四季牌阵&#xff0c;是一个神奇的牌阵&#xff0c;因为一年之中只有占卜四次的机会。塔罗牌四季牌阵最初从塔罗师 Marcia Masino 的《Best Tarot Practices》&#xff0c;作为占卜接下来三个月也就是一个季度整体运势的一种特殊牌阵&#xff0c;只能在每年四个季节转变的…

【渝粤题库】国家开放大学2021春3896人文英语1题目

试卷代号&#xff1a; 3896 2021年春季学期期末统一考试 人文英语1 试题 2021年7月 注意事项 一、将你的学号、姓名及分校&#xff08;工作站&#xff09;名称填写在答题纸的规定栏内。考试结束后&#xff0c;把试卷和答题纸放在桌上。试卷和答题纸均不得带出考场。监考人收完考…

了解Beamforming

波束成形技术是将信号以一种能量集中和定向方式发送给无线终端的技术&#xff0c;能全面改善无线终端接收的信号质量&#xff0c;并提升吞吐量。 如果要采用波束成形技术&#xff0c; 前提是必须采用多天线系统。例如&#xff0c;多进多出&#xff08;MIMO&#xff09;&#xf…

saiku docker配置部署_【安装教程】01 Gitea Docker 安装部署 - 【SkywenCode】技术团队基...

在2016年-2019年&#xff0c;SkywenCode技术团队使用码云Gitee 作为线上代码库管理&#xff0c;基于敏捷开发和持续构建的思路&#xff0c;我们整体基础建设以Drone / Jenkins Docker 的方式构建和部署整个基础建设体系。在接下来的时间里&#xff0c;SkywenCode技术团队基础建…

【渝粤题库】国家开放大学2021春3979会计学概论题目

试卷代号&#xff1a;3979 2021年春季学期期末统一考试 会计学概论 试题 2021年7月 一、单项选择题&#xff08;在下列各题的备选答案中选择一个正确的&#xff0c;并将其序号字母填入题中的括号里。每小题2分&#xff0c;计20分&#xff09; 1.下列指标中不属于营运能力分析指…

通信系统的主要性能指标

性能指标的作用就是用来衡量这个系统是否是一个好的系统。 总体的性能指标包括&#xff1a;有效性&#xff0c;可靠性&#xff0c;适用性&#xff0c;经济性&#xff0c;标准性&#xff0c;可维护性等。 其中关键的指标主要是有效性和可靠性。 一. 有效性 &#xff08;1&…

【渝粤题库】广东开放大学 形成性考核 - 副本 (11)

选择题 题目&#xff1a;下列是年金现值系数表示符号的是&#xff08;&#xff09; 题目&#xff1a;某施工企业现在对外投资200万元&#xff0c;5年后一次性收回本金和利息&#xff0c;若年基准收益率为8%&#xff0c;则总计可以收回资金&#xff08;&#xff09;万元已知&…

java jvm调优_(第1部分,共3部分):有关性能调优,Java中的JVM,GC,Mechanical Sympathy等的文章和视频的摘要...

java jvm调优我已经花了几个月的时间考虑审查有关性能调优&#xff0c;JVM&#xff0c;Java中的GC&#xff0c;Mechanical Sympathy等主题的文章和视频的缓存&#xff0c;并最终花了点时间–也许这就是重点我什么时候才需要做我的智力进步&#xff01; 感谢Attila-Mihaly给我提…

数学猜想验证步骤_高中数学解题思路与技巧汇总,19种解题方法,实用!

解数学题&#xff0c;除了掌握有关的数学知识之外&#xff0c;最好掌握一定的解题技巧甚至知道点解题思想。要知道高考试题的解答过程中蕴含着重要的数学思想方法&#xff0c;如果能有意识地在解题过程中加以运用&#xff0c;势必会取得很好的效用。下面邦德华纳整理了19种数学…

【渝粤题库】广东开放大学 标准化专业英语 形成性考核

选择题 题目&#xff1a; Generally speaking, IEC Publications are bilingual in English and ____. 题目&#xff1a; The topic of Unit 3 is ______. 题目&#xff1a; The IEC produces ____ categories of publications. 题目&#xff1a; The international consens…

衡量网络性能的四大指标:带宽、时延、抖动、丢包

一 带宽 1、带宽概念&#xff1a; 带宽在百度百科中定义&#xff1a;在单位时间内从网络中的某一点到另一点所能通过的**“最高数据率”**。 计算机网络的带宽是指网络可通过的最高数据率&#xff0c;即每秒多少比特&#xff08;常用的单位是bps(bit per second)&#xff09…

【渝粤题库】广东开放大学 劳动关系理论与实务 形成性考核

选择题 题目&#xff1a;在20世纪上半叶出现的标志着现代社会保障制度从社会保险制度向综合性社会保障制度转变的法律是() 题目&#xff1a;由工人组成的旨在维护并改善其工作条件的连续性组织是() 题目&#xff1a;早期工业化时代劳动关系的表现形式是() 题目&#xff1a;认为…