缘由

xxnode 是一个程序,执行 xxnode -v 出现卡住不动的情况。

正常情况下,xxnode -v 会打印出程序版本号等信息,而且只是简单的打印版本号就会推出,不涉及到复杂的业务逻辑,但本次碰到的情况是卡住不动了,马上怀疑肯定是出现死锁了。

排查死锁

1
2
$ ps -ef |grep xxnode
8987

上面先找到卡住的 xxnode 的进程号,然后使用 gdb attach 上去:

1
$ gdb attach 8987

然后使用 bt 查看堆栈情况,发现堆栈如下:

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
(gdb) bt
#0 0x00007f9d320df5cc in __lll_lock_wait_private () from /lib64/libc.so.6
#1 0x00007f9d3205bb12 in _L_lock_16654 () from /lib64/libc.so.6
#2 0x00007f9d32058753 in malloc () from /lib64/libc.so.6
#3 0x00007f9d32917ecd in operator new(unsigned long) () from /lib64/libstdc++.so.6
#4 0x00007f9d32976a19 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /lib64/libstdc++.so.6
#5 0x00007f9d3297762b in std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned long) () from /lib64/libstdc++.so.6
#6 0x00007f9d329776d4 in std::string::reserve(unsigned long) () from /lib64/libstdc++.so.6
#7 0x00007f9d3297793f in std::string::append(char const*, unsigned long) () from /lib64/libstdc++.so.6
#8 0x0000000001431d8d in xwrite_log2 (module=0x14d4c14 "xnetwork", file=0x14d7a0b "xmain.cpp", function=0x14db0c0 <get_child_status()::__FUNCTION__> "get_child_status", line=1865,
level=enum_xlog_level_warn, msg=0x14d8a4e "waitpid() failed: ECHILD") at ../../src/xlog.cpp:717
#9 0x0000000000dc4fd5 in get_child_status () at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1865
#10 0x0000000000dc490f in signal_handler (signo=17) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1763
#11 0x0000000000dc4cc8 in xnode_signal_handler (signo=17, siginfo=0x7fffdc448f70, ucontext=0x7fffdc448e40) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1798
#12 <signal handler called>
#13 0x00007f9d32054f41 in _int_malloc () from /lib64/libc.so.6
#14 0x00007f9d320586fc in malloc () from /lib64/libc.so.6
#15 0x00007f9d32917ecd in operator new(unsigned long) () from /lib64/libstdc++.so.6
#16 0x00007f9d32976a19 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /lib64/libstdc++.so.6
#17 0x00000000013e9df1 in char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) ()
#18 0x00007f9d329786d8 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /lib64/libstdc++.so.6
#19 0x00007f9d2fe2d5fa in top::get_md5 () at /home/xchano/src/programs/xtopchano/src/version.cpp:31
#20 0x00007f9d2fe2dc23 in top::print_version () at /home/xchano/src/programs/xtopchano/src/version.cpp:62
#21 0x00007f9d2fe2d24e in init_noparams_component (component_name=0x7f9d32bbf338 <std::string::_Rep::_S_empty_rep_storage+24> "",
pub_key=0x26532c8 "BEANYyQhGD0urjcRfsmqJLctMA5Hpe6DHW4pb6Asm50o/FgY2iU82bamk0WM0GtQXhNQykvy60DMmCHVWGhFR2c=", pub_len=88,
pri_key=0x2656858 "Lv1qWftE/8rQfMxufh+5ywy0maCdWl2ikdNun4wVjAM=", pri_len=44, node_id=0x2653378 "T-0-LWT5UjpGiSPzWs7Gt3Fc8ZmNFFwwA7Gpmt", datadir=0x26517a8 "/chano",
config_file_extra=0x26519e8 "/chano/extra_conf.json") at /home/xchano/src/programs/xtopchano/so.cpp:18
#22 0x0000000000dba3b5 in lib_load (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:176
#23 0x0000000000dc45c0 in child_load_so (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1666
#24 0x0000000000dc4349 in spawn_child (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1611
#25 0x0000000000dc8897 in xnode_master_process_cycle (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:2244
#26 0x0000000000dc092c in main (argc=7, argv=0x7fffdc44bce8) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1155
(gdb) b version.cpp:31
Breakpoint 1 at 0x7f9d2fe2d5d4: file /home/xchano/src/programs/xtopchano/src/version.cpp, line 31.

注意看上面的第 2 帧和第 14 帧,都出现了 malloc 函数的调用,而 glibc malloc 内部的实现是有锁的,所以果然是出现了死锁。

上面的执行流程是这样的:

  1. 主进程启动
  2. fork 子进程,子进程会 load 一个 libxxxnode.so
  3. 子进程调用 so 里提供的 print_version() 函数,打印版本信息
  4. print_version() 函数内部有 string 的操作,也就是说会产生 malloc 的调用,正当在 malloc 内部的时候,不知道什么原因触发了某个信号(上面 signo=17)
  5. 程序执行转移到 signal_handler(),处理信号
  6. 但是不巧, signal_handler() 内部操作了日志,也就是也会调用到 malloc 函数
  7. 然后陷入死锁

所以,问题出现在了 signal_handler() 上,该信号处理函数调用了 malloc,而 malloc 是不可重入函数,造成最终的死锁

解决

要解决该死锁问题当然也简单,就是确保信号处理函数 signal_handler() 是可重入函数即可,这样即便在发生上面的场景,也就不会陷入死锁当中。

Blog:

2020-05-25 于杭州
By 史矛革

写在前面的话

自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之初的几年,并且随着比特币价格的一路飙涨,黑客针对比特币网络的攻击就一直没有停止过。据估算,目前大约有 350 ~ 400 万比特币永久丢失,价值大约 240 ~ 280 亿美元。当然其中不只有由于黑客的攻击导致的丢失,毕竟比特币最初的几年很多人都没有意识到比特币的价值,很多的私钥都遗失了。

本文就谈一下目前几种区块链网络攻击,以及其防御方案。

本文尽量用简单易懂的白话来描述,也仅代表我个人的看法,欢迎探讨

同系列:

异形攻击

异形攻击又称地址污染攻击,是指诱使同类链的节点之间互相发现、互联、侵入的一种攻击手法。同类链的意思是底层 P2P 网络使用了相同或者相似的 P2P 通信协议。这尤其针对比特币和以太坊系列的公链。

众所周知,最近几年区块链行业蓬勃发展,又过于浮躁。其中很多劣质公链大量 COPY 以太坊、比特币的源码,甚至不做修改,仅仅修改下名字成为一条新的公链,这就导致大量的公链的底层是相同的或者兼容的。

那么如果攻击者执行了异形攻击,就有可能导致同类链的节点之间互相缠绕在一起,影响公链节点内部的通信和路由,进而影响到交易、共识和安全。从而让攻击者有机会施行其他的攻击,比如 DDoS 攻击,网络分裂攻击。

本质上还是由于伸手党的存在,并且不加以修饰和对节点的检测造成了异形攻击。应对办法也很简单,首先是拒绝做伸手党,即便伸手党,起码也要研究下别人的代码,做点创新和原创的东西;其次加强对本公链的节点类型的检测,比如节点地址不符合的一切拒绝,通信协议不一致的一切拒绝,通信报文头特殊字段不一致的一切拒绝等等。

配图与本文无关
阅读全文 »

写在前面的话

自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之初的几年,并且随着比特币价格的一路飙涨,黑客针对比特币网络的攻击就一直没有停止过。据估算,目前大约有 350 ~ 400 万比特币永久丢失,价值大约 240 ~ 280 亿美元。当然其中不只有由于黑客的攻击导致的丢失,毕竟比特币最初的几年很多人都没有意识到比特币的价值,很多的私钥都遗失了。

本文就谈一下目前几种区块链网络攻击,以及其防御方案。

本文尽量用简单易懂的白话来描述,也仅代表我个人的看法,欢迎探讨

同系列:

拒绝服务攻击(Denial of Service Attack)

分布式拒绝服务攻击(Distributed Denial of Service Attack)

概念

信息安全的三要素——“保密性”、“完整性”和“可用性”中,拒绝服务攻击,针对的目标正是“可用性”。该攻击方式利用目标系统网络服务功能缺陷或者直接消耗其系统资源,使得该目标系统无法提供正常的服务。

拒绝服务攻击(DoS) 问题一直得不到合理的解决,目前还是世界性难题,究其原因是因为这是由于网络协议本身的安全缺陷造成的,从而拒绝服务攻击也成为了攻击者的终极手法。攻击者进行拒绝服务攻击,实际上让服务器实现两种效果:一是迫使服务器的缓冲区满,不接收新的请求;二是使用IP欺骗,迫使服务器把合法用户的连接复位,影响合法用户的连接。

而分布式拒绝服务攻击 (DDoS) 是指攻击者采用分布式攻击手法施行 DoS 攻击,通常是控制了多台机器向目标主机或者路由器发起 DoS 攻击。

针对区块链来说,攻击者通过 DDoS 攻击试图减慢网络速度,或者迫使网络停止运作。也可用于针对矿池,使矿池脱机,或者针对特定的目标主机,使其从网络离线。

阅读全文 »

写在前面的话

自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之初的几年,并且随着比特币价格的一路飙涨,黑客针对比特币网络的攻击就一直没有停止过。据估算,目前大约有 350 ~ 400 万比特币永久丢失,价值大约 240 ~ 280 亿美元。当然其中不只有由于黑客的攻击导致的丢失,毕竟比特币最初的几年很多人都没有意识到比特币的价值,很多的私钥都遗失了。

本文就谈一下目前几种区块链网络攻击,以及其防御方案。

本文尽量用简单易懂的白话来描述,也仅代表我个人的看法,欢迎探讨

同系列:

女巫攻击(Sybil Attack)

什么是女巫攻击

“女巫”这个词我们应该不陌生,通常指邪恶的化身,并且拥有可怕的魔法。

阅读全文 »

写在前面的话

自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之初的几年,并且随着比特币价格的一路飙涨,黑客针对比特币网络的攻击就一直没有停止过。据估算,目前大约有 350 ~ 400 万比特币永久丢失,价值大约 240 ~ 280 亿美元。当然其中不只有由于黑客的攻击导致的丢失,毕竟比特币最初的几年很多人都没有意识到比特币的价值,很多的私钥都遗失了。

本文就谈一下目前几种区块链网络攻击,以及其防御方案。

本文尽量用简单易懂的白话来描述,也仅代表我个人的看法,欢迎探讨

同系列:

日蚀攻击(Eclipse Attack)

P2P 网络

概念

在介绍什么是日蚀攻击之前,有必要先对区块链系统的底层 P2P 网络做一个简单的介绍,因为日蚀攻击就是利用了 P2P 网络的特性来进行的攻击。

P2P 即 Peer to Peer,中文意思是对等网络,它是分布式系统和计算机网络相结合的产物。对等的意思就是网络中的节点角色、地位是平等的,任何节点具有极强的自由,可以任意加入、离开网络。这跟传统的 C/S 模型的结构有很大区别,任何节点既是 client ,也是 server,或者说网络中没有 server 节点,任何节点 down 掉不会对整个网络产生致命的影响,具有极强的伸缩性。

P2P 网络从诞生到现在经过了几个阶段,分别是混合式 P2P,无结构化 P2P以及结构化 P2P。

  • 混合式:顾名思义,P2P 网络混合了传统的 C/S 模型,网络中有角色充当 server 角色
  • 无结构化:也就是网状结构模型,纯分布式网络,典型代表就是比特币网络,节点之间以一种随机的,松散的方式组织在一起
  • 结构化:节点按照一定规则组织在一起,路由算法比较精准,比如 DHT 算法

混合式
阅读全文 »

写在前面的话

自比特币诞生到现在,比特币(网络)经历过大大小小非常多次的攻击,尤其在比特币诞生之初的几年,并且随着比特币价格的一路飙涨,黑客针对比特币网络的攻击就一直没有停止过。据估算,目前大约有 350 ~ 400 万比特币永久丢失,价值大约 240 ~ 280 亿美元。当然其中不只有由于黑客的攻击导致的丢失,毕竟比特币最初的几年很多人都没有意识到比特币的价值,很多的私钥都遗失了。

本文就谈一下目前几种区块链网络攻击,以及其防御方案。

本文尽量用简单易懂的白话来描述,也仅代表我个人的看法,欢迎探讨

同系列:

51%攻击

在了解什么是 51%攻击前,先简单科普下区块链的几个概念,这里主要以比特币为例作说明。

什么是挖矿?

其实挖矿这个词描述得有点太过于形象了,以至于弄得反而很生涩。当然区块链世界里还有很多玩概念的东西,背后道理其实反而没那么复杂。

在比特币网络里,大家共同在维护一张账目表,参与记账的节点可以称之为矿工,其中矿工需要做的事情就是拼命竞争记账的权利,这个竞争记账权的过程可以称之为挖矿,当一个节点得到这个记账权之后,可以描述为这个节点挖到矿了。那么节点为什么会拼命的竞争这个记账权呢?因为比特币会对挖到矿的节点有奖励。这个奖励是基于区块高度的,最开始是每个区块奖励 50btc,每产生 210000 个区块为一个减半间隔,减半间隔之后奖励会减半。比如目前(2020.04)区块奖励是 12.5btc。

上面这段话里面有两个点需要解释:

  1. 为什么节点要竞争这个记账权
  2. 区块高度又是什么

针对第一个问题,如果用比较白话的方式讲的话就是,在分布式去信任的系统中,由于有激励的存在,大家都想拿到这个记账权,但是这个记账权在同一时刻(这里用词不一定表示某一刻,更多的形容相对的同一时刻)只允许其中一个节点拿到,并且由这个节点对交易进行记录。这样才能保证这张账本是唯一的,大家看到的是一样的账本。不然大家都来记账的话,这张账本就乱了,这就是称之为 ”共识“ 的由来。

针对第二个问题,很好理解,区块高度或者说时钟高度,其实是用来描述一个区块的序号的,从创世区块 0 开始依次递增。不用过分纠结,本身是一个很简单的东西,或者叫区块序号更容易理解【手动滑稽】,可以看一下下图:

阅读全文 »

cpu 性能分析

CPU 性能分析工具很多,我常用的工具是 perf 工具。

perf

perf 是 Linux 上的一款性能分析工具,可以对 on-cpu、off-cpu、memory 等进行采集分析。on-cpu 是指程序运行在 cpu 上的时间,off-cpu 是指程序阻塞在锁、IO 事件、cpu 调度等的时间, memory 采集是针对内存堆栈的采集(我没有用过)。

perf 的原理是定时在 cpu 上产生一个中断,然后看一下此时正在执行的是哪一个 pid,那个函数,然后进行统计汇总,最后形成一幅 cpu 的采样图。消耗 cpu 越多的函数理论上被采集到的次数以及概率就会越多,那么根据采样得到的比例就能推算出哪里是性能热点,哪里能进行性能优化。

火焰图

perf 采集到的数据无法直观的感受到程序性能消耗情况,通常我们会结合火焰图来分析。火焰图顾名思义,就是一幅像火焰的图,它对 perf 采集到的数据进行了分析,直观的把进程和函数对 cpu 的消耗情况进行了统计和展示。如下图:

如何分析火焰图

火焰图的横轴和纵轴没有具体的单位,纵轴表示调用栈,横轴代表 cpu 百分比,也就是说整个横轴看成 100% cpu,由不同的进程组成,每个进程占用的 cpu 百分比之和就是 100%。

占用 cpu 百分比较多的函数就是我们重点关注的对象,另外如果一个火焰的顶部呈水平秃顶的状态,那么这个函数通常要重点关注,可能有性能瓶颈。

一键采集脚本

特性

上面简单对 perf 和火焰图做了一点介绍,这不是本文的重点。通过使用 perf 采集 cpu 数据,并利用一些工具生成火焰图的过程稍微复杂,所以下面分享一个我自己写的脚本:

https://github.com/smaugx/dailytools/blob/master/flamesvg.sh

该脚本实现以下功能:

  • 自动安装采集过程的依赖包和工具
  • 一键采集 cpu 信息
  • 一键生成火焰图
  • 一键生成文本形式的采样图
  • 支持自定义采集时间长短
  • 支持 ubuntu 和 centos
阅读全文 »

Linux 进程

Linux 上值得关注的几种进程分别是:

  • 孤儿进程
  • 僵尸进程
  • 守护进程

孤儿进程是指子进程还在运行的时候,其父进程退出了,此时该子进程就变成 “孤儿进程”,孤儿进程会被 1 号 init(systemd) 进程收养,后续该子进程的退出清理工作就交由 init 进程来管理。

僵尸进程是指父进程 fork 了子进程后,子进程运行完毕退出,但父进程并没有使用 wait 或者 waitpid 来获取子进程的状态信息,导致子进程的进程描述符依然存在系统中,如果使用 ps 、top 等命令会看到进程状态为 Z,这种进程就是僵尸进程。僵尸进程有危害,如果大量产生僵尸进程,会大量消耗系统资源。

守护进程是指运行在后台的一种特殊进程,它脱离于控制终端,并且周期性地执行某种任务或等待处理某些发生的事件。linux 上大多数系统进程都是守护进程。

本文重点研究一下守护进程。

守护进程

先来看一个进程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
1 1162 1162 1162 ? -1 Ss 0 0:00 /usr/sbin/sshd -D
1162 1460 1460 1460 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1460 1464 1464 1464 pts/0 1507 Ss 0 0:00 | \_ -bash
1464 1506 1506 1464 pts/0 1507 S 0 0:00 | \_ ./hello_world
1464 1507 1507 1464 pts/0 1507 S+ 0 0:00 | \_ ./hello_world_fork
1507 1508 1507 1464 pts/0 1507 S+ 0 0:00 | \_ ./hello_world_fork
1507 1509 1507 1464 pts/0 1507 S+ 0 0:00 | \_ ./hello_world_fork
1507 1510 1507 1464 pts/0 1507 S+ 0 0:00 | \_ ./hello_world_fork
1507 1511 1507 1464 pts/0 1507 S+ 0 0:00 | \_ ./hello_world_fork

1162 1479 1479 1479 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1479 1483 1483 1483 pts/1 1516 Ss 0 0:00 | \_ -bash
1483 1516 1516 1483 pts/1 1516 S+ 0 0:00 | \_ ./hello_world

1162 1517 1517 1517 ? -1 Ss 0 0:00 \_ sshd: root@pts/2
1517 1521 1521 1521 pts/2 1536 Ss 0 0:00 \_ -bash
1521 1536 1536 1521 pts/2 1536 R+ 0 0:00 \_ ps ajxf

上面是使用 ps ajxf 命令的输出的其中一部分,只展示了终端相关的进程情况。可以看到这台机器上开了 3 个终端,每个终端上有不同的进程在运行。

接下来就以上面的结果为例作说明。

阅读全文 »

python脚本采集bandwidth

经常要做一些 linux 系统上的性能分析或者采集 cpu/mem/bandwidth 上报到监控系统。

分享一个我平常常用到的 bandwidth 采集脚本,原理是分析 /proc/net/dev 文件, 脚本如下:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#脚本探测网卡流入带宽,循环输出

import os
import time
import pdb

filename = './bandwidth.log'

'''
[root@~]$ cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 1144779885672 14057281982 0 0 0 0 0 0 1144779885672 14057281982 0 0 0 0 0 0
eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
eth1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
eth2: 26686495240 203608963 0 0 0 0 0 1 78529414436 193724479 0 0 0 0 0 0
eth3: 10038847365 82467612 0 0 0 0 0 0 26209215795 64571217 0 0 0 0 0 0
bond0: 36725342605 286076575 0 0 0 0 0 1 104738630231 258295696 0 0 0 0 0 0
'''

def get_rx(interface = 'eth0'):
rsbytes = []
cmd = 'cat /proc/net/dev'
r = os.popen(cmd).readlines()
if len(r) < 4:
print "error: can't find eth interface"
return rsbytes
interface_dict = {}
for i in xrange(2,len(r),1): #从 lo 开始
interface_name = r[i].split()[0].split(':')[0]
interface_dict[interface_name] = i

if interface in interface_dict:
position = interface_dict.get(interface)
recvbytes = r[position].split()[1]
sendbytes = r[position].split()[9]
rsbytes.append(int(recvbytes))
rsbytes.append(int(sendbytes))

return rsbytes


def iftop_interface(interface = 'eth0'):
begin = int(time.time())
beginrs = get_rx(interface)
if not beginrs:
print 'error: can not find interface %s' % interface
return
while True:
time.sleep(2)
endrs = get_rx(interface)
end = int(time.time())
rxrate = float((endrs[0] - beginrs[0])) / (end - begin) * 8
sxrate = float((endrs[1] - beginrs[1])) / (end - begin) * 8
tl = time.localtime(end)
date = time.strftime('%m-%d %H:%M:%S', tl)
cout = "%s [recv(rate) = %s Mbps] [send(rate) = %s Mbps] \n" % (date,rxrate / 1000000,sxrate / 1000000)

fout = open(filename,'a')
fout.write(cout)
fout.close()

print cout

#重新赋值,进入再次循环
begin,beginrs = end,endrs


if __name__ == "__main__":
iftop_interface('ens33')

默认的网卡名字是 eth0,有些机器可能会不一样,只需要修改成你自己机器的网卡名称就行。

脚本可以直接在 我的github 进行下载。

其他

欢迎关注下我的其他脚本,平常可能会用到的一些脚本,整理了一下。

https://github.com/smaugx/dailytools

Blog:

2018-05-21 于杭州
By 史矛革

python脚本采集cpu

经常要做一些 linux 系统上的性能分析或者采集 cpu/mem/bandwidth 上报到监控系统。

分享一个我平常常用到的 cpu 采集脚本,原理是分析 /proc/stat 文件, 脚本如下:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/usr/bin/env python
# -*- coding:utf8 -*-

import time
import copy

class CpuWatch(object):
def __init__(self, time_step):
self.cpufile_ = '/proc/stat'
self.watch_time_step_ = time_step # 60 s
return

# 采集cpu信息
def read_cpu(self):
cpu_info = {}
cpufile = self.cpufile_
with open(cpufile, 'r') as fin:
for line in fin:
line_fields = line.split()
if line_fields[0] != "cpu":
continue
total = 0
for field in line_fields:
if field == "cpu":
continue
total += int(field)

cpu_info = {
"User": int(line_fields[1]),
"Sys": int(line_fields[3]),
"Idle": int(line_fields[4]),
"Steal": int(line_fields[8]),
"Wait": int(line_fields[5]),
"Total": total
}
fin.close()
return cpu_info

def get_avg_cpu(self, cpu_info_old, cpu_info):
if not cpu_info_old or not cpu_info:
return None

result = {}
if set(cpu_info.keys()) != set(cpu_info_old.keys()):
return None

delta_total = cpu_info["Total"] - cpu_info_old["Total"]
delta_user = cpu_info["User"] - cpu_info_old["User"]
delta_sys = cpu_info["Sys"] - cpu_info_old["Sys"]
delta_idle = cpu_info["Idle"] - cpu_info_old["Idle"]
delta_wait = cpu_info["Wait"] - cpu_info_old["Wait"]
delta_steal = cpu_info["Steal"] - cpu_info_old["Steal"]

last_cpu_info = cpu_info
result = {
"cpu_user": int(float(delta_user)/float(delta_total) * 100),
"cpu_sys": int(float(delta_sys)/float(delta_total) * 100),
"cpu_wait": int(float(delta_wait)/float(delta_total) * 100),
"cpu_steal": int(float(delta_steal)/float(delta_total) * 100),
"cpu_idle": int(float(delta_idle)/float(delta_total) * 100),
"cpu_util": int(float(delta_total - delta_idle - delta_wait - delta_steal)/float(delta_total) * 100)
}
print(result)
return result

def run(self):
cpu_info_old = {}
while True:
if not cpu_info_old:
cpu_info_old = self.read_cpu()
time.sleep(self.watch_time_step_)
cpu_info = self.read_cpu()

result = self.get_avg_cpu(cpu_info_old, cpu_info)
cpu_info_old = copy.deepcopy(cpu_info)

if __name__ == '__main__':
cpu_watcher = CpuWatch(time_step = 5)
cpu_watcher.run()

脚本可以直接在 我的github 进行下载。

其他

欢迎关注下我的其他脚本,平常可能会用到的一些脚本,整理了一下。

https://github.com/smaugx/dailytools

Blog:

2018-05-20 于杭州
By 史矛革