C++11 High-Level Threading

cppnow2012 John Wiegley C++11 High-Level Threading

这个ppt讲的是std::thread, 算是一个教学指南

构造函数声明是这个样子的

 struct thread{
 template<class F, class ...Args> explicit
         thread(F&&f, Args&&... args);
 };

一例

#include <thread>
std::map<std::string, std::string> french 
{ {"hello","bonjour"},{"world","tout le monde"} };

int main(){
    std::string greet = french["hello"];
    std::thread t([&]{std::cout<<greet<<", ";});
    std::string audience = freanch["world"];
    t.join();
    std::cout<< audience<<std::endl;
}

这是普通用法,如果想传参数引用,而不是捕获怎么办 ->转成ref,std::ref

std::thread t([](const std::string& s){std::cout<<s<<", ";}, std::ref(greet));

` std::thread overreview`

不可复制 ->移动语义

系统相关的细节不涉及(调度,优先级)

joinability

几个条件

  • 肯定有线程id,不是默认的
  • 可以join或detach的,或者调用过的,肯定是joinable的, 如果joinable没调用join/detach,析构就会调用std::terminate
  • 当析构或移动了之后肯定不是joinable的,这时候去访问(join)肯定会挂的。

所以这就有几个典型异常场景

  • system_error
  • 资源耗尽导致launch fail
  • detach /join fail,比如不是joinable,或者死锁了,join挂掉
  • 线程函数抛异常,这个就比较傻逼了,肯定调不到join或者detach

就上面的例子,第八行如果挂掉,线程就忘记join了。这样thread析构会直接调用std::terminate,所以需要catch住,在catch里join一下,然后把join转发出去

try {
    std::string audience = french["world"];
} catch(...) {
    t.join; 
    throw;
}

可真难看啊。

this_thread

接口是这样的

namespace this_thread{
    thread::id get_id() noexcept;
    void yield() noexcept;
    
    template <class Clock class Duration>
    void sleep_until(
      const chrono::time_point<Clock, Duration>& abs_time);
    
    template <class Rep, class Period>
    void sleep_for(
      const chrono::duration<Rep,Period>& rel_time);
}

太长了不好记

后面两个可以记成

void sleep_until(time_point);
void sleep_for(duration);

std::async std::future

上面例子的写法很难看,作者使用async 和future重写了一下

#include <future>
...
std::fucure<void> f = std:;async([&]{std::cout<< greet<<", ";});
std::string audience = french["world"];
f.get();
...

Or…

...
auto greet = std::async([]{return french["hellp"];});
std::string audience = french["world"];
std::cout<<greet.get()<<", "<<audience<<std::endl;

这样好看多了

async的launch逻辑

async deferred

一个例子

template <class Iter>
void parallel_merge_sort(Iter start, Itera finish) {
    std::size_t d = std::distance(start, finish);
    if(d<=1) return;
    Iter mid = start; std::advance(mid, d/2);
    auto f = std::async(
        d<768?std::launch::deferred
        : std::launch:deferred | std::launch::async,
        [=]{parallel_merge_sort(start,mid);});
    parallel_merge_sort(mid, finish);
    f.get();
    std::inplace_merge(start,mid,finish);
}

std::future

api是这个样子

enum class future_status{ready, timeout, deferred};
template <class R> struct future {
    future() noexcept;
    bool valid() const noexcept;// ready
    R get();
    void wait() const;// wait for ready
    future_status wait_for(duration) const;
    future_status wait_untile(time_point) const;
    shared_future<R> share();
};

整体是move-only的,有个share接口用于共享

std::shared_future

接口和future差不多,提供copy干脏活的

enum class future_status{ready, timeout, deferred};
template <class R> struct shared_future {
    future() noexcept;
    bool valid() const noexcept;// ready
    R get();
    void wait() const;// wait for ready
    future_status wait_for(duration) const;
    future_status wait_untile(time_point) const;
    shared_future(future<R>&& f) noexcept;
};

std::promise

如果用promise来重构,能彻底异步解耦,代码更好看一些

int main() {
    std::promise<std::string> audience_send;
    auto greet = std::async(
        [](std::future<std::string> audience_rcv)
        {
            std::cout<<french["hello"]  <<", ";
            std::cout<<audience_rcv.get()<<std:;endl;
        },
        audience_send.get_future()//pull
    );
    
    audience_send_value(french["world"]);//push
    greet.wait();
}

std::promise api

template <class R>
struct promise{
    promise();
    template <class Allocator>
        promise(allocator_arg_t, const Allocator& a);
    future<R> get_future(); //pull the future
    void set_value(R); //push, make the future ready
    void set_exception(exception_ptr p);
    void set_value_at_thread_exit(R); //push result but defer readiness
    void set_exception_at_thread_exit(exception_ptr p);
};

推迟异步动作,deferring launch, -> std::packaged_task

更进一步,把 std::async 换成std::packaged_task, 必须显式调用operator()才会执行,不像async立即异步执行

int main(){
    std::packaged_task<std::sring()> do_lookup(
        []{return french["hello"];});
    auto greet = do_lookup.get_future();
    do_lookup();
    std::string audience = french["world"];
    std::cout<<greet.get()<<", "<<audience<<std::endl;
}

std::packaged_task长这个样子

template<class> class packaged_task;// undefined
template<class R, class... ArgTypes>
struct packaged_task<R(ArgTypes...)> {
  packaged_task() noexcept;
  template <class F> explicit packaged_task(F&& f);
  template <class F, class Alloc>
    explicit packaged_task(allocator_arg_t, const Alloc& a, F&& f);
  future<R> get_future();//pull
  bool valid() const noexcept;
  void operator()(ArgTypes...);//make the future ready(push)
  void make_ready_at_thread_exit(ArgTypes...);
  void reset();
};

基于锁的数据共享

基本概念,线程安全,强线程安全

std::mutex

实现一个强线程安全的堆栈

template <class T>
struct shared_stack{
    bool empty() const {
        std::lock_guard<std::mutex>  l(m);
        bool r = v.empty();
        return r;
    }
    T top() const{
        std::lock_guard<std::mutex>  l(m);
        T r = v.back();
        return r;
    }
    void pop();
    void push(T x);
private:
    mutable std::mutex m;
    std::vector<T> v;
};

这里讨论了锁,lock_guard和unique_lock

实现一个线程安全的队列

template<unsigned size, class T>
struct bounded_msg_queue{
    bounded_msg_queue()
        :begin(0),end(0),buffered(0){}
    void send(T x){
        {
            std::unique_lock<std::mutex> lk(broker);
            while(buffered == size)
                not_full.wait(lk);
            buf[end] = x;
            end = (end +1)%size;
            ++buffered;
        }
        not_empty.notify_all();
    }
    T receive(){
        T r;
        {
            std::unique_lock<std::mutex> lk(broker);
            while (buffered == 0)
                not_empty.wait(lk);
            
            r = buf[begin];
            begin = (begin+1) % size;
            -- buffered;
        }
        not_full.notify_all();
        return r;
    }    
private:
    std::mutex broker;
    unsigned int begin, end, buffered;
    T buf[size];
    std::condition_variable not_full, not_empty;
};

std::condition_variable 这个api和pthread原语差不多,不说了

boost::shared_mutex

这个可以用于多读少写的场景,有点读写锁封装的感觉。具体没有研究

ref

Read More

最近的思路


  • IO线程解包,worker线程干活,当灌入数据,IO线程跑不满CPU,是什么原因

    • set好,get不一定好,这就很让人费解
    • bench数据集是否造成了一定量的上下文切换?
  • 延迟和压力有关,如何才能保证最大的ops和最好的延迟呢,怎么调服务呢?

  • 每五分钟就想摸一次手机

  • 查cpu sar -u 1
    查流量 dstat
    sar -n DEV 1
    

Read More

程序员自我修养链接加载库 读书笔记

静态链接

  • 预处理 编译 汇编 链接
    • 预处理,展开#
      • #define替换
      • #if #endif替换
      • 处理#include 递归替换
      • 删除注释
      • 添加行号和文件名标识
  • 词法分析,语法分析,语义分析,中间语言生成
  • 链接
    • 地址空间分配
    • 符号决议
    • 重定位

目标文件

  • relocatable executable shared object

  • 格式都一样,布局也一样,代码段数据段

    • .text 代码.data已经初始化的全局变量静态变量 .bss未初始化的全局变量静态变量
      • ->二进制文件也可以强转成relocatable文件 objcopy
    • .plt .got跳转表/全局入口
    • strtab .debug .rodata .hash .line .dynamic
    • .init .fini
    • 自定义段 __attribute__((section("FOO")))

    elf结构

    ELF头
    .text
    .data
    .bss
    其他段
    section header table,段表 readelf查看 elf 头串起段表
    string table
    symbol table…

    特殊符号__executable_start __etext __edata

    符号,name mangleing,extern “C”

    强符号,弱符号,默认强符号 __attribute__((week))

    强引用,弱引用,默认强引用 __attribute__((weakref)) void foo 用来被覆盖

    弱符号典型代表,未初始化的全局变量

静态链接

  • 相似段合并

    • 空间地址分配

      • elf专属地址,其他给个偏移
    • 符号解析重定位

      • 重定位表 object -r a.o
      • 链接需要符号表 readelf -s a.o
      • 链接器不感知类型信息,多个弱符号冲突如何处理? common block ,一种符号提升手段。类似类型提升。
        • (gcc -fno-common, __attribute__((nocommon)))
    • c++相关的链接问题

      • 代码重复消除,模板和虚表造成的膨胀 linkonce段,多余的直接丢弃
      • 函数级别链接,提供接口让函数(或者参数)单独成段
      • 构造函数段和析构函数段
      • ABI问题
      • 静态链接 no-buildin -static –verbose 发生了什么

装载与动态链接

  • 菜谱与炒菜

  • overlay vs paging

    • paging 页映射

      • 创建独立虚拟地址空间

        • 分配一个页,给个页目录结束,不完整的页映射关系,等到页错误再配置
      • 执行文件头,建立虚拟空间与可执行文件的映射,准备照着菜谱炒菜

        • 可执行二进制文件又叫image懂了伐

        • VMA

          image-20200313121136446

      • 将CPU指令集设定成可执行文件入口地址,启动执行

    • 页错误

  • ELF文件链接视图和执行视图

    • 不在乎段占用,段到底什么内容,只注意权限,相同权限合到一起映射 同一个VMA
    • 可执行文件会有程序表头 ProgramHeaderTable来保存映射的段信息(segment)
  • 堆和栈也是VMA cat /proc/pid/maps

  • 可以看到和可执行文件映射vma不同,没有名字 aka AVMA anonymoout virtual memory area
  • 类似堆和栈,vdso 内核交互vma

  • 总结四中VMA类型

      执行 映像文件
    代码 VMA x
    数据VMA
    堆VMA 匿名,无映像,可向上扩展
    x 匿名,无映像,可向下拓展
  • 内核装载ELF的优化

  • 直接为0,bss不映射扔到堆里
  • 段地址对齐以及优化

    • 碎片浪费 ->共享物理页,映射多次
  • 进程栈初始化
  • 内核装载ELF过程简介
    • exec -> sysexec ->do_exec
      • magic number判断开始解释执行 ->binnary_handle -> load_elf_binary
        • elf有效性
        • .interp段存在否,设置动态链接库路径
        • elf文件映射
        • 初始化elf进程环境
        • 系统调用返回地址改成elf可执行文件的入口点 e_entry
        • eip寄存器调到elf程序入口地址,开始执行

动态链接

  • 静态链接磁盘一份内存一份造成的浪费

    • gcc -fPIC -shared -o xx.so xx.c
  • 动态链接程序运行时地址分布

    • 代码段多出来libc ld和动态库

    • 装载时重定位以及地址无关代码PIC

      • 装载时重定位和链接时重定位差不多,没有重复利用代码。引入地址无关PIC可以重复使用,即尽量让地址相关的代码放到数据段

        • 代码段复用,数据段各自复制

          • 模块内部调用,相对地址调用,无需重定位

          • 模块内部数据访问,拿到PC (内部hack)+ 记录的偏移量

            call 484 <__i686.get_pc_thunk.cx>
            add $0x118c, %ecx
            movl $0x1, 0x28(%ecx)
            
          • 模块间数据访问,数据段中建立全局偏移表。间接引用

          • 模块间调用 也是全局偏移表,保存目标函数地址 存在性能问题。elf有优化

            call 484 <__i686.get_pc_thunk.cx>
            add $0x118c, %ecx
            mov 0xfffffffc(%ecx), %eax
            call *(%eax)
            
        • 全局变量怎么处理

          • 可执行文件bss段创建库的全局变量副本 加一条mov来访问
    • 数据段地址无关性

  • 延迟绑定PLT

    • 调用时再绑定(这种理念到处都有啊原来)

      • _dl_runtime_rosolve()
      bar@plt:
      jmp *(bar@GOT)
      push n
      push moduleID
      jump _dl_runtime_resolve
      
    • .got段里拆出来。.got.plt

      • .dynamic地址
      • 本模块id
      • _dl_runtime_resolve地址
  • 动态链接相关结构

    • 引入动态链接器
      • .interp段,专门记录ld目录,字符串
      • .dynamic 导出符号表 .hash 加速查找
      • 重定位 .rel.dyn .rel.plt
    • 动态链接的步骤和实现
      • 动态链接器自举,本身也是动态链接库,需要自举完成状态切换,自举不能访问全局变量调用函数 ,因为没有重定位。.dynamic是入口点
      • 装载共享文件,合并全局符号表
        • 共享库符号冲突?后加入无效
      • 动态链接库的实现
        • 不仅是动态库,还是可执行文件
        • 内核执行不在乎是ET_EXEC还是ET_DYN,就是装载然后转移给ELF入口
          • e_entry, .interp
          • 就elf头不一样,扩展名不一样,其他都一样,window dll和exe也是类似的,rundll32.exe可以吧dll强行按照可执行文件执行
        • _dl_start -> boostrap -> _dl_start_final -> _dl_sysdep_start -> _dl_main _dl_main本身来判断自己是ld还是其他
        • 几点思考
          • 动态链接器本身是动态链接还是静态链接?ldd一下就知道了
          • 动态链接库本身是不是PIC?不是PIC的话,代码段需要重定位,没意义。
          • 动态链接库可以当做可执行文件执行,那么装载地址是?和其他动态库没区别
  • 显示运行时链接

    • 灵活注入动态库。
    • dlopen, dlsym dlerror dlclose
      • dlopen
        1. 查找LD_LIBRARY_PATH
        2. 查找/etc/ld.so.cache
        3. /lib /usr/lib
        4. 返回handle,如果filename为空返回全局符号表
        5. 会执行.init
      • dlsym 根据dlopen返回的handle来查符号

Linux共享库的组织

  • 版本
    • 兼容性 尽量别用c++接口。ABI灾难
    • 命名规则 libname.so.x.y.z
      1. x重大变动,可能不兼容
      2. y增量升级,新增接口
      3. z发布版本号,bugfix,改进等等
    • SO-NAME
      1. 只保留朱版本号的软连
        • 由于历史原因 libc.so.2.6.1 -> libc.so.6 ld.so.2.6.1 ->ld-linux.so
      2. ldconfig
    • 符号版本, 比如glibc的 GLIBC_2.6.1,更新符号来保证依赖
  • 共享库系统路径
    • /lib 系统关键库(动态链接器,c运行时,数学库,bin sbin用到的库)
    • /usr/lib 非系统运行时的关键共享库,静态库,目标文件。不会被用户用到
    • /usr/local/lib 第三方库,python解析器的lib,之类的
  • 共享库的查找过程
    • .dynamic段中DT_NEED列出路径,如果是绝对路径,就会找这个文件,如果是相对路径,就会从/lib /usr/lib /etc/ld.so.conf配置文件指定的目录中查找
      • 每次查/etc/ld.so.conf中的目录必然很慢,ldconfig会cache一份/etc/ld.so.cache
      • 更改/etc/ld.so.conf需要运行ldconfig 重新cache一份
  • 环境变量
    • LD_LIBRARY_PATH 临时更改某个应用程序的共享库查找路径,不影响整体
      • LD_LIBRARY_PATH=/home/user /bin/ls
      • 相同方案,直接启动动态链接器运行程序 /lib/ld-linux.so.2 -library-path /home/user /bin/ls
      • 整体查找顺序 LD_LIBRARY_PATH -> /etc/ld.so.cache -> /usr/lib, /lib
      • 注意不要滥用LD_LIBRARY_PATH,最好不要export
    • LD_PRELOAD 指定覆盖,优先加载,比LD_LIBRARY_PATH优先级还高
      • 同样,有个/etc/ld.so.preload
    • LD_DEBUG 打开动态链接器的调试功能
      • LD_DEBUG=files ls
        • 还支持libs bindings versions reloc symbols statictics all help
  • 共享库的创建和安装
    • gcc -shared -Wl, -soname, my_soname -o libraty_name source_files libraty_files
      • soname 不指定,就没有,ldlconfig就没用
      • 调试先别去掉符号和调试信息(strip),以及-fomit-frame-pointer
      • 查找lib目录,可以临时定义LD_LIBRARY_PATH,也可以-rpath=/home/mylib
      • 符号表,用不到不会导出。如果延迟导入dlopen可能就会反向引用失败,使用-export-dynamic
    • 清除符号信息 strip libfoo.so
      • 生成库不带信息 ld -s/ld -S S debug symbol, s all symbol
    • 共享库安装 ldconfig -n lib_dir
    • 共享库构造与析构
      • ` attribute((constructor))` 在main之前/dlopen返回之前执行
      • __attribute__((destructor)) 在main执行结束/dlclose返回之前执行
      • 必须依赖startfiles stdlib
    • 共享库脚本

内存

  • 程序的内存布局

    • 栈,维护函数调用上下文
    • 堆,动态分配内存区
    • 可执行文件映像
    • 保留区
  • 栈与调用惯例

    • 堆栈帧

      • 函数返回地址和参数

      • 临时变量

      • 保存的上下文 寄存器 ebp espimage-20200324105106459

        • 参数入栈,有遗漏的参数,分配给寄存器
        • 下一条指令的地址入栈,跳转到函数体执行
        push ebp#后面会出栈恢复
        mov ebp, esp
        #sub esp, xxx
        #push xxx
              
        ####结束后,与开头正好相反
        #pop xxx
        mov esp, ebp
        pop ebp
        ret
        
    • 调用惯例

      • 函数参数的传递顺序和方式
      • 栈维护方式
      • 名字修饰策略 name-mangling cdecl
    • 函数返回值传递

      • 寄存器有限,如果返回值太大,寄存器传指针,做复制动作rep move 或者call memcpy
        • 返回值多出来的空间占用,在栈上回预留
      • 流程 栈空间预留,预留地址传给函数-> 函数执行拷贝,把地址传出 ->外层函数把地址指向的对象拷贝 拷贝两次。
        • 返回大对象非常浪费
        • 返回值优化可能会优化掉一次拷贝。
  • 堆与内存管理

    • free list 容易损坏,性能差
    • bitmap 碎片浪费
    • 内存池

运行时

  • main并不是开始

    void _start()
    {
        %ebp = 0;
        int argc = `pop from stack`;
        char** argv = `top of stack`;
        __libc_start_main(main, argc, argv, libc_csu_init, __lib_csu_fini, edx, `top of stack`);
    }
    
  • exit都做了什么

    • 遍历函数链表,执行atexit __cxa_atexit

      movl 4(%esp), %ebx
      movl $__NR_exit, %eax ;call exit
      int $0x80; halt如果exit退出失败,就强制停止。一般走不到这里
      
  • 运行库与IO

  • C/C++运行库

    • 基本功能
      • 启动与退出
      • 标准函数
        • 变长参数,压栈
        • 复杂化printf,所以要指定参数
          • va_list char *
          • va_start 参数末尾
          • va_args获得当前参数的值,调整指针位置
          • va_end,指针清零
      • IO功能封装和实现
      • 堆的封装和实现
      • 语言实现
      • 调试
  • glibc

    • crt1.0 _start crti.o init fini开头 crtn.o init fini结尾
    • crtbegin.o crtend.o c++相关全局构造析构目标文件。属于gcc
  • 运行库和多线程

    • 栈,tls,寄存器私有,其余共有
    • 线程安全
      1. errno等全局变量
      2. strtok等不可重入函数
      3. 内存分配
      4. 异常
      5. IO函数
      6. 信号相关
  • c++全局构造与析构

    • __libc_csu_init -> _init() 调用init段 -> __do_global_ctors_aux
      • 和编译系统相关。 来自crtbegin.ogcc/Crtstuff.c编好。内部会有__CTOR_LIST__ 如何生成?- > 所有的.ctor段拼凑-> crtbegin.o串起来
        • crtend.o负责定义__CTOR_END__指向.ctor末尾
  • IO 初探,通过fread

    • 缓冲buffer
    • 缓冲溢出保护,枷锁 -> 循环读取,缓冲 ->换行符转换 ->读取api

系统调用

  • glibc封装系统调用,可绕过
  • 系统调用原理 image-20200327205248542
  • 特权级与中断
    • 中断向量表( 原来内核都有这玩意儿。。我之前玩stm32也有这东西,以为搞的什么新花样)
    • image-20200327205619506
      • 触发中断陷入内核 ->切换堆栈,保存寄存器信息,每个进程都有自己的内核栈
  • linux新型系统调用,由于int指令性能不佳
    • linux-gate.so.1 aka [vdso] 可以通过maps查看,占用4k,可以导出内部细节就是sysenter等

CRT运行库实现

  • 入口以及exit
  • 实现堆
    • freelist based 堆空间分配算法 malloc free
    • new delete
  • IO与文件操作
  • 格式化字符串

Read More

一月待读

把印象笔记收藏的链接捞出来

https://just-taking-a-ride.com/inside_python_dict/chapter1.html

https://medium.com/@ehudt/redis-hash-table-scan-explained-537cc8bb9f52

https://www.lysator.liu.se/c/pikestyle.html

http://git.kernel.dk/cgit/linux-block/commit/?h=aio-poll&id=5aeaa1ad235c708e31ad930d1ff6ba6fd39bee91

https://blog.conjur.org/special-cases-are-a-code-smell/

https://www.outlyer.com/blog/why-not-to-build-a-time-series-database/

https://99designs.com/blog/engineering/gqlgen-a-graphql-server-generator-for-go/

https://www.haiku-os.org/docs/HIG/index.xml

https://www.scientificamerican.com/article/algorithms-designed-to-fight-poverty-can-actually-make-it-worse/

这个考虑优先翻译

https://arjunsreedharan.org/post/148675821737/memory-allocators-101-write-a-simple-memory#=

https://blog.gopheracademy.com/advent-2018/avoid-gc-overhead-large-heaps/


ref


Read More

怀旧系列 boost fusion

why

最近看了一堆ppt,boost::fusion和boost::proto 出现频率太高,不得不仔细看一遍官网教程总结一下。

boost::fusion 是一套元编程工具,结合编译期与运行时的异构容器和算法组件,在c++0x时代比较有名,现在有了boost::hana这种牛逼的替代品,以后再研究,这个帖主要是总结学习fusion


Heterogeneous computing

boost::fusion::vector std::tuple boost::hana::tuple都是类似的东西,不过std::tuple光秃秃的,只有get,没有相关短发,boost::fusion和boost::hana都是在trick上做了算法加强

std::get<N> 和at_c<N>是一样的

#include <boost/fusion/sequence.hpp>
#include <boost/fusion/include/sequence.hpp>
vector<int, char, std::string> stuff(1, 'x', "howdy");
int i = at_c<0>(stuff);
char ch = at_c<1>(stuff);
std::string s = at_c<2>(stuff);

把Vector打印成XML

struct print_xml
{
    template <typename T>
    void operator()(T const& x) const
    {
        std::cout
            << '<' << typeid(x).name() << '>'
            << x
            << "</" << typeid(x).name() << '>'
            ;
    }
};
for_each(stuff, print_xml());

这个例子有个具体的推导的ppt fusion by example。以后有机会在水一贴

组织

Tuple <- iterator + Sequence + Algorithm <- Support

  • tuple
  • algorithm
    • auxiliary
    • iteration
    • query
    • transformation
  • adapted
    • adt
    • array
    • boost::array
    • boost::tuple
    • mpl
    • std_pair
    • std_tuple
    • struct
  • view
    • filter_view
    • flatten_view
    • iterator_range
    • joint_view
    • nview
    • repetitive_view
    • reverse_view
    • single_view
    • transform_view
    • zip_view
  • container
    • deque
    • list
    • map
    • set
    • vector
    • generation
  • mpl
  • functional
    • adapter
    • generation
    • invocation
  • sequence
    • comparison
    • intrinsic
    • io
  • iterator
  • support

ref

Read More

2017总结

今年就要结束了。回想起今年还算充实。更深入的了解到自己很菜了 新年计划就是读更多的书看更多好看的电影电视剧了。

Read More

十三不靠摘抄

这个是我以前导出到印象笔记的笔记,印象笔记这几年太傻逼了,我准备迁出去,把这种笔记放到豆瓣或者github。

Read More


googletest segmentation fault

自己水平有限,总结下来记录自己菜比挣扎过的时光

最近研究googletest 其实研究很久了,只是琢磨如何用到工作中的模块上。

我将工程代码和自己写的空的测试代码放在一块编译,Makefile中

CPPFLAGS        := -m32 -O1 -g -Wall -fPIC -fpack-struct=1 

然后就遇到崩溃的问题

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Test
[ RUN      ] Test.Test
Segmentation fault

gdb 跟进去,提示

(gdb) r
Starting program: /mnt/hgfs/share_work/Br_R6.5_r2676/TestCode/RM_TestCode/tes                                                                                 t/run_test.exe
[Thread debugging using libthread_db enabled]
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Test
[ RUN      ] Test.Test

Program received signal SIGSEGV, Segmentation fault.
testing::internal::scoped_ptr<std::string>::reset (this=0x812d5e8)
    at /usr/include/gtest/internal/gtest-port.h:1172
1172            delete ptr_;
(gdb) q

库肯定不可能有错误。问题到这里根据我的经验就卡住了,实际上在string上。

我用dmesg查看

[52938.831139] run_test.exe[6391]: segfault at f36968 ip 080cf52d sp bff36920 error 4 in run_test.exe[80480                                                   00+ab000]

然后addr2line

addr2line -e run_test.exe 080cf52d -                                                   f
_ZNKSs6_M_repEv
/usr/include/c++/4.4/bits/basic_string.h:281

basic_string 281是这样的

      _Rep*
      _M_rep() const
      { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }

然后我就搜到了这个帖子

关于std::string出现在_M_dispose发生SIGABRT错误的问题 - superarhow的专栏 - CSDN博客

肯定是内存对齐导致的,我搜了代码没有#pragma pack

结果发现makefile中有这个选项。。。-fpack-struct=1 就相当于#pragma pack(1)

去掉问题就解决了。

感谢前人们的铺路


Read More

网络延迟-tc工具使用简单说明

在工作中遇到了制造延迟

tc qdisc add dev eth1 root netem delay 600ms

测试部需要的场景比较特殊,只针对核心与组件之间延迟,对于普通设备等不做延迟。还需要保存延迟配置重启不失效

具体的做法是加过滤限制,只针对核心通信的组件进行延迟,组件本身做延迟。

针对不同设备,加到平台配置层里面

核心网上具体的配置 将

 tc qdisc add dev eth1 root handle 1: prio priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 tc qdisc add dev eth1 parent 1:2 handle 20: netem delay 600ms
 tc filter add dev eth1 parent 1:0 protocol ip u32 match ip dst 192.168.69.23 flowid 1:2
 tc filter add dev eth1 parent 1:0 protocol ip u32 match ip dst 192.168.69.24 flowid 1:2

写在/etc/rc.d/rc.local里面 其中192.168.69.23,24是组件ip eth1是核心对外网卡

组件设备上的具体配置 将

 cd /tcdir && chmod 777 tc
 ./tc qdisc add dev eth1 root netem delay 600ms

写在/opt/local/sbin/osscripts/OSStart 脚本中 其中 tcdir是tc文件所在的目录(不要放在root下) ,eth1是组件对外网卡

tc qdisc del dev eth1 root #删除之前的延迟配置

tc也可以针对端口做限制 以上参考 https://stackoverflow.com/questions/40196730/simulate-network-latency-on-specific-port-using-tc


ref

Read More

^