线程内存监控优化方案

线程内存监控优化方案

一、背景

目前我们使用 jemalloc 这个第三方库来对我们的项目进行内存管理。我们需要对线程级别的内存进行监控。因此使用 jemalloc 提供的一些接口来设计。方案:https://ku.baidu-int.com/knowledge/HFVrC7hq1Q/pKzJfZczuc/WRJGZXv7Ts/1Qi2RRW2Vf2y2v

简单来说,jemalloc 内部会使用 TLS(thread local storage)对线程分配内存,减少多线程竞争,提高性能。而 jemalloc 内部有两个变量分别用来记录当前线程申请的内存累加值、释放的内存累加值。我们获取到这两个变量的值,即可得到线程在一定周期的内存使用情况。

当前方案:我们使用 LD_PRELOAD 的方式 hook pthread_create 系统调用,当创建线程的时候,我们获取到 jemalloc 内部保存内存申请、释放情况的两个指针,将这两个指针进行保存。然后 pavaro 会每秒调度 resmon 执行器,由 resmon 执行器将这两个指针的值进行处理后,录到 bag 包中。

如上是我们的背景。下面说明我们遇到的问题。

线程内存监控的目的:监控线程内存使用的稳定性,查看是否有内存泄漏,线程的内存使用是否有冲高等等。但是目前发现:

查看更多

线程内存监控优化框架

线程内存监控优化框架

一、数据存储方式

我们已经验证我们的方案是可以在异常情况发生时,收集到监控数据。因此我们对共享内存中的数据结构进行设计。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct ThreadMemMonitor {
union {
struct {
uint32_t tid;
uint32_t arena_id;
};
uint64_t key_number;
};
union {
struct {
uint32_t allocated_kb;
uint32_t deallocated_kb;
};
uint64_t val_number;
};
};

如上是一个线程所要监控的数据结构体。我们现代化以 64 位的 cpu 操作系统作为基础,64 位的操作系统大部分地址总线也是 64位的(不考虑特殊情况)。因此 cpu 一次可以获取到 8 字节的数据,当然也有缓存的影响,但我们就以最坏情况来讨论,因此我们让 cpu 读取数据最好是 8 的倍数,减少不必要的读取。

因此如上设计了单个线程的内存监控信息占用 16 字节。

查看更多

优先级反转

实时操作系统的一个基本要求就是基于优先级的抢占系统,保证高优先级的线程在 “第一时间” 抢到执行权,是实时系统的第一黄金法则。

但是基于优先级抢占的系统,有一个著名问题就是:优先级反转。也就是说,有低优先级的线程占据了 CPU,妨碍了高优先级线程的执行。

有一个比较著名的案例,就是 1997 年美国宇航局的火星探路车,在登陆火星后的一段时间里无法工作,最后查明是因为优先级反转导致探路车的计算机不断重启的问题。

一、什么是优先级反转

如下是经典的优先级反转案例

查看更多

多线程编程总结

一、线程的定义

线程的定义:操作系统中能够被调度的最小单元,线程有自己的 context、stack 以及 thread local storage;但与同一进程中的其他线程共享进程资源。

如果从另外一个角度来思考:比如把 C 语言的指针作为机器地址的抽象的话,那么线程可以认为是 “机器流水线” 或者 “虚拟处理器” 的抽象。只是流水线之间的耦合并没有线程来的那么紧密,原因在于线程之间还是共享了很多资源,编码的时候需要注意线程安全。

二、线程安全

首先注意,在很多场合,“线程安全” 和 “可重入” 总是混在一起,但这两个并不是同一概念。可重入(别名:异步信号安全)是一个更严格的要求,首先他要求线程安全,其次当发生信号中断并且执行完中断处理例程后,回头继续执行线程代码需要仍然保证正确才可以。

使用 mutex 的代码可能是线程安全的,但不是可重入的。可重入函数只是线程安全函数的一部分。

s

查看更多

profiler 工具

内存泄漏的定位与排查:Heap Profiling 原理解析:https://cn.pingcap.com/blog/an-explanation-of-the-heap-profiling-principle

gperftools 的 wiki:https://github.com/gperftools/gperftools/wiki

gprof, Valgrind and gperftools - an evaluation of some tools for application level CPU profiling on Linux :https://gernotklingler.com/blog/gprof-valgrind-gperftools-evaluation-tools-application-level-cpu-profiling-linux/

系统级性能分析工具perf的介绍与使用:https://ivanzz1001.github.io/records/post/linuxops/2017/11/16/linux-perf-usge

valgrind 工作原理简介:https://blog.mengy.org/how-valgrind-work/

查看更多

coredump

介绍


​ core dump又叫核心转储。在程序运行过程中发生异常时,将其内存数据保存到文件中,这个过程叫做core dump。core是指记忆体也就是现场的内存。

​ 在开发过程中,难免会遇到程序运行过程中异常退出的情况,这时候想要定位哪里出了问题,仅仅依靠程序自身的信息打印(日志记录)往往是不够的,这个时候就需要 Core Dump 文件来帮忙定位。一个完整的 Core Dump 文件实际上相当于恢复了异常现场,利用 Core Dump 文件,可以查看到程序异常时的所有信息,变量值、栈信息、内存数据,程序异常时的运行位置(甚至记录代码行号)等等,定位所需要的一切信息都可以从 Core Dump文件获取到,能够非常有效的提高定位效率。

Linux下配置core文件生成


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 查看core文件限制大小,0:程序出错时不会产生core文件
ulimit -l

# 设置core文件限制为1024k,不过通过对于idg业务等高内存业务来看,基本都是不够的
ulimit -c 1024

# 移除core文件的限制
ulimit -c unlimited

# 关闭core文件生成
ulimit -c 0
# 查看core文件生成路径
cat /proc/sys/kernel/core_pattern

sysctl -w kernel.core_pattern=${YOUR_PATH}/core-%e-%p-%t
# or
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
Naming of core dump files
By default, a core dump file is named core, but the
/proc/sys/kernel/core_pattern file (since Linux 2.6 and 2.4.21)
can be set to define a template that is used to name core dump
files. The template can contain % specifiers which are
substituted by the following values when a core file is created:

%% A single % character.
%c Core file size soft resource limit of crashing process
(since Linux 2.6.24).
%d Dump mode—same as value returned by prctl(2)
PR_GET_DUMPABLE (since Linux 3.7).
%e The process or thread's comm value, which typically is
the same as the executable filename (without path prefix,
and truncated to a maximum of 15 characters), but may
have been modified to be something different; see the
discussion of /proc/[pid]/comm and
/proc/[pid]/task/[tid]/comm in proc(5).
%E Pathname of executable, with slashes ('/') replaced by
exclamation marks ('!') (since Linux 3.0).
%g Numeric real GID of dumped process.
%h Hostname (same as nodename returned by uname(2)).
%i TID of thread that triggered core dump, as seen in the
PID namespace in which the thread resides (since Linux
3.18).
%I TID of thread that triggered core dump, as seen in the
initial PID namespace (since Linux 3.18).
%p PID of dumped process, as seen in the PID namespace in
which the process resides.
%P PID of dumped process, as seen in the initial PID
namespace (since Linux 3.12).
%s Number of signal causing dump.
%t Time of dump, expressed as seconds since the Epoch,
1970-01-01 00:00:00 +0000 (UTC).
%u Numeric real UID of dumped process.

查看更多