pmonitor 1.4.8__py3-none-any.whl → 1.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,51 +1,116 @@
1
+ import subprocess
2
+ import threading
3
+ import time
4
+ import json
1
5
  import re
2
- import asyncio
6
+ import os
3
7
 
4
8
 
5
9
  class VmMapMemory:
6
10
  def __init__(self):
7
- self.last_nonzero_memory = None
11
+ """
12
+ 初始化实时内存监控器
13
+ """
14
+ self.pids = set() # 当前监控的 PID
15
+ self.current_data = {} # 存储最新的内存数据 {pid: memory_mb}
16
+ self.lock = threading.Lock() # 线程锁
17
+ self.running = True # 控制线程运行状态
18
+ self.thread = None # 监控线程
19
+ # 启动监控线程
20
+ self.start_monitoring()
21
+
22
+ def start_monitoring(self):
23
+ """启动监控线程"""
24
+ self.thread = threading.Thread(target=self._monitor_memory)
25
+ self.thread.daemon = True # 设置为守护线程
26
+ self.thread.start()
27
+
28
+ def update_pids(self, new_pids):
29
+ """更新要监控的 PID 列表"""
30
+ with self.lock:
31
+ # 转换为字符串并更新集合
32
+ self.pids = set(map(str, new_pids))
33
+
34
+ def _convert_to_mb(self, mem_str):
35
+ """
36
+ 将内存字符串转换为 MB
37
+ 支持格式: 123, 123K, 123M, 123G
38
+ """
39
+ # 移除逗号(如果存在)
40
+ mem_str = mem_str.replace(',', '')
41
+
42
+ # 检查单位并转换
43
+ if 'G' in mem_str:
44
+ return float(mem_str.replace('G', '')) * 1024
45
+ elif 'M' in mem_str:
46
+ return float(mem_str.replace('M', ''))
47
+ elif 'K' in mem_str:
48
+ return float(mem_str.replace('K', '')) / 1024
49
+ else:
50
+ # 假设为字节
51
+ return float(mem_str) / (1024 * 1024)
52
+
53
+ def _monitor_memory(self):
54
+ """线程函数:实时监控内存使用"""
55
+ # macOS 专用 top 命令
56
+ cmd = ["top", "-stats", "pid,mem"]
57
+
58
+ # 启动 top 进程
59
+ process = subprocess.Popen(
60
+ cmd,
61
+ stdout=subprocess.PIPE,
62
+ stderr=subprocess.PIPE,
63
+ text=True,
64
+ bufsize=1, # 行缓冲
65
+ universal_newlines=True # 确保文本模式
66
+ )
8
67
 
9
- async def get_process_memory(self, pid):
10
- cmd = ["top", "-l", "1", "-stats", "mem", "-pid", str(pid)]
11
68
  try:
12
- # 创建子进程并设置超时
13
- proc = await asyncio.create_subprocess_exec(
14
- *cmd,
15
- stdout=asyncio.subprocess.PIPE,
16
- stderr=asyncio.subprocess.DEVNULL
17
- )
18
-
19
- try:
20
- stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=1)
21
- except asyncio.TimeoutError:
22
- # 超时后确保子进程被终止
23
- proc.kill()
24
- await proc.wait()
25
- raise
26
-
27
- if proc.returncode == 0:
28
- # 解析 top 输出中的内存信息
29
- lines = stdout.decode().strip().split('\n')
30
- if lines:
31
- last_line = lines[-1].strip() # 获取最后一行
32
- match = re.match(r'^([\d.]+)([GMK]?)$', last_line)
33
-
34
- if match:
35
- val, unit = float(match.group(1)), match.group(2)
36
- memory_value = val * {
37
- 'G': 1024, 'M': 1, 'K': 1 / 1024, '': 1 / (1024 * 1024)
38
- }.get(unit, 1)
39
-
40
- if memory_value > 0:
41
- self.last_nonzero_memory = memory_value
42
- # 返回当前值或上一次非零值
43
- return memory_value if memory_value != 0 else self.last_nonzero_memory
69
+ while self.running:
70
+ line = process.stdout.readline() # 逐行读取
71
+
72
+ if not line: # 进程结束或无输出时退出
73
+ break
74
+
75
+ # 匹配 PID 和内存行 (例如: "12345 123.4M")
76
+ match = re.match(r'^\s*(\d+)\s+([\d,.]+[KMG]?)\s*$', line.strip())
77
+ if match:
78
+ pid = match.group(1)
79
+ mem_str = match.group(2)
80
+
81
+ # 检查是否在监控列表中
82
+ with self.lock:
83
+ if pid in self.pids:
84
+ mem_mb = self._convert_to_mb(mem_str)
85
+ # 直接更新当前数据,不保存历史
86
+ self.current_data[pid] = round(mem_mb, 2)
87
+
44
88
  except Exception as e:
45
- print(e)
89
+ print(f"Monitoring error: {e}")
46
90
  finally:
47
- if proc and proc.returncode is None:
48
- proc.kill()
49
- await proc.wait()
50
- # 如果未能获取到有效值,返回上一次非零值或默认值 0.0
51
- return self.last_nonzero_memory if self.last_nonzero_memory is not None else 0.0
91
+ if process.poll() is None: # 确保进程终止
92
+ process.terminate()
93
+
94
+ def get_current_data(self):
95
+ """
96
+ 获取当前内存数据(线程安全)
97
+ 只返回当前监控的 PID 的最新内存值
98
+ """
99
+ with self.lock:
100
+ # 只返回当前监控列表中的 PID 数据
101
+ return {
102
+ pid: self.current_data.get(pid, 0.0)
103
+ for pid in self.pids
104
+ }
105
+
106
+ def get_current_json(self):
107
+ """获取 JSON 格式的当前内存数据"""
108
+ data = self.get_current_data()
109
+ return json.dumps(data, indent=2)
110
+
111
+ def stop(self):
112
+ """停止监控"""
113
+ self.running = False
114
+ if self.thread and self.thread.is_alive():
115
+ self.thread.join(timeout=1)
116
+ print("Memory monitor stopped")
monitor/monitor_pids.py CHANGED
@@ -25,6 +25,8 @@ class PidsPerf:
25
25
  async def get_mac_perf(self):
26
26
  current_pid = self.processUtil.find_main_process_pid(self.process_name)
27
27
  vm = VmMapMemory()
28
+ next_time = time.time() # 初始化下一次执行的时间点
29
+ first_iteration = True # 标志变量,用于跳过第一次循环
28
30
  while True:
29
31
  start_time = time.time() # 记录循环开始时间
30
32
  minor_cpu_sum = 0
@@ -44,13 +46,19 @@ class PidsPerf:
44
46
 
45
47
  gpu_memory_usage, gpu_memory_total, gpu_memory_free = get_gpu_memory()
46
48
  # 使用 asyncio.gather 并发获取内存数据
47
- memory_tasks = [vm.get_process_memory(process.pid) for process in pids_process]
48
- memory_results = await asyncio.gather(*memory_tasks, return_exceptions=True)
49
- for idx, process in enumerate(pids_process):
49
+ vm.update_pids(current_pids)
50
+
51
+ if first_iteration:
52
+ first_iteration = False # 第一次循环后将标志置为 False
53
+ await asyncio.sleep(self.interval / 1000) # 等待一段时间以确保数据稳定
54
+ continue # 跳过第一次循环
55
+
56
+ for process in pids_process:
50
57
  try:
51
58
  cpu_percent = process.cpu_percent()
52
59
  mem_percent = u'%.2f' % (process.memory_percent()) # 内存利用率
53
- real_mem = memory_results[idx] # 实际内存
60
+ vm_memory_data = json.loads(vm.get_current_json())
61
+ real_mem = vm_memory_data.get(str(process.pid), 0) # 默认值为 0(如果未找到对应 PID)
54
62
  thread_count = process.num_threads() # 线程总数
55
63
  except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
56
64
  cpu_percent = 0
@@ -80,7 +88,7 @@ class PidsPerf:
80
88
  # minor_thread_count_sum)
81
89
  # 获取磁盘IO
82
90
  io_read_bytes_start, io_write_bytes_start = self.get_disk_usage()
83
- time.sleep(0.1) # 采集间隔
91
+ time.sleep(0.1)
84
92
  io_read_bytes_end, io_write_bytes_end = self.get_disk_usage() # io read/write
85
93
  io_read_bytes = io_read_bytes_end - io_read_bytes_start # io read/byte
86
94
  io_write_bytes = io_write_bytes_end - io_write_bytes_start # io write/byte
@@ -91,9 +99,13 @@ class PidsPerf:
91
99
  "memory_total": mem_total, "memory_available": mem_available}
92
100
  json_data = json.dumps(data)
93
101
  print(json_data)
94
- elapsed = time.time() - start_time
95
- remaining_time = max(0, self.interval / 1000 - elapsed)
96
- await asyncio.sleep(remaining_time)
102
+ # 精确控制间隔
103
+ next_time += self.interval
104
+ sleep_time = next_time - time.time()
105
+ if sleep_time > 0:
106
+ await asyncio.sleep(sleep_time)
107
+ else:
108
+ next_time = time.time() # 如果超时,重置时间点
97
109
  sys.stdout.flush()
98
110
 
99
111
  def get_disk_usage(self):
@@ -129,6 +141,7 @@ class PidsPerf:
129
141
  # 获取GPU物理内存
130
142
  computer = Computer()
131
143
  gpu_key = list(computer.gpu.keys())[0]
144
+ next_time = time.time() # 初始化下一次执行的时间点
132
145
  while True:
133
146
  minor_cpu_sum = 0
134
147
  minor_workSet_mem_sum = 0
@@ -280,9 +293,13 @@ class PidsPerf:
280
293
  sys.stdout.flush()
281
294
  # 更新系统时间
282
295
  system_time_pre = system_time_post
283
- # 等待以确保大约每秒更新一次
284
- elapsed = time.time() - start_time
285
- time.sleep(max(0, self.interval - elapsed))
296
+ # 精确控制间隔
297
+ next_time += self.interval
298
+ sleep_time = next_time - time.time()
299
+ if sleep_time > 0:
300
+ time.sleep(sleep_time)
301
+ else:
302
+ next_time = time.time() # 如果超时,重置时间点v
286
303
 
287
304
  async def start_perf(self):
288
305
  if platform.system() == 'Windows':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pmonitor
3
- Version: 1.4.8
3
+ Version: 1.5.0
4
4
  Summary: pc monitor
5
5
  Home-page: UNKNOWN
6
6
  Author: cfr
@@ -3,8 +3,8 @@ monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  monitor/get_process_name.py,sha256=HxePo2gdrTo2Ukg4DTnYzUMdczVWywCeLCXQ-RRWyu8,3913
4
4
  monitor/mac_gpu.py,sha256=kQDPMW04lIYPBbJw772Jh1OQxj-D4JNmdjJlP6BuR4w,2320
5
5
  monitor/monitor_mac.py,sha256=OoptTTXTZQupzBwgGOfKNjyYAKBxDy-wI3l6T7XVE8s,4651
6
- monitor/monitor_mac_vmmap.py,sha256=Lu1AAw6auuZzuNZtASOaNBpRPOOgyzGr1jYSTErmRmo,1971
7
- monitor/monitor_pids.py,sha256=1Bmemd-cf1NkrcCbKwOg-56fDgxSGLXam-EGmr_5AWU,16531
6
+ monitor/monitor_mac_vmmap.py,sha256=j_4Gl6uTnOcjbrYVsYfd_TNhhYo0ewUZf-zCpGUeEgs,3751
7
+ monitor/monitor_pids.py,sha256=kIy8oHIN-OwSDqWHTEUlCFpqCdsvGWmX9gtU0cb0W40,17228
8
8
  monitor/monitor_win.py,sha256=xs5nzqqEPoDmJTegh3lQhVjjpPcOWnruWKK65ttqnTo,6161
9
9
  monitor/run_monitor.py,sha256=HDQXHIx47ZRN4Gp0PTr0yWKVM28n8A7-NaO--_pdgso,1577
10
10
  monitor/sys_info.py,sha256=aNultuRoQuRYPkYo397xAXVDXP07Qx5JOHtYzNmEvuc,3208
@@ -24,8 +24,8 @@ monitor/DLL/sechost.dll,sha256=1mXCrgNDRBiZ2XZk7eTpDm4oPG-x9BW4uYUVkrfvBkk,69986
24
24
  monitor/DLL/ucrtbased.dll,sha256=vtmKFPEHyr2OXkrUOu3Qs1dlbKG1dxZ8ItKCkTTU5S4,2238056
25
25
  monitor/DLL/vcruntime140.dll,sha256=AsaqDm5iRBGp8ZsDYKeGWrFZCOJgJFEOXDipwINiw1o,119888
26
26
  monitor/DLL/vcruntime140_1.dll,sha256=fdmqAuJxxoym1fGNZR0joV1yWXFa9DMmV4993ifzdjc,49640
27
- pmonitor-1.4.8.dist-info/METADATA,sha256=cHkAUgHDu0voEx1JNF_q1qbPo7YGVzlt6_h4J53KZRs,291
28
- pmonitor-1.4.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
29
- pmonitor-1.4.8.dist-info/entry_points.txt,sha256=vmlLEANgf2fZar9BeXYiKdF6GMJbVXip-SIZx5yPXDo,55
30
- pmonitor-1.4.8.dist-info/top_level.txt,sha256=tGkQDkVeyKgP5Rr7acpp0df83NBAnI8up0sGwRxuuQ4,8
31
- pmonitor-1.4.8.dist-info/RECORD,,
27
+ pmonitor-1.5.0.dist-info/METADATA,sha256=BAZnw0kdkSDw4V3MrOBTuSW2Oc090dVpMWOLfNj2Xco,291
28
+ pmonitor-1.5.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
29
+ pmonitor-1.5.0.dist-info/entry_points.txt,sha256=vmlLEANgf2fZar9BeXYiKdF6GMJbVXip-SIZx5yPXDo,55
30
+ pmonitor-1.5.0.dist-info/top_level.txt,sha256=tGkQDkVeyKgP5Rr7acpp0df83NBAnI8up0sGwRxuuQ4,8
31
+ pmonitor-1.5.0.dist-info/RECORD,,