原子操作
摘要本文介绍原子操作,包括乱序执行、SC=DRF、Memory Model等
乱序执行同一线程中, 没有依赖关系的指令会被乱序执行
12345x = 1;y = 2; //乱序执行x = 1;y = x + z; //顺序执行
原因1: 编译器优化。 编译器优化假设程序是单线程的。原因2: 处理器乱序执行。 乱序流水线,提高处理器的效率。原因3:存储系统, 处理器写指令要先写store buffer, 多核利用一致性协议来保障写一致性原因4: On-chip Network片上网络, 多核数据传输
带来的问题1234567//线程1x = 1;if (y == 0) { //进入临界期} else { //等待并重试}
1234567//线程2y = 1;if (x == 0) { //进入临界区} else { //等待并重试}
实际上并不能保证两个线程只有一个线程进入临界区实际上编译器会做这样的处理:
1234567register1 = (y == 0);x = 1 ...
深入理解cpp-虚函数
摘要本文将详细介绍C++中的继承和虚函数。本文不介绍基础的概念,只详细分析一些需要注意或难以理解的点。
Overview本质上,为了实现代码复用,提出了继承和多态的概念。而为了实现继承,便有了虚函数的概念。为了实现多态,便有了运行时绑定(动态绑定)的概念。
虚函数的原理C++编译器会在类中添加一个私有指针*__vptr,用来指向类自身的虚函数表。通常会将虚函数指针存放在类的起始地址。多继承的环境下,就会存放多个虚函数表指针,指向不同基类的虚函数表。虚函数表中存放虚函数的地址,其实就是一个数组中,存放函数指针。在继承时,派生类继承了父类的虚函数表。表现为:
先复制基类的虚函数列表。
如果出现了重写,则替换虚函数表中的函数指针。
如果添加了新的虚函数,则追加在虚函数表的末尾此时便可以理解多态了,若指针指向的是基类对象,其实是在基类的虚函数表中调用函数,如果指向的是派生类对象,则在派生类的虚函数表中调用函数,以此来实现多态。
动态绑定普通成员函数的调用是在编译时发生绑定。而虚函数的调用是在运行时才进行绑定。
123456class Base;class A: public Base;Ba ...
深入理解cpp-右值和完美转发
前言其实上一篇文章”深入理解cpp-拷贝和移动”就已经可以把右值和移动理解的非常透了,但是在度学堂里看了大佬的讲座,感觉讲的非常清晰明了,所以想再补充一点细节。
std::move12345678910/** * @brief Convert a value to an rvalue. * @param __t A thing of arbitrary type. * @return The parameter cast to an rvalue-reference to allow moving it.*/template<typename _Tp> _GLIBCXX_NODISCARD constexpr typename std::remove_reference<_Tp>::type&& move(_Tp&& __t) noexcept { return static_cast<typename std::remove_reference<_Tp>::type&&am ...
内存泄露排查
背景服务内存使用率一直上涨,quota已达28G,近三天内存使用率达从60上涨到70%以上,上涨幅度3G左右;基本可排除词典本身上涨原因;同时策略对词典有强依赖;故需通过排查内存泄漏原因并修复.
编译编译配置文件中需要新增
1CONFIGS('xxxx/tcmalloc@tcmalloc_V2.7.0.7_GCC820_4U3_K3_GEN_PD_BL', Libraries('libtcmalloc_and_profiler.a'))
或者使用gperftools也可以,gperftools其中就包括了tcmalloc工具`
1CONFIGS('xxxx/gperftools@gperftools_V2.0.0.1_GCC820_4U3_K3_GEN_PD_BL@git_tag',Libraries('libtcmalloc_and_profiler.a'))
同时,CPPFLAGS需要增加:
1-DBAIDU_RPC_ENABLE_HEAP_PROFILER
CFLAGS增加:
1-fno-om ...
Linux源码解析--从main函数初始化到开中断
前言上文讲到了Linux系统启动前执行的三个汇编程序,head.s程序通过将main函数压栈再出栈跳转到main函数执行,此时真正进入由C语言编写的Linux源代码。上一篇文章可以点这里进行跳转Linux源码解析–从开机到main函数
介绍本文基于Linux0.11源代码,分析main函数中前几个初始化步骤,直到main函数中打开中断,执行move_to_user_mode(),由内核特权级转为用户特权级。
main函数12345678910111213//init/main.cmem_init(main_memory_start,memory_end);trap_init();blk_dev_init();chr_dev_init();tty_init();time_init();sched_init();buffer_init(buffer_memory_end);hd_init();floppy_init();sti();move_to_user_mode();
main函数位于init/main.c在进入main函数后,执行mem_init()之前,系统首先对根设备 ...
深入理解cpp-拷贝和移动
摘要本文将详细介绍C++中的拷贝构造函数、移动构造函数、拷贝赋值函数和移动赋值函数。
前言其实,简单的理解,构造函数就是通过这个函数构造一个类的实例对象,即提供参数进行直接初始化。而拷贝构造函数是通过拷贝的方式,提供一个存在的对象拷贝一份进行拷贝初始化。而移动构造函数是利用移动构造函数来获取另一个对象的所有权,而不进行拷贝进行移动初始化。
explicit首先先来了解explicit关键字。explicit翻译过来就是显示的。explicit用于修饰构造函数,表明构造函数只能用于显式构造,而不能用于隐式转换。例如下面的例子:
1234567891011class A{public: A(int a){ }};A a = new A(1); //显式定义A a(1);A a{1}; //c++11的列表初始化A a = 1; //隐式转换
而如果类A的构造函数加了explicit,则不能使用隐式转换:
123456789class A{public: explicit A(int a){ ...
Linux源码解析--从开机加电到main函数
摘要本文所参考的源码为linux0.11,对源码进行解析。
总体介绍说明一下整体的思路。首先启动bios,bios在内存中建立中断向量表和中断服务程序。然后bios会发出0x19中断,将软盘中的第一扇区加载到内存中。第一扇区对应的是bootsect.s程序,此时处于实模式状态下,该程序的作用是将软盘中的后续扇区加载到内存中来,也就是setup.s和system模块。bootsect.s先规划内存,然后在把自己从0x07C00的位置移动到0x90000后bootsect执行0x13中断,加载setup程序。setup加载进入内存后开始加载第三部分代码,即system模块,system模块由head.s汇编好的目标代码和操作系统用C语言编写好的内核程序编译成的目标代码链接而成。bootsect.s跳转到setup程序的内存地址,开始执行setup程序。setup程序的主要目的是为了进入保护模式。setup程序首先关中断,然后将system模块移动到内存的最开始位置0x00000,覆盖掉原先存放在此处的bios中断向量表和bios数据区。setup对idtr和gdtr进行初始化,即建立gdt ...
深入理解cpp-强制转换
摘要在C++中,有4种显示的强制类型转换,分别为:static_cast,const_cast,reinterpret_cast和dynamic_cast, 本文将介绍这四种强制类型转换。
static_caststatic_cast使用于任何具有明确定义的类型转换。但是不包括底层cosnt,底层const由const_cast进行转换。同时也不支持不想关的两个类型进行转换。例如:
1234int i = 1;double k = 22.3;double j = static_cast<double>(i) / k;double u = static_cast<char *>(i); //非法
const_castconst_cast用于去除const属性,但是只能用于去除底层const,例如:
123456int b = 1;const int *i = &b;int *j = const_cast<int *>(i);*j = 2;std::cout<< *i <<std::endl; // *i = 2;std ...
深入理解cpp-static
摘要本文将介绍C++中常见的关键字static
C++内存管理开始之前,我们需要了解C++的内存管理模型。如下图所示:
由上至下,分别是栈、未使用的内存、堆、全局数据区、常量区和代码区。其中栈的增长方向为由高地址向低地址增长,局部变量存放在栈中,而堆和其他内存区域都是优先存放在低地址区域,向高地址增长。动态分配的内存,例如malloc和calloc,存放在堆区。全局数据区和常量区就是通常所说的data区。其中全局数据区就用来存放全局变量和static静态变量。常量区就用来存放常量,最后的代码区用来存放函数体的二进制代码。
static修饰局部变量static修饰的局部变量存储在全局数据区,不会随着函数的结束而释放,在程序结束时才释放。static修饰的局部变量只在编译时初始化,如果没有定义,则默认初始化为0。因为所有定义在全局数据区的未定义变量都会被初始化未0。
static修饰全局变量static修饰的全局变量或者函数会变为内部链接,即仅本文件内可以访问static的变量和函数。 通常来说,一个文件内的定义的函数和变量可以被其他文件使用,但是static修饰后仅本文件内可以使用。
s ...
Jmeter压测
摘要Jmeter是一款java开发的测试工具,本文主要对Jmeter测测进行简单介绍,通过本文可以利用Jmeter快速进行压测
0 安装首先需要安装jdk在启动Jmeter时,会提示,使用GUI进行配置,使用CLI进行测试。所以在安装Jmeter时,需要在开发机和MAC中分别安装。
mac和开发机分别安装:直接去官网下载 然后解压 https://jmeter.apache.org/download_jmeter.cgi然后配置jmeter环境,
12export JMETER_HOME=/home/jmeter/apache-jmeter-5.6.3export PATH=${JMETER_HOME}/bin:$PATH
jmeter -v出现提示则表示安装成功
由于是在开发机内进行测试,mac仅仅使用图形界面进行参数配置(也可以直接修改jmx文件),本质上mac上的jmeter只要解压缩即可。
1 测试进入bin目录中,执行./jmeter.sh启动图形界面首先添加线程组:然后添加http请求添加常量吞吐定时器,用于控制压测的qps注意,常量吞吐定时器 ...