30 April 2021

The ps utility displays information about processes. The information shown is similar to the output in the “fields display” of top. The main difference is that ps takes a single snapshot of processes, while top refreshes the output every few seconds. If you are investigating a high system load then top is likely to be more useful, but ps still has its uses.

Unix and BSD options

Most versions of ps support both Unix and BSD options. The difference between the two is that BSD options don’t use a hyphen. If, for instance, you run the well-known command ps aux to display information about all processes for all users (a) together with the process’ user (u) and processes that aren’t attached to a terminal (x) then you are using a BSD-style command. This is different from ps -aux, which prints all processes (-a) owned by the user (-u) named x (x).

The command ps -aux still works if there is no user named x on your system – the utility is smart enough to realise that you probably want to list all processes. Nevertheless, it is best to stick with either the Unix or BSD-style options. In this article I will use Unix-style options.

Listing all processes

The Unix-style alternative for ps aux is ps -e:

# ps -e
   PID TTY          TIME CMD
      1 ?        00:01:00 systemd
      2 ?        00:00:00 kthreadd
     35 ?        00:00:00 oom_reaper
   1647 ?        00:00:05 php-fpm
   1712 ?        00:00:00 php-fpm
   1713 ?        00:00:00 php-fpm
   1726 ?        00:00:00 httpd
   1728 ?        00:00:27 httpd
   1729 ?        00:00:21 httpd
   1730 ?        00:00:21 httpd

I have shortened the output for readability. The main thing to focus on are the four columns that are displayed:

  • PID shows the process ID. The above output is from a Centos 7 server and the first process is therefore systemd.
  • TTY shows that none of the processes are associated with a particular terminal.
  • TIME shows the amount of CPU time the process has consumed.
  • CMD shows the name of the command.

You can make the output more verbose by adding the -f option. This adds four more columns:

# ps -ef
root         1     0  0 Nov20 ?        00:02:00 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
root         2     0  0 Nov20 ?        00:00:00 [kthreadd]
apache    1911  1064  0 19:29 ?        00:00:07 /usr/sbin/httpd -DFOREGROUND
apache    1912  1064  0 19:29 ?        00:00:06 /usr/sbin/httpd -DFOREGROUND
apache    1994  1064  0 19:30 ?        00:00:05 /usr/sbin/httpd -DFOREGROUND
apache    1995  1064  0 19:30 ?        00:00:06 /usr/sbin/httpd -DFOREGROUND
  • The user ID UID) and parent process ID (PPID) should be self-explanatory: the user is the entity that created the process and the parent is the parent process.
  • The C column shows the CPU load, which is calculated as the CPU time divided by the time the process has been running. This information is often more useful than the TIME value as it shows you how hard the processor is being hit. It is not an exact science though. To quote the ps man page: “It will not add up to 100% unless you are lucky.” Also note that this information is unlikely to tell you how much strain your system is currently under – if a process has been running for a long time then any temporary spikes aren’t visible.
  • The STIME column shows when the process was started. If a process was started in a previous year then the column only shows the year.

Customising the output

The -o option lets you specify what columns are displayed. Multiple columns can be separated with commas. For instance, the below command shows the PID, the command and the time that has elapsed since a process was started:

# ps -eo pid,cmd,etime
  PID CMD                             ELAPSED
    1 init                        589-14:35:45
    2 [kthreadd/103022]           589-14:35:45
    3 [khelper/103022]            589-14:35:45
 2528 /usr/sbin/sshd               15-18:07:42
11469 crond                       254-08:04:28

The -o option has lots of possible values – search the man page for standard format specifiers.

Changing the sort order

So far our output has been sorted by the process ID. You can specify another sort key using the --sort option. By default, ps sorts the output in increasing numerical or lexicographic order, which is probably the exact opposite of what you want. It means that if you sort the output by, say, memory usage (pmem) the process with the least amount of memory usage is listed first. You can reverse the sort order by adding a minus sign before the name of the column:

# ps -eo pid,%cpu,pmem,user,comm --sort=-pmem | head
16011 44.1  9.4 mysql    mysqld
28327 10.2  2.2 example  php-fpm
28328  9.0  2.2 example  php-fpm
28370  7.1  1.9 example  php-fpm
 6687  0.5  1.9 nobody   httpd
 4929  0.5  1.9 nobody   httpd
 5219  0.5  1.9 nobody   httpd
 7087  0.5  1.9 nobody   httpd
 7315  0.5  1.9 nobody   httpd

In the above example processes are sorted by memory usage. You can of course also sort the output by CPU usage:

# ps -eo pid,%cpu,pmem,user,comm --sort=-%cpu | head
16011 43.8  9.4 mysql    mysqld
28387  7.8  2.3 example  php-fpm
28327  7.2  2.2 example  php-fpm
28318  6.7  1.9 example  php-fpm
28329  6.6  1.8 example  php-fpm
28388  6.5  2.1 example  php-fpm
28372  6.4  2.0 example  php-fpm
28385  6.4  1.9 example  php-fpm
28319  6.1  2.2 example  php-fpm

Listing a user’s processes

The are a couple of other ps options you should know about. For instance, you can list all the processes that belong to a particular user:

# ps -u example
  PID TTY          TIME CMD
28318 ?        00:01:31 php-fpm
28319 ?        00:00:58 php-fpm
28326 ?        00:00:51 php-fpm
28327 ?        00:01:04 php-fpm
28328 ?        00:00:51 php-fpm

If you get a very long list then you may want to get a count of the processes. The below command tells ps to not print the headers (--no-headers) and it then pipes the output to an awk command that prints the last column. The last three commands produce a count and sort the output:

# ps --no-headers -u example \
| awk '{print $NF}' \
| sort | uniq -c | sort -hr
     21 php-fpm
      1 quota-status

Unfortunately, you can’t use the -e and -u options together. However, if you want to see the output of, say, ps -ef for the user example then you can of course pipe the output to grep:

# ps -ef | grep ^example
example 28318 28301  7 12:00 ?        00:01:34 php-fpm: pool example_example.com
example 28319 28301  5 12:00 ?        00:01:02 php-fpm: pool example_example.com
example 28326 28301  4 12:00 ?        00:00:56 php-fpm: pool example_example.com
example 28327 28301  5 12:00 ?        00:01:06 php-fpm: pool example_example.com
example 28328 28301  4 12:00 ?        00:00:53 php-fpm: pool example_example.com

Note that the caret character (^) is used to only match strings that begin with the string “example”. Also note that you lose the headers when you use grep.