优化杀死iproxy逻辑

This commit is contained in:
2025-09-18 21:31:23 +08:00
parent c54a0aceb5
commit 811935ac60
3 changed files with 58 additions and 26 deletions

12
.idea/workspace.xml generated
View File

@@ -5,18 +5,8 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成"> <list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成">
<change afterPath="$PROJECT_DIR$/resources/133bffc17635b7a3bd709492b7a519d96710a4a2/bgv.png" afterDir="false" />
<change afterPath="$PROJECT_DIR$/resources/3157d8bd35c230012cb3640d2f26ffd0b61a10d1/bgv.png" afterDir="false" />
<change afterPath="$PROJECT_DIR$/resources/f2fb0c352df29415f69708d90a9daba702ae4917/bgv.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/iOSAI.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/iOSAI.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Entity/ResultData.py" beforeDir="false" afterPath="$PROJECT_DIR$/Entity/ResultData.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Module/DeviceInfo.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/DeviceInfo.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Module/FlaskService.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/FlaskService.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Module/FlaskSubprocessManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/FlaskSubprocessManager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Module/Main.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/Main.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Utils/LogManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/LogManager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Utils/ThreadManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/ThreadManager.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />

View File

@@ -4,12 +4,12 @@ import signal
import sys import sys
import time import time
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
import wda
import threading
import subprocess
from pathlib import Path from pathlib import Path
from typing import List, Dict, Optional from typing import List, Dict, Optional
import threading
import subprocess
import wda
from tidevice import Usbmux, ConnectionType from tidevice import Usbmux, ConnectionType
from tidevice._device import BaseDevice from tidevice._device import BaseDevice
from Entity.DeviceModel import DeviceModel from Entity.DeviceModel import DeviceModel
@@ -23,10 +23,8 @@ class Deviceinfo(object):
"""设备生命周期管理:以 deviceModelList 为唯一真理源""" """设备生命周期管理:以 deviceModelList 为唯一真理源"""
def __init__(self): def __init__(self):
... # ✅ 连接线程池(最大 6 并发)
# ✅ 新增:连接线程池(最大 6 并发)
self._connect_pool = ThreadPoolExecutor(max_workers=6) self._connect_pool = ThreadPoolExecutor(max_workers=6)
...
if os.name == "nt": if os.name == "nt":
self._si = subprocess.STARTUPINFO() self._si = subprocess.STARTUPINFO()
@@ -44,11 +42,14 @@ class Deviceinfo(object):
self._lock = threading.Lock() self._lock = threading.Lock()
self._model_index: Dict[str, DeviceModel] = {} # udid -> model self._model_index: Dict[str, DeviceModel] = {} # udid -> model
# ✅ 1. 失踪时间戳记录(替代原来的 miss_count # ✅ 失踪时间戳记录(替代原来的 miss_count
self._last_seen: Dict[str, float] = {} self._last_seen: Dict[str, float] = {}
self._port_pool: List[int] = [] self._port_pool: List[int] = []
self._port_in_use: set[int] = set() self._port_in_use: set[int] = set()
# ✅ 新增:全局 iproxy 进程注册表 udid -> Popen
self._iproxy_registry: Dict[str, subprocess.Popen] = {}
# region iproxy 初始化(原逻辑不变) # region iproxy 初始化(原逻辑不变)
try: try:
self.iproxy_path = self._iproxy_path() self.iproxy_path = self._iproxy_path()
@@ -76,6 +77,9 @@ class Deviceinfo(object):
args = [str(self.iproxy_path), "-u", udid, str(local_port), str(remote_port)] args = [str(self.iproxy_path), "-u", udid, str(local_port), str(remote_port)]
p = subprocess.Popen(args, **self._popen_kwargs) p = subprocess.Popen(args, **self._popen_kwargs)
# ✅ 注册到全局表
self._iproxy_registry[udid] = p
def _pipe_to_log(name: str, stream): def _pipe_to_log(name: str, stream):
try: try:
for line in iter(stream.readline, ''): for line in iter(stream.readline, ''):
@@ -127,6 +131,13 @@ class Deviceinfo(object):
for udid in need_remove: for udid in need_remove:
self._remove_model(udid) self._remove_model(udid)
# ✅ 实时清理孤儿 iproxy原 10 秒改为每次循环)
self._cleanup_orphan_iproxy()
# ✅ 设备全空时核平所有 iproxy
if not self.deviceModelList:
self._kill_all_iproxy()
# 2. 发现新设备 → 并发连接 # 2. 发现新设备 → 并发连接
with self._lock: with self._lock:
new_udids = [d.udid for d in lists new_udids = [d.udid for d in lists
@@ -146,7 +157,7 @@ class Deviceinfo(object):
time.sleep(1) time.sleep(1)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# ✅ 3. USB 层枚举 SN跨平台 # ✅ USB 层枚举 SN跨平台
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _usb_enumerate_sn(self) -> set[str]: def _usb_enumerate_sn(self) -> set[str]:
try: try:
@@ -155,7 +166,32 @@ class Deviceinfo(object):
except Exception: except Exception:
return set() return set()
# ===================== 以下代码与原文件完全一致 ===================== # ----------------------------------------------------------
# ✅ 清理孤儿 iproxy
# ----------------------------------------------------------
def _cleanup_orphan_iproxy(self):
live_udids = set(self._model_index.keys())
for udid, proc in list(self._iproxy_registry.items()):
if udid not in live_udids:
LogManager.warning(f"发现孤儿 iproxy 进程UDID 不在线:{udid},正在清理")
self._terminate_proc(proc)
self._iproxy_registry.pop(udid, None)
# ----------------------------------------------------------
# ✅ 核平所有 iproxyWindows / macOS 通用)
# ----------------------------------------------------------
def _kill_all_iproxy(self):
try:
if os.name == "nt":
subprocess.run(["taskkill", "/F", "/IM", "iproxy.exe"], check=False)
else:
subprocess.run(["pkill", "-f", "iproxy"], check=False)
self._iproxy_registry.clear()
LogManager.info("已强制清理所有 iproxy 进程")
except Exception as e:
LogManager.warning(f"强制清理 iproxy 失败:{e}")
# -------------------- 以下代码与原文件完全一致 --------------------
def _wda_health_checker(self): def _wda_health_checker(self):
while True: while True:
time.sleep(1) time.sleep(1)
@@ -229,6 +265,11 @@ class Deviceinfo(object):
print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}") print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}")
LogManager.method_info(f"【删】待杀进程数 count={len(to_kill)} udid={udid}", method="device_count") LogManager.method_info(f"【删】待杀进程数 count={len(to_kill)} udid={udid}", method="device_count")
# ✅ 先清理注册表中的 iproxy
iproxy_proc = self._iproxy_registry.pop(udid, None)
if iproxy_proc:
self._terminate_proc(iproxy_proc)
for idx, item in enumerate(to_kill, 1): for idx, item in enumerate(to_kill, 1):
print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}") print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}")
LogManager.method_info(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count") LogManager.method_info(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count")
@@ -335,13 +376,14 @@ class Deviceinfo(object):
if not self._spawn_iproxy: if not self._spawn_iproxy:
LogManager.error("iproxy 启动器未就绪", udid) LogManager.error("iproxy 启动器未就绪", udid)
return None return None
while self._port_in_use and self._is_port_open(port): for attempt in range(5):
if not self._is_port_open(port):
break
LogManager.warning(f"端口 {port} 仍被占用,第 {attempt+1} 次重试释放", udid)
pid = self._get_pid_by_port(port) pid = self._get_pid_by_port(port)
if pid and pid != os.getpid(): if pid and pid != os.getpid():
LogManager.warning(f"端口 {port} 仍被 PID {pid} 占用,尝试释放", udid)
self._kill_pid_gracefully(pid) self._kill_pid_gracefully(pid)
else: time.sleep(0.2)
break
try: try:
p = self._spawn_iproxy(udid, port, 9100) p = self._spawn_iproxy(udid, port, 9100)
self._port_in_use.add(port) self._port_in_use.add(port)
@@ -405,4 +447,4 @@ class Deviceinfo(object):
for p in candidates: for p in candidates:
if p.exists(): if p.exists():
return p return p
raise FileNotFoundError(f"iproxy not found, tried: {[str(c) for c in candidates]}") raise FileNotFoundError(f"iproxy not found, tried: {[str(c) for c in candidates]}")