1. Overview

Sometimes, we want to close unresponsive programs or check if a background process has started in our Linux system.

In this quick tutorial, we’ll explore how the ps process-monitoring command can help us in such situations.

2. Syntax

Let’s take a look at the basic syntax:

ps [options]

Since all arguments are optional, let’s see what it does with no options:

$ ps 
  PID TTY          TIME CMD
14900 pts/1    00:00:00 bash
14925 pts/1    00:00:00 ps

By default, it prints the processes of the current user and terminal in four columns:

  • PID – the process id
  • TTY – terminal associated with the process
  • TIME – elapsed CPU utilization time for the process
  • CMD – the executable command

Also, unlike the top command, we can only see a snapshot of this information at a given time.

Due to historical reasons, ps accepts options in various formats:

  • not preceded with a dash (BSD style)
  • preceded with a dash (UNIX style)
  • preceded with two dashes (GNU style)

While most options have equivalents in all three formats, we’ll be focusing primarily on UNIX-style syntax throughout the remainder of the article.

3. Basic Usage

Now that we’ve seen the default output, it’s time to see a few of the options in action.

3.1. Listing All Processes

Let’s use the -e flag to print all the processes within the system, not just the ones from the current terminal:

$ ps -e
  PID TTY          TIME CMD
    1 ?        00:00:25 systemd
    2 ?        00:00:00 kthreadd
    3 ?        00:00:00 rcu_gp
  ...

We can also see a more detailed output by using the -f option:

$ ps -e -f
  UID        PID  PPID  C STIME TTY          TIME CMD
  root         1     0  0 Feb11 ?        00:00:26 /usr/lib/systemd/systemd --switched-root --system --deserialize 28
  root         2     0  0 Feb11 ?        00:00:00 [kthreadd]
  root         3     2  0 Feb11 ?        00:00:00 [rcu_gp]
  ...

Let’s take a closer look at this example to understand it better. We now have some additional columns:

  • UID – the user id of the process owner
  • PPID – the parent process id (in this particular snippet, rcu_gp was spawned by kthread)
  • C – the CPU utilization in percentage
  • STIME – the start time of the process

Furthermore, when ps can identify the process arguments, it will also print them in the CMD column.

3.2. Simple Filtering

In practice, we’re most likely searching for a particular process by name with the -C option:

$ ps -C systemd
  PID TTY          TIME CMD
    1 ?        00:00:06 systemd
 1658 ?        00:00:00 systemd

Notice that we use the complete process executable name rather than a substring.

Additionally, ps also allows us to filter based on a list of process ids using the -p flag:

$ ps -p 1,1658
  PID TTY          TIME CMD
    1 ?        00:00:06 systemd
 1658 ?        00:00:00 systemd

We can also search by the user name by specifying it in the -u option:

$ ps -u root
  PID TTY          TIME CMD
    1 ?        00:00:08 systemd
    2 ?        00:00:00 kthreadd
    3 ?        00:00:00 rcu_gp
   ...

There is one particular gotcha here, but we’ll explain it in detail in the next section.

4. Advanced Usage

Let’s now take a look at more advanced options.

4.1. Listing Threads

In some situations, it might be useful to know the spawned threads of a particular process:

$ ps -C gedit -L 
  PID   LWP TTY          TIME CMD
12050 12050 ?        00:00:02 gedit
12050 12051 ?        00:00:00 gmain
12050 12052 ?        00:00:00 gdbus
12050 12054 ?        00:00:00 dconf worker

Let’s see what happened. We first started the gedit text editor and typed some random strings inside it.

Then, we filtered by the process name and passed the -L option to print out the spawned threads.

The additional LWP column represents the thread id.

If we want additional output, we can also combine this with the -f option:

$ ps -C gedit -L -f 
  UID       PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
  user    12050  1658 12050  0    4 21:36 ?        00:00:03 /usr/bin/gedit --gapplication-service
  user    12050  1658 12051  0    4 21:36 ?        00:00:00 /usr/bin/gedit --gapplication-service
  user    12050  1658 12052  0    4 21:36 ?        00:00:00 /usr/bin/gedit --gapplication-service
  user    12050  1658 12054  0    4 21:36 ?        00:00:00 /usr/bin/gedit --gapplication-service

We now obtain the total number of threads of the process in the NLWP column.

4.2. Listing Child Processes

Sometimes we want to see the spawned child processes rather than threads. We can achieve this with the -H option:

$ ps -e -H
  PID TTY          TIME CMD
 ...
 2493 ?        00:01:36       firefox
 2562 ?        00:00:03         Web Content
 2614 ?        00:00:09         WebExtensions
 2688 ?        00:00:32         Web Content
 2730 ?        00:00:33         Web Content
 2949 ?        00:00:09         Web Content
 ...

In this particular case, we can see a process hierarchy of all the processes in the system. Unfortunately, we cannot filter directly by the process id or process name.

However, we can filter by session id (SID). To obtain it, we need to modify how ps prints the output. We’ll explain how this works in detail in the next section.

First, let’s get the session id for our process:

$ ps -C firefox -o pid,sid,cmd
  PID   SID CMD
 2493  1874 /usr/lib64/firefox/firefox

Then, we can filter by SID using the -g flag to obtain a smaller output:

$ ps -g 1874 -H 
  PID TTY          TIME CMD
 2125 ?        00:00:00 ibus-x11
 1874 ?        00:00:46 gnome-shell
 1919 ?        00:00:20   Xwayland
 2116 ?        00:00:02   ibus-daemon
 2122 ?        00:00:00     ibus-dconf
 2123 ?        00:00:00     ibus-extension-
 2214 ?        00:00:01     ibus-engine-sim
 2493 ?        00:01:58   firefox
 2562 ?        00:00:04     Web Content
 2614 ?        00:00:10     WebExtensions
 2688 ?        00:00:36     Web Content
 2730 ?        00:00:36     Web Content
 2949 ?        00:00:09     Web Content

Let’s focus a bit on this output. The session id is equal to the process id that started the session — also called the session leader. In this case, the session leader is the gnome-shell process.

The session leader spawned several child processes, including our process of interest. We can now see them in a tree-view output.

4.3. Controlling the Output

So far, we have only seen the default output and the more detailed output. This is not very helpful in some cases, especially if we want to automatically process the output of ps with the help of other utilities such as read.

We can control which columns are printed with the help of the -o flag:

$ ps -C gedit -L -o pid,lwp,time,comm
  PID   LWP     TIME COMMAND
12050 12050 00:00:03 gedit
12050 12051 00:00:00 gmain
12050 12052 00:00:00 gdbus
12050 12054 00:00:00 dconf worker

We can also influence the order in which we print them:

$ ps -C gedit -L -o lwp,comm,time,pid
  LWP COMMAND             TIME   PID
12050 gedit           00:00:03 12050
12051 gmain           00:00:00 12050
12052 gdbus           00:00:00 12050
12054 dconf worker    00:00:00 12050

The ps command supports a multitude of output modifiers. For their complete descriptions, we can always consult the man page.

Since there are a lot of modifiers, let’s just experiment with some of the more interesting ones:

$ ps -C gedit -o comm,pid,rss,pmem,stat,vsz
COMMAND           PID   RSS %MEM STAT     VSZ
gedit           12050 63072  0.1 Sl    371060

Let’s explain this output a bit:

  • RSS represents the non-swapped physical memory used, in kilobytes
  • %MEM is the percentage of the physical memory the process uses (ratio between RSS and total physical memory of the system)
  • STAT is the multi-character process state (in this case, the process is multi-threaded and in interruptible sleep)
  • VSZ represents the virtual memory size of the process in kilobytes

4.4. Effective and Real User Name

Remember that we previously talked about filtering by a user name.

When it comes to processes, in Linux, we distinguish between two types of user names: real and effective.

The real user name is the one that started the process. The effective user is the one that owns the executable behind the process.

Let’s run the passwd utility in a separate terminal and let it wait for our prompt:

$ passwd
Changing password for user.
Current password:

Now, let’s see what happens if we print out the real and effective user names with ps:

$ ps -C passwd -o pid,tty,ruser,user,cmd
  PID TT       RUSER    USER     CMD
12503 pts/2    user     root     passwd

The real user is different from the effective user. That’s because passwd is owned by root but has been called by our user.

This particular behavior is valid for setuid compatible executables. It happens when the current user needs to run programs with temporarily elevated privileges.

The ps command can filter by real user names with the -U option and by effective user names with the -u option.

5. Conclusion

In this quick tutorial, we’ve seen how to use the ps command.

We first covered the basics of filtering processes by name, process id, and user. Then, we jumped into advanced topics like process trees and threads.

Finally, we explored how output can be customized and explained the different types of user names.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.