From 30f9999fc65052d54591ef606472114aef9e2a7d Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Wed, 23 Mar 2022 11:13:18 +0800
Subject: [PATCH 01/13] rename

---
 clients/client-linux.py  | 8 ++++++--
 clients/client-psutil.py | 8 ++++++--
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index bf71317..7c99180 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -210,7 +210,11 @@ def _net_speed():
             netSpeed["avgtx"] = avgtx
         time.sleep(INTERVAL)
 
-def get_realtime_date():
+def get_realtime_data():
+    '''
+    real time get system data
+    :return:
+    '''
     t1 = threading.Thread(
         target=_ping_thread,
         kwargs={
@@ -273,7 +277,7 @@ if __name__ == '__main__':
         elif 'INTERVAL' in argc:
             INTERVAL = int(argc.split('INTERVAL=')[-1])
     socket.setdefaulttimeout(30)
-    get_realtime_date()
+    get_realtime_data()
     while True:
         try:
             print("Connecting...")
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index aed5b2c..5b61d0d 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -190,7 +190,11 @@ def _net_speed():
         netSpeed["avgtx"] = avgtx
         time.sleep(INTERVAL)
 
-def get_realtime_date():
+def get_realtime_data():
+    '''
+    real time get system data
+    :return:
+    '''
     t1 = threading.Thread(
         target=_ping_thread,
         kwargs={
@@ -253,7 +257,7 @@ if __name__ == '__main__':
         elif 'INTERVAL' in argc:
             INTERVAL = int(argc.split('INTERVAL=')[-1])
     socket.setdefaulttimeout(30)
-    get_realtime_date()
+    get_realtime_data()
     while 1:
         try:
             print("Connecting...")

From d64beb7ba8806bfaf21f2cb92eb9c52b6c6a2384 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Wed, 23 Mar 2022 11:45:16 +0800
Subject: [PATCH 02/13] psutil add io total

---
 clients/client-linux.py  | 39 +++++++++++--------
 clients/client-psutil.py | 82 ++++++++++++++++++++++++++++++++--------
 2 files changed, 89 insertions(+), 32 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 7c99180..d96a670 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 # coding: utf-8
-# Update by : https://github.com/cppla/ServerStatus, Update date: 20211009
-# 版本:1.0.2, 支持Python版本:2.7 to 3.9
+# Update by : https://github.com/cppla/ServerStatus, Update date: 20220323
+# 版本:1.0.3, 支持Python版本:2.7 to 3.9
 # 支持操作系统: Linux, OSX, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures
 # 说明: 默认情况下修改server和user就可以了。丢包率监测方向可以自定义,例如:CU = "www.facebook.com"。
 
@@ -9,16 +9,15 @@ SERVER = "127.0.0.1"
 USER = "s01"
 
 
-
-PORT = 35601
 PASSWORD = "USER_DEFAULT_PASSWORD"
-INTERVAL = 1
-PROBEPORT = 80
-PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
-PING_PACKET_HISTORY_LEN = 100
+PORT = 35601
 CU = "cu.tz.cloudcpp.com"
 CT = "ct.tz.cloudcpp.com"
 CM = "cm.tz.cloudcpp.com"
+PROBEPORT = 80
+PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
+PING_PACKET_HISTORY_LEN = 100
+INTERVAL = 1
 
 import socket
 import time
@@ -147,6 +146,10 @@ netSpeed = {
     'avgrx': 0,
     'avgtx': 0
 }
+diskIO = {
+    'read': 0,
+    'write': 0
+}
 
 def _ping_thread(host, mark, port):
     lostPacket = 0
@@ -210,6 +213,10 @@ def _net_speed():
             netSpeed["avgtx"] = avgtx
         time.sleep(INTERVAL)
 
+def _disk_io():
+    diskIO["read"] = 0
+    diskIO["write"] = 0
+
 def get_realtime_data():
     '''
     real time get system data
@@ -242,14 +249,12 @@ def get_realtime_data():
     t4 = threading.Thread(
         target=_net_speed,
     )
-    t1.setDaemon(True)
-    t2.setDaemon(True)
-    t3.setDaemon(True)
-    t4.setDaemon(True)
-    t1.start()
-    t2.start()
-    t3.start()
-    t4.start()
+    t5 = threading.Thread(
+        target=_disk_io(),
+    )
+    for ti in [t1, t2, t3, t4, t5]:
+        ti.setDaemon(True)
+        ti.start()
 
 def byte_str(object):
     '''
@@ -347,6 +352,8 @@ if __name__ == '__main__':
                 array['time_189'] = pingTime.get('189')
                 array['time_10086'] = pingTime.get('10086')
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
+                array['io_read'] = diskIO.get("read")
+                array['io_write'] = diskIO.get("write")
 
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index 5b61d0d..62919b2 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -1,8 +1,8 @@
 #!/usr/bin/env python3
 # coding: utf-8
-# Update by : https://github.com/cppla/ServerStatus, Update date: 20211009
+# Update by : https://github.com/cppla/ServerStatus, Update date: 20220323
 # 依赖于psutil跨平台库
-# 版本:1.0.2, 支持Python版本:2.7 to 3.9
+# 版本:1.0.3, 支持Python版本:2.7 to 3.9
 # 支持操作系统: Linux, Windows, OSX, Sun Solaris, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures
 # 说明: 默认情况下修改server和user就可以了。丢包率监测方向可以自定义,例如:CU = "www.facebook.com"。
 
@@ -10,16 +10,15 @@ SERVER = "127.0.0.1"
 USER = "s01"
 
 
-
-PORT = 35601
 PASSWORD = "USER_DEFAULT_PASSWORD"
-INTERVAL = 1
-PROBEPORT = 80
-PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
-PING_PACKET_HISTORY_LEN = 100
+PORT = 35601
 CU = "cu.tz.cloudcpp.com"
 CT = "ct.tz.cloudcpp.com"
 CM = "cm.tz.cloudcpp.com"
+PROBEPORT = 80
+PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
+PING_PACKET_HISTORY_LEN = 100
+INTERVAL = 1
 
 import socket
 import time
@@ -131,6 +130,10 @@ netSpeed = {
     'avgrx': 0,
     'avgtx': 0
 }
+diskIO = {
+    'read': 0,
+    'write': 0
+}
 
 def _ping_thread(host, mark, port):
     lostPacket = 0
@@ -190,6 +193,53 @@ def _net_speed():
         netSpeed["avgtx"] = avgtx
         time.sleep(INTERVAL)
 
+def _disk_io():
+    """
+    the code is by: https://github.com/giampaolo/psutil/blob/master/scripts/iotop.py
+    good luck for opensource! modify: cpp.la
+    Calculate IO usage by comparing IO statics before and
+        after the interval.
+        Return a tuple including all currently running processes
+        sorted by IO activity and total disks I/O activity.
+    """
+    while True:
+        # first get a list of all processes and disk io counters
+        procs = [p for p in psutil.process_iter()]
+        for p in procs[:]:
+            try:
+                p._before = p.io_counters()
+            except psutil.Error:
+                procs.remove(p)
+                continue
+        disks_before = psutil.disk_io_counters()
+
+        # sleep some time, only when INTERVAL==1 , io read/write per_sec.
+        # when INTERVAL > 1, io read/write per_INTERVAL
+        time.sleep(INTERVAL)
+
+        # then retrieve the same info again
+        for p in procs[:]:
+            with p.oneshot():
+                try:
+                    p._after = p.io_counters()
+                    p._cmdline = ' '.join(p.cmdline())
+                    if not p._cmdline:
+                        p._cmdline = p.name()
+                    p._username = p.username()
+                except (psutil.NoSuchProcess, psutil.ZombieProcess):
+                    procs.remove(p)
+        disks_after = psutil.disk_io_counters()
+
+        # finally calculate results by comparing data before and
+        # after the interval
+        for p in procs:
+            p._read_per_sec = p._after.read_bytes - p._before.read_bytes
+            p._write_per_sec = p._after.write_bytes - p._before.write_bytes
+            p._total = p._read_per_sec + p._write_per_sec
+
+        diskIO["read"] = disks_after.read_bytes - disks_before.read_bytes
+        diskIO["write"] = disks_after.write_bytes - disks_before.write_bytes
+
 def get_realtime_data():
     '''
     real time get system data
@@ -222,14 +272,12 @@ def get_realtime_data():
     t4 = threading.Thread(
         target=_net_speed,
     )
-    t1.setDaemon(True)
-    t2.setDaemon(True)
-    t3.setDaemon(True)
-    t4.setDaemon(True)
-    t1.start()
-    t2.start()
-    t3.start()
-    t4.start()
+    t5 = threading.Thread(
+        target=_disk_io(),
+    )
+    for ti in [t1, t2, t3, t4, t5]:
+        ti.setDaemon(True)
+        ti.start()
 
 def byte_str(object):
     '''
@@ -328,6 +376,8 @@ if __name__ == '__main__':
                 array['time_189'] = pingTime.get('189')
                 array['time_10086'] = pingTime.get('10086')
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
+                array['io_read'] = diskIO.get("read")
+                array['io_write'] = diskIO.get("write")
 
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:

From d8c8d8fd3f59f5b92cda271b3cb1f0a099b5498f Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Thu, 24 Mar 2022 17:24:01 +0800
Subject: [PATCH 03/13] add todo

---
 clients/client-linux.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index d96a670..39a2730 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -214,6 +214,7 @@ def _net_speed():
         time.sleep(INTERVAL)
 
 def _disk_io():
+    # todo, 这里希望能有人贡献下代码
     diskIO["read"] = 0
     diskIO["write"] = 0
 

From 2d457c66ad50754d7df625765d9f439098c9a034 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Thu, 31 Mar 2022 19:48:26 +0800
Subject: [PATCH 04/13] push disk io for client-linux.py

---
 clients/client-linux.py  | 60 ++++++++++++++++++++++++++++++++++++++--
 clients/client-psutil.py |  4 +++
 2 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 39a2730..81bbcfb 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -214,9 +214,63 @@ def _net_speed():
         time.sleep(INTERVAL)
 
 def _disk_io():
-    # todo, 这里希望能有人贡献下代码
-    diskIO["read"] = 0
-    diskIO["write"] = 0
+    '''
+    磁盘IO:因为IOPS原因,SSD和HDD、包括RAID卡,ZFS等阵列技术。IO对性能的影响还需要结合自身服务器情况来判断。
+    比如我这里是机械硬盘,大量做随机小文件读写,那么很低的读写也就能造成硬盘长时间的等待。
+    如果这里做连续性IO,那么普通机械硬盘写入到100Mb/s,那么也能造成硬盘长时间的等待。
+    磁盘读写有误差:4k,8k ,https://stackoverflow.com/questions/34413926/psutil-vs-dd-monitoring-disk-i-o
+    :return:
+    '''
+    # pre pid snapshot
+    snapshot_first = {}
+    # next pid snapshot
+    snapshot_second = {}
+    # read count snapshot
+    snapshot_read = 0
+    # write count snapshot
+    snapshot_write = 0
+    # process snapshot
+    pid_snapshot = [str(i) for i in os.listdir("/proc") if i.isdigit() is True]
+    for pid in pid_snapshot:
+        try:
+            with open("/proc/{}/io".format(pid)) as f:
+                pid_io = {}
+                for line in f.readlines():
+                    if "read_bytes" in line:
+                        pid_io["read"] = int(line.split("read_bytes:")[-1].strip())
+                    elif "write_bytes" in line and "cancelled_write_bytes" not in line:
+                        pid_io["write"] = int(line.split("write_bytes:")[-1].strip())
+                pid_io["name"] = open("/proc/{}/comm".format(pid), "r").read().strip()
+                snapshot_first[pid] = pid_io
+        except:
+            if pid in snapshot_first:
+                snapshot_first.pop(pid)
+
+    time.sleep(INTERVAL)
+
+    for pid in pid_snapshot:
+        try:
+            with open("/proc/{}/io".format(pid)) as f:
+                pid_io = {}
+                for line in f.readlines():
+                    if "read_bytes" in line:
+                        pid_io["read"] = int(line.split("read_bytes:")[-1].strip())
+                    elif "write_bytes" in line and "cancelled_write_bytes" not in line:
+                        pid_io["write"] = int(line.split("write_bytes:")[-1].strip())
+                pid_io["name"] = open("/proc/{}/comm".format(pid), "r").read().strip()
+                snapshot_second[pid] = pid_io
+        except:
+            if pid in snapshot_first:
+                snapshot_first.pop(pid)
+            if pid in snapshot_second:
+                snapshot_second.pop(pid)
+
+    for k, v in snapshot_first.items():
+        if snapshot_first[k]["name"] == snapshot_second[k]["name"] and snapshot_first[k]["name"] != "bash":
+            snapshot_read += (snapshot_second[k]["read"] - snapshot_first[k]["read"])
+            snapshot_write += (snapshot_second[k]["write"] - snapshot_first[k]["write"])
+    diskIO["read"] = snapshot_read
+    diskIO["write"] = snapshot_write
 
 def get_realtime_data():
     '''
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index 62919b2..f8e88fa 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -201,6 +201,10 @@ def _disk_io():
         after the interval.
         Return a tuple including all currently running processes
         sorted by IO activity and total disks I/O activity.
+    磁盘IO:因为IOPS原因,SSD和HDD、包括RAID卡,ZFS等。IO对性能的影响还需要结合自身服务器情况来判断。
+    比如我这里是机械硬盘,大量做随机小文件读写,那么很低的读写也就能造成硬盘长时间的等待。
+    如果这里做连续性IO,那么普通机械硬盘写入到100Mb/s,那么也能造成硬盘长时间的等待。
+    磁盘读写有误差:4k,8k ,https://stackoverflow.com/questions/34413926/psutil-vs-dd-monitoring-disk-i-o
     """
     while True:
         # first get a list of all processes and disk io counters

From 7698ce0e4bda998b12db403de2b24072328942a4 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Thu, 31 Mar 2022 19:53:17 +0800
Subject: [PATCH 05/13] =?UTF-8?q?=E5=BC=80=E6=BA=90=E8=AE=A9=E7=BC=96?=
 =?UTF-8?q?=E7=A8=8B=E6=9B=B4=E5=8A=A0=E7=BE=8E=E5=A5=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 clients/client-linux.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 81bbcfb..0c858aa 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -215,6 +215,7 @@ def _net_speed():
 
 def _disk_io():
     '''
+    good luck for opensource! by: cpp.la
     磁盘IO:因为IOPS原因,SSD和HDD、包括RAID卡,ZFS等阵列技术。IO对性能的影响还需要结合自身服务器情况来判断。
     比如我这里是机械硬盘,大量做随机小文件读写,那么很低的读写也就能造成硬盘长时间的等待。
     如果这里做连续性IO,那么普通机械硬盘写入到100Mb/s,那么也能造成硬盘长时间的等待。

From 1f47bcdb6f89c10393adc2ff40ad6aae7aafd029 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 14:55:19 +0800
Subject: [PATCH 06/13] beta

---
 server/src/main.cpp    | 19 ++++++++++++-------
 server/src/main.h      |  2 ++
 web/index.html         |  1 +
 web/js/serverstatus.js |  3 +++
 4 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/server/src/main.cpp b/server/src/main.cpp
index bcf3575..cbd2052 100644
--- a/server/src/main.cpp
+++ b/server/src/main.cpp
@@ -178,6 +178,10 @@ int CMain::HandleMessage(int ClientNetID, char *pMessage)
 			pClient->m_Stats.m_HDDTotal = rStart["hdd_total"].u.integer;
 		if(rStart["hdd_used"].type)
 			pClient->m_Stats.m_HDDUsed = rStart["hdd_used"].u.integer;
+		if(rStart["io_read"].type)
+			pClient->m_Stats.m_IORead = rStart["io_read"].u.integer;
+		if(rStart["io_write"].type)
+			pClient->m_Stats.m_IOWrite = rStart["io_write"].u.integer;
 		if(rStart["cpu"].type)
 			pClient->m_Stats.m_CPU = rStart["cpu"].u.dbl;
 		if(rStart["online4"].type && pClient->m_ClientNetType == NETTYPE_IPV6)
@@ -190,19 +194,19 @@ int CMain::HandleMessage(int ClientNetID, char *pMessage)
 		if(m_Config.m_Verbose)
 		{
 			if(rStart["online4"].type)
-				dbg_msg("main", "Online4: %s\nUptime: %" PRId64 "\nLoad_1: %f\nLoad_5: %f\nLoad_15: %f\nPing_10010: %f\nPing_189: %f\nPing_10086: %f\nTime_10010: %" PRId64 "\nTime_189: %" PRId64 "\nTime_10086: %" PRId64 "\nTcp_count: %" PRId64 "\nUdp_count: %" PRId64 "\nprocess_count: %" PRId64 "\nthread_count: %" PRId64 "\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n",
+				dbg_msg("main", "Online4: %s\nUptime: %" PRId64 "\nLoad_1: %f\nLoad_5: %f\nLoad_15: %f\nPing_10010: %f\nPing_189: %f\nPing_10086: %f\nTime_10010: %" PRId64 "\nTime_189: %" PRId64 "\nTime_10086: %" PRId64 "\nTcp_count: %" PRId64 "\nUdp_count: %" PRId64 "\nprocess_count: %" PRId64 "\nthread_count: %" PRId64 "\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\nIORead: %" PRId64 "\nIOWrite: %" PRId64 "\n",
 					rStart["online4"].u.boolean ? "true" : "false",
 					pClient->m_Stats.m_Uptime,
-					pClient->m_Stats.m_Load_1, pClient->m_Stats.m_Load_5, pClient->m_Stats.m_Load_15, pClient->m_Stats.m_ping_10010, pClient->m_Stats.m_ping_189, pClient->m_Stats.m_ping_10086, pClient->m_Stats.m_time_10010, pClient->m_Stats.m_time_189, pClient->m_Stats.m_time_10086,pClient->m_Stats.m_tcpCount,pClient->m_Stats.m_udpCount,pClient->m_Stats.m_processCount,pClient->m_Stats.m_threadCount,pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU);
+					pClient->m_Stats.m_Load_1, pClient->m_Stats.m_Load_5, pClient->m_Stats.m_Load_15, pClient->m_Stats.m_ping_10010, pClient->m_Stats.m_ping_189, pClient->m_Stats.m_ping_10086, pClient->m_Stats.m_time_10010, pClient->m_Stats.m_time_189, pClient->m_Stats.m_time_10086,pClient->m_Stats.m_tcpCount,pClient->m_Stats.m_udpCount,pClient->m_Stats.m_processCount,pClient->m_Stats.m_threadCount,pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU, pClient->m_Stats.m_IORead, pClient->m_Stats.m_IOWrite);
 			else if(rStart["online6"].type)
-				dbg_msg("main", "Online6: %s\nUptime: %" PRId64 "\nLoad_1: %f\nLoad_5: %f\nLoad_15: %f\nPing_10010: %f\nPing_189: %f\nPing_10086: %f\nTime_10010: %" PRId64 "\nTime_189: %" PRId64 "\nTime_10086: %" PRId64 "\nTcp_count: %" PRId64 "\nUdp_count: %" PRId64 "\nprocess_count: %" PRId64 "\nthread_count: %" PRId64 "\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n",
+				dbg_msg("main", "Online6: %s\nUptime: %" PRId64 "\nLoad_1: %f\nLoad_5: %f\nLoad_15: %f\nPing_10010: %f\nPing_189: %f\nPing_10086: %f\nTime_10010: %" PRId64 "\nTime_189: %" PRId64 "\nTime_10086: %" PRId64 "\nTcp_count: %" PRId64 "\nUdp_count: %" PRId64 "\nprocess_count: %" PRId64 "\nthread_count: %" PRId64 "\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\nIORead: %" PRId64 "\nIOWrite: %" PRId64 "\n",
 					rStart["online6"].u.boolean ? "true" : "false",
 					pClient->m_Stats.m_Uptime,
-					pClient->m_Stats.m_Load_1, pClient->m_Stats.m_Load_5, pClient->m_Stats.m_Load_15, pClient->m_Stats.m_ping_10010, pClient->m_Stats.m_ping_189, pClient->m_Stats.m_ping_10086, pClient->m_Stats.m_time_10010, pClient->m_Stats.m_time_189, pClient->m_Stats.m_time_10086,pClient->m_Stats.m_tcpCount,pClient->m_Stats.m_udpCount,pClient->m_Stats.m_processCount,pClient->m_Stats.m_threadCount,pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU);
+					pClient->m_Stats.m_Load_1, pClient->m_Stats.m_Load_5, pClient->m_Stats.m_Load_15, pClient->m_Stats.m_ping_10010, pClient->m_Stats.m_ping_189, pClient->m_Stats.m_ping_10086, pClient->m_Stats.m_time_10010, pClient->m_Stats.m_time_189, pClient->m_Stats.m_time_10086,pClient->m_Stats.m_tcpCount,pClient->m_Stats.m_udpCount,pClient->m_Stats.m_processCount,pClient->m_Stats.m_threadCount,pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU, pClient->m_Stats.m_IORead, pClient->m_Stats.m_IOWrite);
 			else
-				dbg_msg("main", "Uptime: %" PRId64 "\nLoad_1: %f\nLoad_5: %f\nLoad_15: %f\nPing_10010: %f\nPing_189: %f\nPing_10086: %f\nTime_10010: %" PRId64 "\nTime_189: %" PRId64 "\nTime_10086: %" PRId64 "\nTcp_count: %" PRId64 "\nUdp_count: %" PRId64 "\nprocess_count: %" PRId64 "\nthread_count: %" PRId64 "\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n",
+				dbg_msg("main", "Uptime: %" PRId64 "\nLoad_1: %f\nLoad_5: %f\nLoad_15: %f\nPing_10010: %f\nPing_189: %f\nPing_10086: %f\nTime_10010: %" PRId64 "\nTime_189: %" PRId64 "\nTime_10086: %" PRId64 "\nTcp_count: %" PRId64 "\nUdp_count: %" PRId64 "\nprocess_count: %" PRId64 "\nthread_count: %" PRId64 "\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\nIORead: %" PRId64 "\nIOWrite: %" PRId64 "\n",
 					pClient->m_Stats.m_Uptime,
-					pClient->m_Stats.m_Load_1, pClient->m_Stats.m_Load_5, pClient->m_Stats.m_Load_15, pClient->m_Stats.m_ping_10010, pClient->m_Stats.m_ping_189, pClient->m_Stats.m_ping_10086, pClient->m_Stats.m_time_10010, pClient->m_Stats.m_time_189, pClient->m_Stats.m_time_10086,pClient->m_Stats.m_tcpCount,pClient->m_Stats.m_udpCount,pClient->m_Stats.m_processCount,pClient->m_Stats.m_threadCount,pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU);
+					pClient->m_Stats.m_Load_1, pClient->m_Stats.m_Load_5, pClient->m_Stats.m_Load_15, pClient->m_Stats.m_ping_10010, pClient->m_Stats.m_ping_189, pClient->m_Stats.m_ping_10086, pClient->m_Stats.m_time_10010, pClient->m_Stats.m_time_189, pClient->m_Stats.m_time_10086,pClient->m_Stats.m_tcpCount,pClient->m_Stats.m_udpCount,pClient->m_Stats.m_processCount,pClient->m_Stats.m_threadCount,pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU, pClient->m_Stats.m_IORead, pClient->m_Stats.m_IOWrite);
 		}
 
 		// clean up
@@ -274,7 +278,7 @@ void CMain::JSONUpdateThread(void *pUser)
                 }
 
 				str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf),
-				 "{ \"name\": \"%s\",\"type\": \"%s\",\"host\": \"%s\",\"location\": \"%s\",\"online4\": %s, \"online6\": %s, \"uptime\": \"%s\",\"load_1\": %.2f, \"load_5\": %.2f, \"load_15\": %.2f,\"ping_10010\": %.2f, \"ping_189\": %.2f, \"ping_10086\": %.2f,\"time_10010\": %" PRId64 ", \"time_189\": %" PRId64 ", \"time_10086\": %" PRId64 ", \"tcp_count\": %" PRId64 ", \"udp_count\": %" PRId64 ", \"process_count\": %" PRId64 ", \"thread_count\": %" PRId64 ", \"network_rx\": %" PRId64 ", \"network_tx\": %" PRId64 ", \"network_in\": %" PRId64 ", \"network_out\": %" PRId64 ", \"cpu\": %d, \"memory_total\": %" PRId64 ", \"memory_used\": %" PRId64 ", \"swap_total\": %" PRId64 ", \"swap_used\": %" PRId64 ", \"hdd_total\": %" PRId64 ", \"hdd_used\": %" PRId64 ", \"last_network_in\": %" PRId64 ", \"last_network_out\": %" PRId64 ",\"custom\": \"%s\" },\n",
+				 "{ \"name\": \"%s\",\"type\": \"%s\",\"host\": \"%s\",\"location\": \"%s\",\"online4\": %s, \"online6\": %s, \"uptime\": \"%s\",\"load_1\": %.2f, \"load_5\": %.2f, \"load_15\": %.2f,\"ping_10010\": %.2f, \"ping_189\": %.2f, \"ping_10086\": %.2f,\"time_10010\": %" PRId64 ", \"time_189\": %" PRId64 ", \"time_10086\": %" PRId64 ", \"tcp_count\": %" PRId64 ", \"udp_count\": %" PRId64 ", \"process_count\": %" PRId64 ", \"thread_count\": %" PRId64 ", \"network_rx\": %" PRId64 ", \"network_tx\": %" PRId64 ", \"network_in\": %" PRId64 ", \"network_out\": %" PRId64 ", \"cpu\": %d, \"memory_total\": %" PRId64 ", \"memory_used\": %" PRId64 ", \"swap_total\": %" PRId64 ", \"swap_used\": %" PRId64 ", \"hdd_total\": %" PRId64 ", \"hdd_used\": %" PRId64 ", \"last_network_in\": %" PRId64 ", \"last_network_out\": %" PRId64 ",\"io_read\": %" PRId64 ", \"io_write\": %" PRId64 ",\"custom\": \"%s\" },\n",
 					pClients[i].m_aName,pClients[i].m_aType,pClients[i].m_aHost,pClients[i].m_aLocation,
 					pClients[i].m_Stats.m_Online4 ? "true" : "false",pClients[i].m_Stats.m_Online6 ? "true" : "false",
 					aUptime, pClients[i].m_Stats.m_Load_1, pClients[i].m_Stats.m_Load_5, pClients[i].m_Stats.m_Load_15, pClients[i].m_Stats.m_ping_10010, pClients[i].m_Stats.m_ping_189, pClients[i].m_Stats.m_ping_10086,
@@ -283,6 +287,7 @@ void CMain::JSONUpdateThread(void *pUser)
 					pClients[i].m_Stats.m_SwapTotal, pClients[i].m_Stats.m_SwapUsed, pClients[i].m_Stats.m_HDDTotal, pClients[i].m_Stats.m_HDDUsed,
 					pClients[i].m_Stats.m_NetworkIN == 0 || pClients[i].m_LastNetworkIN == 0 ? pClients[i].m_Stats.m_NetworkIN : pClients[i].m_LastNetworkIN,
 					pClients[i].m_Stats.m_NetworkOUT == 0 || pClients[i].m_LastNetworkOUT == 0 ? pClients[i].m_Stats.m_NetworkOUT : pClients[i].m_LastNetworkOUT,
+					pClients[i].m_Stats.m_IORead, pClients[i].m_Stats.m_IOWrite,
 					pClients[i].m_Stats.m_aCustom);
 				pBuf += strlen(pBuf);
 			}
diff --git a/server/src/main.h b/server/src/main.h
index 36596f1..2993809 100644
--- a/server/src/main.h
+++ b/server/src/main.h
@@ -73,6 +73,8 @@ class CMain
 			int64_t m_udpCount;
 			int64_t m_processCount;
 			int64_t m_threadCount;
+			int64_t m_IORead;
+			int64_t m_IOWrite;
 			double m_CPU;
 			char m_aCustom[512];
 			// Options
diff --git a/web/index.html b/web/index.html
index a96d554..ebb389c 100644
--- a/web/index.html
+++ b/web/index.html
@@ -87,6 +87,7 @@
 					<th id="cpu">核芯</th>
 					<th id="ram">内存</th>
 					<th id="hdd">硬盘</th>
+					<th id="io">IO(r/w)</th>
 					<th id="ping">联通|电信|移动</th>
 				</tr>
 				</thead>
diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index d7b6556..c5a4782 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -238,6 +238,9 @@ function uptime() {
 				TableRow.children["hdd"].children[0].children[0].innerHTML = HDD + "%";
 				ExpandRow[0].children["expand_hdd"].innerHTML = "硬盘: " + bytesToSize(result.servers[i].hdd_used*1024*1024, 2) + " / " + bytesToSize(result.servers[i].hdd_total*1024*1024, 2);
 
+				//IO
+				TableRow.children["io"].innerHTML = bytesToSize(result.servers[i].io_read, 2) + "💿" + bytesToSize(result.servers[i].io_write, 2);
+
                 // delay time
 
 				// tcp, udp, process, thread count

From 5176cb0340ea94fb61bfd4780aeeedaf60ee899e Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 15:19:06 +0800
Subject: [PATCH 07/13] table control

---
 web/css/dark.css       | 10 +++++-----
 web/css/light.css      | 10 +++++-----
 web/js/serverstatus.js |  4 ++++
 3 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/web/css/dark.css b/web/css/dark.css
index ca4b0a0..2211a84 100644
--- a/web/css/dark.css
+++ b/web/css/dark.css
@@ -26,7 +26,7 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 	#type, tr td:nth-child(4)			{ display:none; visibility:hidden; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 720px) {
 	body								{ font-size: 10px; }
@@ -34,7 +34,7 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 	#type, tr td:nth-child(4)			{ display:none; visibility:hidden; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 620px) {
 	body								{ font-size: 10px; }
@@ -44,7 +44,7 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
 	#traffic, tr td:nth-child(9)		{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 533px) {
 	body								{ font-size: 10px; }
@@ -54,7 +54,7 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
 	#traffic, tr td:nth-child(9)		{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 450px) {
 	body								{ font-size: 10px; }
@@ -66,5 +66,5 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
 	#traffic, tr td:nth-child(9)		{ display:none; visibility:hidden; }
 	#cpu, #ram, #hdd 					{ min-width: 25px; max-width: 50px; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
diff --git a/web/css/light.css b/web/css/light.css
index cde5cb6..3d6e11d 100644
--- a/web/css/light.css
+++ b/web/css/light.css
@@ -23,7 +23,7 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 	#type, tr td:nth-child(4)			{ display:none; visibility:hidden; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 720px) {
 	body								{ font-size: 10px; }
@@ -31,7 +31,7 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 	#type, tr td:nth-child(4)			{ display:none; visibility:hidden; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 620px) {
 	body								{ font-size: 10px; }
@@ -41,7 +41,7 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
 	#traffic, tr td:nth-child(9)		{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 533px) {
 	body								{ font-size: 10px; }
@@ -51,7 +51,7 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 	#location, tr td:nth-child(5)		{ display:none; visibility:hidden; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
 	#traffic, tr td:nth-child(9)		{ display:none; visibility:hidden; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 450px) {
 	body								{ font-size: 10px; }
@@ -63,5 +63,5 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 	#uptime, tr td:nth-child(6)			{ display:none; visibility:hidden; }
 	#traffic, tr td:nth-child(9)		{ display:none; visibility:hidden; }
 	#cpu, #ram, #hdd 					{ min-width: 25px; max-width: 50px; }
-	#ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+	#ping, tr td:nth-child(14)          { display:none; visibility:hidden; }
 }
diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index c5a4782..21b1734 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -74,6 +74,7 @@ function uptime() {
 						"<td id=\"cpu\"><div class=\"progress\"><div style=\"width: 100%;\" class=\"progress-bar progress-bar-warning\"><small>加载中</small></div></div></td>" +
 						"<td id=\"memory\"><div class=\"progress\"><div style=\"width: 100%;\" class=\"progress-bar progress-bar-warning\"><small>加载中</small></div></div></td>" +
 						"<td id=\"hdd\"><div class=\"progress\"><div style=\"width: 100%;\" class=\"progress-bar progress-bar-warning\"><small>加载中</small></div></div></td>" +
+						"<td id=\"io\">加载中</td>" +
 						"<td id=\"ping\"><div class=\"progress\"><div style=\"width: 100%;\" class=\"progress-bar progress-bar-warning\"><small>加载中</small></div></div></td>" +
 					"</tr>" +
 					"<tr class=\"expandRow " + hack + "\"><td colspan=\"16\"><div class=\"accordian-body collapse\" id=\"rt" + i + "\">" +
@@ -135,6 +136,7 @@ function uptime() {
 					TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-danger";
 					TableRow.children["hdd"].children[0].children[0].style.width = "100%";
 					TableRow.children["hdd"].children[0].children[0].innerHTML = "<small>关闭</small>";
+					TableRow.children["io"].innerHTML = "–";
 					TableRow.children["ping"].children[0].children[0].className = "progress-bar progress-bar-danger";
 					TableRow.children["ping"].children[0].children[0].style.width = "100%";
 					TableRow.children["ping"].children[0].children[0].innerHTML = "<small>关闭</small>";
@@ -294,6 +296,8 @@ function uptime() {
 				TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-error";
 				TableRow.children["hdd"].children[0].children[0].style.width = "100%";
 				TableRow.children["hdd"].children[0].children[0].innerHTML = "<small>错误</small>";
+				TableRow.children["io"].children[0].children[0].className = "progress-bar progress-bar-error";
+				TableRow.children["io"].children[0].children[0].innerHTML = "<small>错误</small>";
 				TableRow.children["ping"].children[0].children[0].className = "progress-bar progress-bar-error";
 				TableRow.children["ping"].children[0].children[0].style.width = "100%";
 				TableRow.children["ping"].children[0].children[0].innerHTML = "<small>错误</small>";

From 47cf7a181838eb26a6fb9fe79de767d938d2cab2 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 15:37:43 +0800
Subject: [PATCH 08/13] =?UTF-8?q?=E6=8C=81=E7=BB=AD=E8=8E=B7=E5=8F=96io?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 clients/client-linux.py | 95 +++++++++++++++++++++--------------------
 1 file changed, 48 insertions(+), 47 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 0c858aa..301231e 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -222,56 +222,57 @@ def _disk_io():
     磁盘读写有误差:4k,8k ,https://stackoverflow.com/questions/34413926/psutil-vs-dd-monitoring-disk-i-o
     :return:
     '''
-    # pre pid snapshot
-    snapshot_first = {}
-    # next pid snapshot
-    snapshot_second = {}
-    # read count snapshot
-    snapshot_read = 0
-    # write count snapshot
-    snapshot_write = 0
-    # process snapshot
-    pid_snapshot = [str(i) for i in os.listdir("/proc") if i.isdigit() is True]
-    for pid in pid_snapshot:
-        try:
-            with open("/proc/{}/io".format(pid)) as f:
-                pid_io = {}
-                for line in f.readlines():
-                    if "read_bytes" in line:
-                        pid_io["read"] = int(line.split("read_bytes:")[-1].strip())
-                    elif "write_bytes" in line and "cancelled_write_bytes" not in line:
-                        pid_io["write"] = int(line.split("write_bytes:")[-1].strip())
-                pid_io["name"] = open("/proc/{}/comm".format(pid), "r").read().strip()
-                snapshot_first[pid] = pid_io
-        except:
-            if pid in snapshot_first:
-                snapshot_first.pop(pid)
+    while True:
+        # pre pid snapshot
+        snapshot_first = {}
+        # next pid snapshot
+        snapshot_second = {}
+        # read count snapshot
+        snapshot_read = 0
+        # write count snapshot
+        snapshot_write = 0
+        # process snapshot
+        pid_snapshot = [str(i) for i in os.listdir("/proc") if i.isdigit() is True]
+        for pid in pid_snapshot:
+            try:
+                with open("/proc/{}/io".format(pid)) as f:
+                    pid_io = {}
+                    for line in f.readlines():
+                        if "read_bytes" in line:
+                            pid_io["read"] = int(line.split("read_bytes:")[-1].strip())
+                        elif "write_bytes" in line and "cancelled_write_bytes" not in line:
+                            pid_io["write"] = int(line.split("write_bytes:")[-1].strip())
+                    pid_io["name"] = open("/proc/{}/comm".format(pid), "r").read().strip()
+                    snapshot_first[pid] = pid_io
+            except:
+                if pid in snapshot_first:
+                    snapshot_first.pop(pid)
 
-    time.sleep(INTERVAL)
+        time.sleep(INTERVAL)
 
-    for pid in pid_snapshot:
-        try:
-            with open("/proc/{}/io".format(pid)) as f:
-                pid_io = {}
-                for line in f.readlines():
-                    if "read_bytes" in line:
-                        pid_io["read"] = int(line.split("read_bytes:")[-1].strip())
-                    elif "write_bytes" in line and "cancelled_write_bytes" not in line:
-                        pid_io["write"] = int(line.split("write_bytes:")[-1].strip())
-                pid_io["name"] = open("/proc/{}/comm".format(pid), "r").read().strip()
-                snapshot_second[pid] = pid_io
-        except:
-            if pid in snapshot_first:
-                snapshot_first.pop(pid)
-            if pid in snapshot_second:
-                snapshot_second.pop(pid)
+        for pid in pid_snapshot:
+            try:
+                with open("/proc/{}/io".format(pid)) as f:
+                    pid_io = {}
+                    for line in f.readlines():
+                        if "read_bytes" in line:
+                            pid_io["read"] = int(line.split("read_bytes:")[-1].strip())
+                        elif "write_bytes" in line and "cancelled_write_bytes" not in line:
+                            pid_io["write"] = int(line.split("write_bytes:")[-1].strip())
+                    pid_io["name"] = open("/proc/{}/comm".format(pid), "r").read().strip()
+                    snapshot_second[pid] = pid_io
+            except:
+                if pid in snapshot_first:
+                    snapshot_first.pop(pid)
+                if pid in snapshot_second:
+                    snapshot_second.pop(pid)
 
-    for k, v in snapshot_first.items():
-        if snapshot_first[k]["name"] == snapshot_second[k]["name"] and snapshot_first[k]["name"] != "bash":
-            snapshot_read += (snapshot_second[k]["read"] - snapshot_first[k]["read"])
-            snapshot_write += (snapshot_second[k]["write"] - snapshot_first[k]["write"])
-    diskIO["read"] = snapshot_read
-    diskIO["write"] = snapshot_write
+        for k, v in snapshot_first.items():
+            if snapshot_first[k]["name"] == snapshot_second[k]["name"] and snapshot_first[k]["name"] != "bash":
+                snapshot_read += (snapshot_second[k]["read"] - snapshot_first[k]["read"])
+                snapshot_write += (snapshot_second[k]["write"] - snapshot_first[k]["write"])
+        diskIO["read"] = snapshot_read
+        diskIO["write"] = snapshot_write
 
 def get_realtime_data():
     '''

From 654ecd7a3e266cbb64f001fef6915a28ac8c8af6 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 15:50:09 +0800
Subject: [PATCH 09/13] update disk io style

---
 clients/client-linux.py  |  2 +-
 clients/client-psutil.py |  2 +-
 web/css/dark.css         |  1 +
 web/css/light.css        |  1 +
 web/js/serverstatus.js   | 14 +++++++++++---
 5 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 301231e..235c734 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -307,7 +307,7 @@ def get_realtime_data():
         target=_net_speed,
     )
     t5 = threading.Thread(
-        target=_disk_io(),
+        target=_disk_io,
     )
     for ti in [t1, t2, t3, t4, t5]:
         ti.setDaemon(True)
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index f8e88fa..5f1b58b 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -277,7 +277,7 @@ def get_realtime_data():
         target=_net_speed,
     )
     t5 = threading.Thread(
-        target=_disk_io(),
+        target=_disk_io,
     )
     for ti in [t1, t2, t3, t4, t5]:
         ti.setDaemon(True)
diff --git a/web/css/dark.css b/web/css/dark.css
index 2211a84..ac3c35b 100644
--- a/web/css/dark.css
+++ b/web/css/dark.css
@@ -20,6 +20,7 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 115px; }
 #cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
+#io { min-width: 55px; }
 #ping { max-width: 95px; }
 
 @media only screen and (max-width: 1080px) {
diff --git a/web/css/light.css b/web/css/light.css
index 3d6e11d..9765fa3 100644
--- a/web/css/light.css
+++ b/web/css/light.css
@@ -17,6 +17,7 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 115px; }
 #cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
+#io { min-width: 55px; }
 #ping { max-width: 95px; }
 
 @media only screen and (max-width: 1080px) {
diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index 21b1734..db7fb8e 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -20,16 +20,24 @@ function bytesToSize(bytes, precision, si)
 	var ret;
 	si = typeof si !== 'undefined' ? si : 0;
 	if(si != 0) {
-		var megabyte = 1000 * 1000;
+		var kilobyte = 1000;
+		var megabyte = kilobyte * 1000;
 		var gigabyte = megabyte * 1000;
 		var terabyte = gigabyte * 1000;
 	} else {
-		var megabyte = 1024 * 1024;
+		var kilobyte = 1024;
+		var megabyte = kilobyte * 1024;
 		var gigabyte = megabyte * 1024;
 		var terabyte = gigabyte * 1024;
 	}
 
-	if ((bytes >= megabyte) && (bytes < gigabyte)) {
+	if ((bytes >= 0) && (bytes < kilobyte)) {
+		return bytes + ' B';
+
+	} else if ((bytes >= kilobyte) && (bytes < megabyte)) {
+		ret = (bytes / kilobyte).toFixed(precision) + ' K';
+
+	} else if ((bytes >= megabyte) && (bytes < gigabyte)) {
 		ret = (bytes / megabyte).toFixed(precision) + ' M';
 
 	} else if ((bytes >= gigabyte) && (bytes < terabyte)) {

From 6a15832966b9bc5fbcddbc79f39785733fe25866 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 16:04:08 +0800
Subject: [PATCH 10/13] bug test

---
 web/css/dark.css       |  2 +-
 web/css/light.css      |  2 +-
 web/js/serverstatus.js | 28 +++++++++++++++-------------
 3 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/web/css/dark.css b/web/css/dark.css
index ac3c35b..13652fa 100644
--- a/web/css/dark.css
+++ b/web/css/dark.css
@@ -20,7 +20,7 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 115px; }
 #cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
-#io { min-width: 55px; }
+#io { width: 90px; }
 #ping { max-width: 95px; }
 
 @media only screen and (max-width: 1080px) {
diff --git a/web/css/light.css b/web/css/light.css
index 9765fa3..ba672c6 100644
--- a/web/css/light.css
+++ b/web/css/light.css
@@ -17,7 +17,7 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 115px; }
 #cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
-#io { min-width: 55px; }
+#io { width: 90px; }
 #ping { max-width: 95px; }
 
 @media only screen and (max-width: 1080px) {
diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index db7fb8e..da99f85 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -20,24 +20,16 @@ function bytesToSize(bytes, precision, si)
 	var ret;
 	si = typeof si !== 'undefined' ? si : 0;
 	if(si != 0) {
-		var kilobyte = 1000;
-		var megabyte = kilobyte * 1000;
+		var megabyte = 1000 * 1000;
 		var gigabyte = megabyte * 1000;
 		var terabyte = gigabyte * 1000;
 	} else {
-		var kilobyte = 1024;
-		var megabyte = kilobyte * 1024;
+		var megabyte = 1024 * 1024;
 		var gigabyte = megabyte * 1024;
 		var terabyte = gigabyte * 1024;
 	}
 
-	if ((bytes >= 0) && (bytes < kilobyte)) {
-		return bytes + ' B';
-
-	} else if ((bytes >= kilobyte) && (bytes < megabyte)) {
-		ret = (bytes / kilobyte).toFixed(precision) + ' K';
-
-	} else if ((bytes >= megabyte) && (bytes < gigabyte)) {
+	if ((bytes >= megabyte) && (bytes < gigabyte)) {
 		ret = (bytes / megabyte).toFixed(precision) + ' M';
 
 	} else if ((bytes >= gigabyte) && (bytes < terabyte)) {
@@ -248,8 +240,18 @@ function uptime() {
 				TableRow.children["hdd"].children[0].children[0].innerHTML = HDD + "%";
 				ExpandRow[0].children["expand_hdd"].innerHTML = "硬盘: " + bytesToSize(result.servers[i].hdd_used*1024*1024, 2) + " / " + bytesToSize(result.servers[i].hdd_total*1024*1024, 2);
 
-				//IO
-				TableRow.children["io"].innerHTML = bytesToSize(result.servers[i].io_read, 2) + "💿" + bytesToSize(result.servers[i].io_write, 2);
+				//IO, 过小的B字节单位没有意义
+				var io = "";
+				if(result.servers[i].io_read < 1024*1024)
+					io += (result.servers[i].io_read/1024).toFixed(1) + "K";
+				else
+					io += (result.servers[i].io_read/1024/1024).toFixed(1) + "M";
+				io += "💿"
+				if(result.servers[i].io_write < 1024*1024)
+					io += (result.servers[i].io_write/1024).toFixed(1) + "K";
+				else
+					io += (result.servers[i].io_write/1024/1024).toFixed(1) + "M";
+				TableRow.children["network"].innerHTML = io;
 
                 // delay time
 

From 39c92f4788a3f74d3d68b548c4cfcd373145a014 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 16:07:30 +0800
Subject: [PATCH 11/13] bug

---
 web/js/serverstatus.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index da99f85..b8bf938 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -251,7 +251,7 @@ function uptime() {
 					io += (result.servers[i].io_write/1024).toFixed(1) + "K";
 				else
 					io += (result.servers[i].io_write/1024/1024).toFixed(1) + "M";
-				TableRow.children["network"].innerHTML = io;
+				TableRow.children["io"].innerHTML = io;
 
                 // delay time
 

From 776ad683923d8c36e8ba1d3b26d3dea91f96163a Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 16:24:52 +0800
Subject: [PATCH 12/13] io css style

---
 web/css/dark.css  | 4 ++--
 web/css/light.css | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/css/dark.css b/web/css/dark.css
index 13652fa..1e0163c 100644
--- a/web/css/dark.css
+++ b/web/css/dark.css
@@ -19,8 +19,8 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 .expandRow > td { padding: 0 !important; border-top: 0px !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 115px; }
-#cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
-#io { width: 90px; }
+#cpu, #ram, #hdd { min-width: 20px; max-width: 60px; }
+#io { width: 115px; }
 #ping { max-width: 95px; }
 
 @media only screen and (max-width: 1080px) {
diff --git a/web/css/light.css b/web/css/light.css
index ba672c6..aaa4d92 100644
--- a/web/css/light.css
+++ b/web/css/light.css
@@ -16,8 +16,8 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 .expandRow > td { padding: 0 !important; border-top: 0px !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 115px; }
-#cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
-#io { width: 90px; }
+#cpu, #ram, #hdd { min-width: 20px; max-width: 60px; }
+#io { width: 115px; }
 #ping { max-width: 95px; }
 
 @media only screen and (max-width: 1080px) {

From 36040be11ea11854e64092146602218179a5bb2a Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 1 Apr 2022 16:28:22 +0800
Subject: [PATCH 13/13] add

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 2455ee2..542db5d 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 [![Python Support](https://img.shields.io/badge/python-2.7%2B%20-blue.svg)](https://github.com/cppla/ServerStatus)
 [![C++ Compiler](http://img.shields.io/badge/C++-GNU-blue.svg?style=flat&logo=cplusplus)](https://github.com/cppla/ServerStatus)
 [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/cppla/ServerStatus)
-[![Version](https://img.shields.io/badge/Version-Beta%201.0.7-red)](https://github.com/cppla/ServerStatus)
+[![Version](https://img.shields.io/badge/Version-Beta%201.0.8-red)](https://github.com/cppla/ServerStatus)
 
 ![Latest Version](http://dl.cpp.la/Archive/serverstatus-1.0.2.png)