mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
feat: add cleanup for redundant mihomo processes
This commit is contained in:
@@ -435,7 +435,244 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CoreManager {
|
impl CoreManager {
|
||||||
|
/// 清理多余的 mihomo 进程
|
||||||
|
async fn cleanup_orphaned_mihomo_processes(&self) -> Result<()> {
|
||||||
|
logging!(info, Type::Core, true, "开始清理多余的 mihomo 进程");
|
||||||
|
|
||||||
|
// 获取当前管理的进程 PID
|
||||||
|
let current_pid = {
|
||||||
|
let child_guard = self.child_sidecar.lock().await;
|
||||||
|
child_guard.as_ref().map(|child| child.pid())
|
||||||
|
};
|
||||||
|
|
||||||
|
let target_processes = ["verge-mihomo", "verge-mihomo-alpha"];
|
||||||
|
|
||||||
|
// 并行查找所有目标进程
|
||||||
|
let mut process_futures = Vec::new();
|
||||||
|
for &target in &target_processes {
|
||||||
|
let process_name = if cfg!(windows) {
|
||||||
|
format!("{}.exe", target)
|
||||||
|
} else {
|
||||||
|
target.to_string()
|
||||||
|
};
|
||||||
|
process_futures.push(self.find_processes_by_name(process_name, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
let process_results = futures::future::join_all(process_futures).await;
|
||||||
|
|
||||||
|
// 收集所有需要终止的进程PID
|
||||||
|
let mut pids_to_kill = Vec::new();
|
||||||
|
for result in process_results {
|
||||||
|
match result {
|
||||||
|
Ok((pids, process_name)) => {
|
||||||
|
for pid in pids {
|
||||||
|
// 跳过当前管理的进程
|
||||||
|
if let Some(current) = current_pid {
|
||||||
|
if pid == current {
|
||||||
|
logging!(
|
||||||
|
debug,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"跳过当前管理的进程: {} (PID: {})",
|
||||||
|
process_name,
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pids_to_kill.push((pid, process_name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
logging!(debug, Type::Core, true, "查找进程时发生错误: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pids_to_kill.is_empty() {
|
||||||
|
logging!(debug, Type::Core, true, "未发现多余的 mihomo 进程");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut kill_futures = Vec::new();
|
||||||
|
for (pid, process_name) in &pids_to_kill {
|
||||||
|
kill_futures.push(self.kill_process_with_verification(*pid, process_name.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let kill_results = futures::future::join_all(kill_futures).await;
|
||||||
|
|
||||||
|
let killed_count = kill_results.into_iter().filter(|&success| success).count();
|
||||||
|
|
||||||
|
if killed_count > 0 {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"清理完成,共终止了 {} 个多余的 mihomo 进程",
|
||||||
|
killed_count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据进程名查找进程PID列表
|
||||||
|
async fn find_processes_by_name(
|
||||||
|
&self,
|
||||||
|
process_name: String,
|
||||||
|
_target: &str,
|
||||||
|
) -> Result<(Vec<u32>, String)> {
|
||||||
|
let output = if cfg!(windows) {
|
||||||
|
tokio::process::Command::new("tasklist")
|
||||||
|
.args(&[
|
||||||
|
"/FI",
|
||||||
|
&format!("IMAGENAME eq {}", process_name),
|
||||||
|
"/FO",
|
||||||
|
"CSV",
|
||||||
|
"/NH",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
tokio::process::Command::new("pgrep")
|
||||||
|
.arg(&process_name)
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
// Linux
|
||||||
|
tokio::process::Command::new("pidof")
|
||||||
|
.arg(&process_name)
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Ok((Vec::new(), process_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let mut pids = Vec::new();
|
||||||
|
|
||||||
|
if cfg!(windows) {
|
||||||
|
// 解析CSV格式输出: "进程名","PID","会话名","会话#","内存使用"
|
||||||
|
for line in stdout.lines() {
|
||||||
|
if !line.is_empty() && line.contains(&process_name) {
|
||||||
|
let fields: Vec<&str> = line.split(',').collect();
|
||||||
|
if fields.len() >= 2 {
|
||||||
|
// 移除引号并解析PID
|
||||||
|
let pid_str = fields[1].trim_matches('"');
|
||||||
|
if let Ok(pid) = pid_str.parse::<u32>() {
|
||||||
|
pids.push(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unix系统直接解析PID列表
|
||||||
|
for pid_str in stdout.trim().split_whitespace() {
|
||||||
|
if let Ok(pid) = pid_str.parse::<u32>() {
|
||||||
|
pids.push(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((pids, process_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 终止进程并验证结果
|
||||||
|
async fn kill_process_with_verification(&self, pid: u32, process_name: String) -> bool {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"尝试终止进程: {} (PID: {})",
|
||||||
|
process_name,
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
|
||||||
|
let success = if cfg!(windows) {
|
||||||
|
tokio::process::Command::new("taskkill")
|
||||||
|
.args(&["/F", "/PID", &pid.to_string()])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map(|output| output.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
tokio::process::Command::new("kill")
|
||||||
|
.args(&["-9", &pid.to_string()])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map(|output| output.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
|
||||||
|
if success {
|
||||||
|
// 短暂等待并验证进程是否真正终止
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||||
|
|
||||||
|
let still_running = self.is_process_running(pid).await.unwrap_or(false);
|
||||||
|
if still_running {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"进程 {} (PID: {}) 终止命令成功但进程仍在运行",
|
||||||
|
process_name,
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"成功终止进程: {} (PID: {})",
|
||||||
|
process_name,
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"无法终止进程: {} (PID: {})",
|
||||||
|
process_name,
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查进程是否仍在运行
|
||||||
|
async fn is_process_running(&self, pid: u32) -> Result<bool> {
|
||||||
|
let output = if cfg!(windows) {
|
||||||
|
tokio::process::Command::new("tasklist")
|
||||||
|
.args(&["/FI", &format!("PID eq {}", pid), "/FO", "CSV", "/NH"])
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
tokio::process::Command::new("ps")
|
||||||
|
.args(&["-p", &pid.to_string()])
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(output.status.success() && !output.stdout.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
async fn start_core_by_sidecar(&self) -> Result<()> {
|
async fn start_core_by_sidecar(&self) -> Result<()> {
|
||||||
|
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"清理多余 mihomo 进程时发生错误: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
logging!(trace, Type::Core, true, "Running core by sidecar");
|
logging!(trace, Type::Core, true, "Running core by sidecar");
|
||||||
let config_file = &Config::generate_file(ConfigType::Run)?;
|
let config_file = &Config::generate_file(ConfigType::Run)?;
|
||||||
let app_handle = handle::Handle::global()
|
let app_handle = handle::Handle::global()
|
||||||
@@ -585,6 +822,17 @@ impl CoreManager {
|
|||||||
pub async fn init(&self) -> Result<()> {
|
pub async fn init(&self) -> Result<()> {
|
||||||
logging!(trace, Type::Core, "Initializing core");
|
logging!(trace, Type::Core, "Initializing core");
|
||||||
|
|
||||||
|
// 应用启动时先清理任何遗留的 mihomo 进程
|
||||||
|
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"应用初始化时清理多余 mihomo 进程失败: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut core_started_successfully = false;
|
let mut core_started_successfully = false;
|
||||||
|
|
||||||
if service::is_service_available().await.is_ok() {
|
if service::is_service_available().await.is_ok() {
|
||||||
@@ -787,6 +1035,18 @@ impl CoreManager {
|
|||||||
/// 重启内核
|
/// 重启内核
|
||||||
pub async fn restart_core(&self) -> Result<()> {
|
pub async fn restart_core(&self) -> Result<()> {
|
||||||
self.stop_core().await?;
|
self.stop_core().await?;
|
||||||
|
|
||||||
|
// 在重启时也清理多余的 mihomo 进程
|
||||||
|
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Core,
|
||||||
|
true,
|
||||||
|
"重启时清理多余 mihomo 进程失败: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.start_core().await?;
|
self.start_core().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user