How ProcessService Manages Core Processes on Different Platforms in v2rayN
ProcessService is the low-level execution wrapper in v2rayN that standardizes how v2ray, Xray, sing-box, and mihomo core binaries are launched, monitored, and terminated across Windows, Linux, and macOS, with specialized handling for elevated privileges on Unix-like systems.
The ProcessService class provides the foundational abstraction layer for process lifecycle management in the v2rayN proxy client. According to the source code in 2dust/v2rayN, this service integrates with CoreManager and CoreAdminManager to handle platform-specific requirements ranging from Windows Job Objects to Linux sudo authentication pipelines.
Platform-Aware Launch Flow
The execution path selection begins in CoreManager.RunProcess, which evaluates the operating system, Tun mode configuration, and core type to determine whether to use standard execution or privilege-escalated workflows.
Windows Normal Execution
On Windows systems, standard core launches proceed through CoreManager.RunProcessNormal. This method instantiates a new ProcessService with the binary path and arguments, then immediately registers the resulting process handle with the WindowsJobService. This registration binds the core process to a Windows Job Object, ensuring automatic cleanup if the parent v2rayN application terminates unexpectedly.
Linux and macOS Normal Execution
For non-Windows platforms without privilege requirements, RunProcessNormal follows the same instantiation pattern but omits the Job Object registration. Instead, ProcessService.StopAsync implements recursive termination logic using _process.Kill(true) to eliminate child processes before killing the parent, preventing orphaned processes on Unix-like systems.
Elevated Privileges on Unix-like Systems
When Tun mode is enabled on Linux or macOS for sing-box or mihomo cores, the system requires root privileges to configure network interfaces. In this scenario, CoreManager delegates execution to CoreAdminManager.RunProcessAsLinuxSudo:
// CoreManager.cs – RunProcess (lines 22-44)
if (mayNeedSudo && _config.TunModeItem.EnableTun &&
(coreInfo.CoreType is ECoreType.sing_box or ECoreType.mihomo) &&
Utils.IsNonWindows())
{
_linuxSudo = true;
await CoreAdminManager.Instance.Init(_config, _updateFunc);
return await CoreAdminManager.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
}
else
{
return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
}
This conditional branch triggers the generation of temporary bash scripts that wrap the core execution with sudo -S, enabling secure password authentication via standard input rather than command-line arguments.
ProcessService Architecture
Located in ServiceLib/Services/ProcessService.cs, the class provides a unified interface for external process management with distinct lifecycle stages.
Construction and Configuration
The constructor (lines 13-21) initializes ProcessStartInfo with the specified filename, arguments, working directory, and stream redirection options. This configuration supports both standard executions and sudo workflows requiring stdin access for password input.
Asynchronous Process Starting
StartAsync (lines 56-71) executes Process.Start() and initiates asynchronous reading of stdout and stderr streams. For privilege-escalated launches, the method accepts an optional password parameter:
// ProcessService.StartAsync implementation detail
if (!string.IsNullOrEmpty(pwd))
{
await _process.StandardInput.WriteLineAsync(pwd);
await _process.StandardInput.FlushAsync();
}
This writes the sudo password directly to the process's standard input stream, satisfying the sudo -S requirement without exposing credentials in process listings.
Cross-Platform Termination
StopAsync (lines 96-108) implements platform-specific termination logic. On non-Windows systems, it first invokes _process.Kill(true) to send termination signals to the entire process tree, followed by _process.Kill() for the parent. Windows implementations call Kill() directly, relying on the previously mentioned Job Object for child process cleanup.
Resource Disposal
The Dispose method (lines 52-70) guarantees process termination and handle release, again utilizing the recursive kill approach on Linux and macOS to ensure complete cleanup of spawned helper processes.
Windows Job Object Integration
Windows-specific durability features leverage WindowsJobService to bind core processes to a Job Object with the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag (0x2000). This kernel-level association ensures that if the v2rayN GUI crashes or closes normally, the operating system automatically terminates all associated core processes.
// CoreManager.cs – AddProcessJob (lines 85-86)
if (Utils.IsWindows())
{
_processJob ??= new();
_processJob?.AddProcess(processHandle);
}
The WindowsJobService (lines 12-18) wraps native Windows APIs including CreateJobObject and AssignProcessToJobObject, providing a managed interface for this functionality.
Linux and macOS Sudo Handling
Privileged execution on Unix-like systems requires generating temporary shell scripts to interface securely with the sudo system.
Starting with Sudo
CoreAdminManager.RunProcessAsLinuxSudo constructs a bash script that executes the core binary with elevated privileges:
// CoreAdminManager.cs – RunProcessAsLinuxSudo (lines 34-48)
sb.AppendLine("#!/bin/bash");
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
sb.AppendLine($"exec sudo -S -- {cmdLine}");
var shFilePath = await FileUtils.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
var procService = new ProcessService(
fileName: shFilePath,
arguments: "",
redirectInput: true,
...);
await procService.StartAsync(AppManager.Instance.LinuxSudoPwd);
Stopping Sudo Processes
Termination requires equivalent privilege escalation. CoreAdminManager.KillProcessAsLinuxSudo creates kill_as_sudo.sh containing the appropriate kill commands, executed via sudo -S with the stored password piped through ProcessService.StartAsync (lines 61-81 in CoreAdminManager.cs).
Practical Code Examples
Standard Core Launch
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(ECoreType.Xray);
var procService = await CoreManager.Instance.RunProcessNormal(
fileName: CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out _),
coreInfo: coreInfo,
configPath: "config.json",
displayLog: true);
await procService.StartAsync();
Elevated Launch on Linux
// Automatically handled by CoreManager when Tun mode is enabled
var procService = await CoreAdminManager.Instance.RunProcessAsLinuxSudo(
fileName: "/usr/local/bin/sing-box",
coreInfo: coreInfo,
configPath: "config.json");
await procService.StartAsync(AppManager.Instance.LinuxSudoPwd);
Stopping a Core Process
await procService.StopAsync(); // Handles child processes on Linux/macOS
procService.Dispose(); // Ensures complete resource cleanup
Summary
- ProcessService provides a unified abstraction for launching v2ray, Xray, sing-box, and mihomo cores across Windows, Linux, and macOS without platform-specific code in the consumer layers.
- Windows utilizes Job Objects via
WindowsJobServicewith theJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEflag to guarantee automatic process tree cleanup when the parent application exits. - Linux and macOS employ
_process.Kill(true)inStopAsyncto recursively terminate child processes, preventing zombie processes when killing the core binary. - Elevated execution on Unix-like systems generates temporary bash scripts (
run_as_sudo.sh,kill_as_sudo.sh) and securely pipes passwords viaStartAsyncwhen Tun mode requires root privileges. - CoreManager orchestrates the platform detection logic, delegating to
CoreAdminManageronly whenmayNeedSudo,EnableTun, andIsNonWindowsconditions converge.
Frequently Asked Questions
How does v2rayN prevent orphaned core processes when the application crashes on Windows?
v2rayN uses the WindowsJobService to create a Windows Job Object with the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag. When CoreManager.AddProcessJob registers the core process handle to this job, the Windows kernel guarantees that all processes in the job terminate automatically if the parent v2rayN process closes unexpectedly.
Why does ProcessService use different kill strategies on Linux versus Windows?
On Windows, the Job Object handles process tree cleanup automatically, so ProcessService.StopAsync calls Kill() directly. On Linux and macOS, StopAsync first calls _process.Kill(true) to recursively signal child processes before killing the parent. This approach prevents zombie processes and ensures that spawned helper processes created by sing-box or mihomo are properly terminated.
What triggers the sudo workflow in v2rayN's process management?
The sudo workflow activates when three conditions are met simultaneously: the configuration enables Tun mode (_config.TunModeItem.EnableTun), the selected core type is either sing-box or mihomo, and the operating system is non-Windows (Utils.IsNonWindows()). When these conditions are satisfied, CoreManager.RunProcess delegates to CoreAdminManager.RunProcessAsLinuxSudo instead of the standard execution path.
How is the sudo password securely transmitted to the core process?
The password is never passed as a command-line argument where it could appear in process listings. Instead, CoreAdminManager creates a temporary bash script that invokes sudo -S, which reads the password from standard input. The ProcessService.StartAsync method receives the password as a parameter and writes it to StandardInput immediately after process creation, allowing secure authentication without exposing credentials.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →