Taking measure of a process’s RAM usage on Windows has always been straightforward.
You launch the Task Manager and browse through the processes to find their corresponding CPU, memory, and disk usage, among other usage metrics.
But Linux distros don’t offer such conveniences, especially if you want to take measure of shared memory. You will need to work with the pmap command to find process metrics.
Here’s a complete guide to using the pmap utility on Linux distros.
The Fundamentals of Memory Mapping
Contemporary OSs allocate memory regions to every process on the machine. Fittingly, these regions are referred to as “allocation spaces.”
Under the hood, modern OSs don’t map allocation spaces to physical hardware addresses. Instead, the OSs generate virtual memory spaces to keep account of every process.
These virtual memory spaces form a vital layer of abstraction of the physical memory. Each process has a translation table associated with it, and the OS’s kernel maintains these tables.
The CPU accesses these tables via the kernel. The kernel inevitably assigns all processes to the CPU core and updates their translation tables while it’s at it. This way, the kernel facilitates interactions between the processes and the CPU’s cores.
The Rewards of Abstraction
When we use any modern OS, we interact with processes in the kernel from outside the kernel in a conceptual space called the “userland.”
The userland allows the operating system – specifically the kernel – to remain secure and stable when applications misbehave.
The use of virtual memory spaces limits access and visibility of the physical memory in the userland. Processes can only access memory as virtual memory addresses assigned by the OS.
Also, processes cannot detect or access the virtual memory addresses assigned to other processes. The only exception to this is when processes are sharing memory.
This abstraction of the physical memory gives the kernel the flexibility to modify the physical address of any virtual memory space.
Virtual memory spaces are sometimes moved to the machine’s disk by changing the physical address a virtual memory space points to.
Modern kernels are designed to avoid providing details of the physical memory address to processes until required, which is rarely the case.
Processes request writing and reading memory, and the kernels grant them access to the virtual memory. However, kernels continually juggle the mapping table to optimize the operating system’s efficiency.
Basics of Shared Memory
The concept of “RAM on demand” coupled with mapping tables opened the doors to sharing memory between processes.
Kernels prioritize not loading a process into physical memory more than once. For instance, you could load a shared library into the memory once, and many processes that need the library can access and use it as necessary.
Every process is assigned a unique virtual memory space for the shared library. However, in our shared library example, each one of these virtual spaces points to the same physical memory space.
If a shared memory space is writable, processes may attempt to write to the physical memory. Kernels use the copy-on-write scheme, which assigns every process a copy of the shared memory to which it is trying to write.
This way, none of the processes access each other’s copies of the shared memory, and the shared memory associated with the original process remains untouched.
The copy-on-write scheme is only one example of any modern Linux distro’s several sophisticated memory handling mechanisms.
Basics of the pmap Utility
The details of how your Linux machine’s kernel works with your machine’s RAM are stored in two pseudo-files in the “/proc” pseudo-filesystem.
Every process has two files associated with it. These files are named after the processes’ Process IDs. The two files are “/proc/maps” and “proc//smaps.”
You don’t have to interact with the pseudo-filesystem to access information about the processes.
The pmap utility can read the information from these files and display it on your terminal. You will need to find the PID of the process you want to check.
Let’s create a process to track. Here’s a brief block of C code that prints a message on the terminal and exits when the user hits Enter:
int main(int argc, char *argv)
You can copy the program and compile it with the gcc compiler with this command:
|gcc -o pm pm.c
Run this command to create an executable named “pm” for the above program. You can run the executable like so:
You will see the expected output “Test program,” and the program won’t terminate till you hit Enter.
Use the ps command to search the PID of this process as it continues to run. The -e flag will show all processes on your machine. It’s a good idea to pipe the output and use grep to find the processes with a “pm” in their name.
|ps -e | grep pm
You can also choose to run the pidof command to find the PID of a process having the name you supply, like so:
Bear in mind that the pidof command only works when you know the precise name of the process. If you only know a part of the name, using the ps command to find the right PID is better.
How to Use pmap to Report Memory Map of a Process
Let’s say the PID you found for the pm process is “40278.” You can now use the pmap command to see the memory mapping for the process like so:
You will see an output with four columns indicating the mapped memory addresses, the memory at the addresses in kilobytes, virtual memory permissions, and the source of the mapping.
This last piece of information may be a library, process, or system name, such as a stack.
The virtual memory permissions define how a process can interact with the mapped memory.
Valid permissions that you will see when you use the pmap utility include:
- R: The mapped memory has no reservation for swap space.
- r: The process may read the mapped memory. The process can read the mapped memory.
- s: Indicates that the virtual memory is shared and all processes can see the changes made to the memory.
- w: The process may write to the mapped memory.
- x: The process may execute instructions present in the mapped memory.
Flags You Can Use with pmap
#1 The Extended Display Flag
Let’s say you run pmap with the -x flag and the example PID we used above, like so:
|pmap -x 40278
You will see a familiar table with columns of information about the memory, but it’ll have two additional columns. These additional columns are called the “RSS” and “Dirty” columns.
The RSS column indicates the “resident set size” of the mapped memory address. This value indicates the amount of memory available in the RAM rather than the swapped-out memory.
The Dirty column indicates the amount of memory that was changed since the inception of the memory mapping.
#2 The “Show Me Everything” Command
You can see virtually all the details about a process if you use pmap with the -X flag. Besides the -X flag, pmap also works with an -XX option, which displays everything the pmap utility can fetch from the kernel.
Interestingly, the -X flag is a subset of the -XX flag. Let’s take a look at how the -XX flag works.
|pmap -XX 40278
As you’d expect, the output of this command will be notably long, and the text will wrap around your terminal in an indecipherable way. The best way to go through this output is to copy and paste the output text into any text document.
You will find that the columns hold a lot of basic information about the supplied PID, including:
- Address: It indicates the start address of the memory mapping.
- Perm: Indicates the memory’s permissions.
- Offset: It indicates the mapping offset of file-based memories.
- Device: You will find your Linux machine’s number in this column in the form of major and minor numbers. If you want to see the device numbers on your machine outside of the pmap command, you can run the lsblk command.
- Inode: In this column, you will find the file’s inode with which the memory mapping is associated.
- Size: Indicates the size of the memory-mapped region.
- KernelPageSize: Indicates the page size the kernel is using.
- MMUPageSize: Indicates the page size the memory management unit is using.
- Rss: This column indicates the amount of memory available in the RAM. This value is referred to as the resident set size.
- Pss: This column indicates the sum of the private shared size and the value resulting from dividing the shared size by the number of shares. The resultant value is known as the proportional share size.
- Shared_Clean: The memory value in this column indicates the shared memory that has not been modified since the mapping’s creation. This memory may be shareable, but it’s still classified as private memory even if it’s not.
- Shared_Dirty: The memory value in this column indicates the shared memory that has been modified since the mapping’s creation.
- Private_Clean: The memory value here indicates the private memory that has remained untouched since the mapping’s creation.
- Private_Dirty: This memory value indicates the amount of modified memory after the mapping’s creation
- Referenced: This is the memory that is currently being accessed or referenced.
- Anonymous: This is the memory that does not have a device to swap out to. Meaning it isn’t file-backed.
- LazyFree: This column indicates the pages flagged as MADV_FREE. These pages can be reclaimed though they may have unwritten changes in them. The MADV_FREE flag is removed from the pages if any changes are made to them after initial flagging. The pages remain unclaimed until the changes are written.
- AnonHugePages: These are pages larger than 4 KB that are not file-backed.
- ShmemPmdMapped: This column indicates the shared memory used by the huge pages that AnonHugePages counts. Filesystems may also use this memory.
- FilePmdMapped: The “Pmd” in the term stands for Page Middle Directory. It is one of the kernel’s paging schemes, and this value indicates the number of file-backed pages that PMD entries are pointing to.
- Shared_Hugetlb: All huge memory pages have Translation Lookaside Tables associated with them, which are memory caches that speed up the process of accessing userspace memory locations. This value indicates the amount of RAM consumed by the TLBs associated with the huge memory pages.
- Private_Hugetlb: It indicates the RAM consumed by the TLBs associated with private huge memory pages.
- Swap: The amount of swap being used.
- SwapPss: The “Pss” is short for proportional share size. So, this value indicates the volume of swap occupied by swapped private memory pages added to the shared size divided by the number of shares.
- Locked: You can lock a memory mapping to prevent the OS from paging out off-heap or heap memory.
- THPeligible: This flag indicates if a memory mapping can allocate transparent huge pages. If the flag value is one, the memory mapping is allowed to allocate the pages. If the flag value is zero, it cannot. Transparent huge pages reduce the overhead of TLB page lookups on machines with a lot of RAM.
- VmFlags: There are several virtual memory flags that you can set. We have covered these below.
- Mapping: This value indicates the mapping’s source. It may be a system name, library name, or process name.
Here is a list of all the available virtual memory flags:
|Area is accountable
|ARM64 bias temperature instability guarded page
|Do not copy this memory region if the process is forked.
|Do not include this memory region in core dumps
|Do not expand this memory region on remapping.
|Disabled write to the mapped file
|Stack segment grows down
|Huge page advise flag
|Area uses huge TLB pages
|Memory-mapped I/O area
|Pages are locked in memory
|Mergeable advise flag
|Mixed map area
|ARM64 Memory tagging extension tags are enabled
|No huge page advise flag
|Swap space is not reserved for the area
|Pure page frame number range. Page frame numbers are a list of the physical memory pages.
|Random read advise provided.
|Soft dirty flag
|Synchronous page fault
|Sequential read advise provided (by the madvise() function.)
|Userfaultfd missing tracking
|Userfaultfd wr-protect tracking
|Wipe this memory region if the process is forked