Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

The .NET Process class on Linux

 

October 29, 2019
Tom Deseyn
Related topics:
.NETLinux

Share:

    In this article, we'll look at .NET's Process class. We'll go over the basics of how and when to use it, then cover differences in usage between Windows and Linux, and point out a few caveats. This article covers behavior in .NET Core 3.0.

    The basics

    The Process class represents an instance of a running process. You can use it to start new processes using Process.Start or get running processes via the static GetProcessById,GetProcesses,GetProcessesByName methods.

    When starting a new Process, all information to start the process is set on a ProcessStartInfo instance (PSI). PSI has properties like FileName and Arguments to set the program to execute and its arguments. UseShellExecute allows you to open documents. RedirectStandard{Input/Output/Error} allows you to write/read the standard I/O streams. Environment/EnvironmentVariables and WorkingDirectory allow you to control the environment variables and working directory.

    .NET Core 2.1 (/netstandard 2.1) added an ArgumentList property. The Arguments property is a string and requires the user to use Windows command-line escaping rules (e.g., using double-quotes to delimit arguments). The ArgumentsList property is a Collection that holds separate arguments. Process.Start will take care of passing those to the underlying platform. It's recommended to use ArgumentList over Arguments when targeting .NET Core 2.1+/netstandard2.1+.

    The following example shows launching the echo application, with a single argument hello world, and then waiting for the process to terminate.

    using var process = Process.Start(
        new ProcessStartInfo
        {
            FileName = "echo",
            ArgumentList = { "hello world" }
        });
    process.WaitForExit();
    

    Not supported on Linux

    The following properties of ProcessStartInfo aren't supported on Linux and throw PlatformNotSupportedException: PasswordInClearText, Domain, LoadUserProfile, Password.

    Retrieving processes from a remote machine using the machineName overload GetProcessById,GetProcesses,GetProcessesByName is also not supported.

    On the Process, it isn't supported to set working set limits (MinWorkingSet, MaxWorkingSet). On a ProcessThread (obtained via Process.Threads) the PriorityLevel/ProcessorAffinity cannot be set.

    Killing processes

    Processes can be stopped by calling Process.Kill. On Linux, this is implemented by sending the SIGKILL signal, which tells the kernel to terminate the application immediately. It’s not possible to send a SIGTERM signal, which requests the application to gracefully terminate.

    Since .NET Core 3.0, Process.Kill no longer throws Win32Exception/InvalidOperationException if the process is terminating or was already terminated. If you are targeting earlier .NET Core versions (or .NET Framework), you should add a try/catch block to handle these exceptions.

    .NET Core 3.0 adds an overload to the Process.Kill method that accepts a bool entireProcessTree. When set to true, descendants of the process will also be killed.

    UseShellExecute

    The ProcessStartInfo.UseShellExecute property can be used to open documents. Shell refers to the graphical shell of the user, and not a command-line shell like bash. Setting this to true means behave as if the user double-clicks the file. When ProcessStartInfo.FileName refers to an executable, it will be executed. When it refers to a document, it will be opened using the default program. For example an .ods file will open with LibreOffice Calc. You can set FileName to an http-uri (like https://redhatloves.net) to open up a browser and show a website.

    Unix shell scripts are considered real executables by the operating system (OS). This means it is not required to set UseShellExecute. This approach is unlike Windows .bat files, which need the Windows shell to find the interpreter.

    On Windows, UseShellExecute allows alternative actions on a document (like printing) by setting the ProcessStartInfo.Verb. On other OSes, this property is ignored.

    FileName resolution

    When setting a relative filename on Process.FileName, the file will be resolved. The resolution steps depend on UseShellExecute being set or not. The resolution on non-Windows platforms is implemented to behave similarly to Windows.

    When UseShellExecute is set to false:

    • Find the file in the native application directory.
    • Find the file in the process's working directory.
    • Find the file on PATH.

    The native application directory is the dotnet installation directory when executing dotnet. When using a native apphost, it’s the apphost directory.

    When UseShellExecute is set to true:

    • Find an executable file in the ProcessStartInfo.WorkingDirectory. If that is not set, the process working directory is used.
    • Find an executable file on PATH.
    • Use the shell open program and pass it the relative path.

    It's important to know that in both cases directories are searched, which may be unsafe (executable directory, working directory). You may want to implement your own resolution and always set FileName to an absolute path.

    Redirected streams

    When UseShellExecute is set to false, you can redirect standard input, output, and error using ProcessStartInfo.RedirectStandard{Input/Output/Error}. Corresponding Encoding properties (like StandardOutputEncoding) allow you to set the encoding for the streams.

    Unless you're running or starting an interactive application (like launching vi), you should redirect the streams and handle them.

    Note that asynchronous methods like Process.StandardOutput.ReadAsync and Process.BeginOutputReadLine use the ThreadPool to do asynchronous reads, which means they block a ThreadPool thread when waiting for application output. This approach can lead to ThreadPool starvation if you have many Processes that don’t output much. This is an issue on Windows, too.

    If you are using Begin{Output/Error}ReadLine and call WaitForExit, that method will wait until all standard output/error was read (and corresponding {Output/Error}DataReceived events are emitted). This approach can cause the call to block for a process that has exited when there are descendants that are keeping the redirected streams open.

    ProcessName

    On Linux, when executing a shell script, Process.ProcessName holds the name of the script. Similarly, Process.GetProcessesByName will match script names. This capability is useful to identify processes regardless of whether they are native executables, scripts, or scripts wrapping native executables.

    Process exit

    Processes hold up some resources in the kernel. On Windows, this information is reference counted, which allows multiple users to keep the information alive using a Process Handle. On Unix, there is a single owner of this information. First, it is the parent process, and when the parent dies, it is the init (pid 1) process (or a process that assumed this responsibility using PR_SET_CHILD_SUBREAPER). The owning process is the process responsible for cleaning up the kernel resources (aka reaping the child). .NET Core reaps child processes as soon as they terminate.

    When the resources are cleaned up, the information about the process can no longer be retrieved. Properties that return runtime information throw InvalidOperationException at that point. On Windows, you can retrieve StartTime, {Privileged,Total,User}ProcessorTime after the process exited. On Linux, these properties throw InvalidOperationException also.

    On Linux, the Process.ExitCode is valid only for direct children. For other processes, it returns 0 or throws InvalidOperationException depending on the state of the Process.

    If you are running in a container, often there is no init process. This means that no one is reaping orphaned children. Such child processes will keep consuming kernel resources, and .NET Core will never consider them exited. This issue occurs when an application has descendants that out-live their parents. If you are in this case, you should add an init process to your container. When using docker/podman run, you can add one using the --init flag.

    Conclusion

    In this article, we explained the behavior of .NET’s Process class on Linux. We covered basic use, non-supported behavior, differences with Windows, and other things to be aware of.

    Last updated: March 29, 2023

    Recent Posts

    • How to run a fraud detection AI model on RHEL CVMs

    • How we use software provenance at Red Hat

    • Alternatives to creating bootc images from scratch

    • How to update OpenStack Services on OpenShift

    • How to integrate vLLM inference into your macOS and iOS apps

    What’s up next?

     

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue