I was searching in Github in vain for a tool which would I could use as a process monitor, until I found that a tool already exists, and is in fact already shipped with Emacs: proced.el (written by Roland Winkler). To start, we can kick off a Proced buffer by M-x proced, and by default we'll be greeted by something like:

210267266-d63a08b6-001d-4ebe-9680-9572034c288b.png

Within this buffer, we can perform many useful process management operations:

Key Action
k, x Send a signal to the process under point
f Filter processes (for example, user-running will only show processes owned by you which are running)
F Choose between a collection of preset and user-defined attributes to show for each process (called formats)
<ENTER> Refine the current list of processes according to attribute of the process under point (see proced-grammar-alist for some more information on how this works - for example pressing ENTER on the memory column of a given process will change it so that only processes with memory >= to the given process will be shown)
m / u Mark/unmark the process at point, M / U mark/unmark all processes
P Mark a process and its parents
t Toggles marks
r Renice process at point

Many of these commands will use marked processes instead of the process at point if any marked processes exist.

By default, processes will by sorted by CPU usage, this can be changed using s, followed by one of c to sort by CPU, m to sort by memory, p to sort by process ID, s to sort by start time, t to sort by time (= system time + user time), u to sort by user, and finally S will prompt you to choose a sort time based on all process attributes (even if they aren't present in the current format).

Customisation

Off the bat, by default the Proced buffer will not update automatically. An update can be manually triggered via g, but to emulate something similar to top / htop behaviour we can set:

(setq-default proced-auto-update-flag t)
(setq proced-auto-update-interval 1)

proced-auto-update-flag enables auto updating the Proced buffer (by default) every five seconds, and we use proced-auto-update-interval to shorten this to every second. We need setq-default for the first of these rather than setq since proced-auto-update-flag is a buffer-local variable (we can make use of this by calling proced-toggle-auto-update within a Proced buffer which will toggle auto-update without changing the global value of proced-auto-update-flag). I'm also not a fan of the default formats, but it's easy to define one yourself and set this as the default:

(add-to-list
 'proced-format-alist
 '(custom user pid ppid sess tree pcpu pmem rss start time state (args comm)))
(setq-default proced-format 'custom)

The car of the value you're adding is the name of the new format, and the other symbols are values which appear in list-system-processes (for more information see proced-format-alist). list-system-processes also gives a nice rundown on the meaning of each attribute. You can also add your own custom attributes, here's a great example I found in legoscia's Emacs config.

Something else you may notice is that moving down a row also sets the column you're in to args, personally I find this annoying, but you can turn this off:

(setq proced-goal-attribute nil)

The final cherry on top is that from Emacs 29 onwards, you can enable colouring for various attributes:

(setq proced-enable-color-flag t)

Which, using our new default format, leaves us with:

212047844-7531d1be-6920-45ef-b7b5-6b3cdb03c7a2.png

Full customisation (using use-package's :custom to handle the vagaries of global and buffer-local variable customisation, thanks to u/deaddyfreddy from reddit for this)

(use-package proced
  :ensure nil
  :commands proced
  :bind (("C-M-p" . proced))
  :custom
  (proced-auto-update-flag t)
  (proced-goal-attribute nil)
  (proced-show-remote-processes t)
  (proced-enable-color-flag t)
  (proced-format 'custom)
  :config
  (add-to-list
   'proced-format-alist
   '(custom user pid ppid sess tree pcpu pmem rss start time state (args comm))))

Rolling Your Own Formatting for Attributes

proced-grammar-alist opens the door for a lot of control over how attributes are shown in Proced buffers. The documentation goes into a lot of detail, but I'll provide a quick example here to give an idea.

Suppose our goal is to set the colour of Java executables in the args column to that strange orangey-brown colour that everyone seems to associate with Java. We can start by first defining our format function:

(defun my-format-java-args (args)
  (pcase-let* ((base (proced-format-args args))
               (`(,exe . ,rest) (split-string base))
               (exe-prop
                (if (string= exe "java")
                    (propertize exe 'font-lock-face '((t (:foreground "#f89820"))))
                  exe)))
    (mapconcat #'identity (cons exe-prop rest) " ")))

Now, we just need to tell proced-grammar-alist to use this function for the args attribute:

(setf (alist-get 'args proced-grammar-alist)
      '("Args"               ; name of the column
        my-format-java-args  ; format function
        left                 ; alignment within column
        proced-string-lessp  ; defines the sort method (ascending)
        nil                  ; non-nil reverses sort order
       (args pid)            ; sort scheme
       (nil t nil)))         ; refiner for custom refinement logic - see proced-refine

And you should see the results straight away:

212048912-991ba757-f3e3-4abb-b386-0b90fc5dc901.png

Remote Systems

Thanks to Michael Albinus, from Emacs 29 onwards invoking proced when default-directory is remote (for example, your current buffer points to a remote file) and proced-show-remote-processes is non-nil, will prompt Proced to show processes from the remote system instead of your local machine, which can make proced a lot more useful when working with tramp.