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.
Let’s take a look at the basic syntax:
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 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.
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.