Finding out how much RAM a Linux process uses isn’t a simple matter—especially
when shared memory needs to be considered. Thankfully, the
pmap command helps you make sense of it all.
Memory Management with pmap Process ID in linux
Memory Management
In Ram we have limited no of size, every process which
gets executed goes in RAM, there is allocation &
deallocation done by OS. In RAM after this process is done.
Memory Mapping
Memory mapping is the translation between the logical address space and
the physical memory.
The objectives of memory mapping are:
- To translate from logical to physical address.
- To aid in memory protection.
- To enable better management of memory resources.
RAM on Demand
The mapping table and the concept of “RAM on demand” open up the
possibility of shared memory. The kernel will try to avoid loading the same
thing into memory more than once. For example, it will load a shared library
into memory once and map it to the different processes that need to use it.
Each of the processes will have its own unique address for the shared library,
but they’ll all point to the same actual location.
If the shared
region of memory is writable, the kernel uses a scheme called copy-on-write.
If one process writes to the shared memory and the other processes sharing
that memory are not supposed to see the changes, a copy of the shared memory
is created at the point of the write request.
The pmap Utility
The kernel exposes a lot of what it is doing with RAM through two
pseudo-files in the “/proc” system information pseudo-filesystem. There are
two files per process, named for the process ID or PID of each process:
“/proc/maps” and “/proc//smaps.”
The pmap tool reads
information from these files and displays the results in the terminal window.
It’ll be obvious that we need to provide the PID of the process we’re
interested in whenever we usepmap.
PMAP(1) User Commands PMAP(1) NAME pmap - report memory map of a process SYNOPSIS pmap [options] pid [...] DESCRIPTION The pmap command reports the memory map of a process or processes. OPTIONS -x, --extended Show the extended format. -d, --device Show the device format. -q, --quiet Do not display some header or footer lines. -A, --range low,high Limit results to the given range to low and high address range. Notice that the low and high arguments are single string separated with comma. -X Show even more details than the -x option. WARNING: format changes according to /proc/PID/smaps -XX Show everything the kernel provides -p, --show-path Show full path to files in the mapping column -c, --read-rc Read the default configuration -C, --read-rc-from file Read the configuration from file -n, --create-rc Create new default configuration -N, --create-rc-to file Create new configuration to file -h, --help Display help text and exit. -V, --version Display version information and exit. EXIT STATUS 0 Success. 1 Failure. 42 Did not find all processes asked for. SEE ALSO ps(1), pgrep(1) STANDARDS No standards apply, but pmap looks an awful lot like a SunOS command. REPORTING BUGS Please send bug reports to ⟨procps@freelists.org⟩ procps-ng 2020-06-04 PMAP(1) ~ ~ ~ ~ ~ ~
Finding the Process ID
There are several ways to find the PID of a process. Here’s the source
code for a trivial program we’ll use in our examples. It is written in C. All
it does is print a message to the terminal window and wait for the user to hit
the “Enter” key.
#include <stdio.h> int main(int argc, char *argv[]) { printf("Thank you for visiting Hacking Truth."); getc(stdin); } // end of main
The program was compiled to an executable called pm using the gcc compiler:
gcc -o pm pm.c
Because the program will wait for the user to hit “Enter”, it’ll stay running for as long as we like.
The program launches, prints the message, and waits for the keystroke. We can now search for its PID. The ps command lists running processes. The -e (show all processes) option makes ps list every process. We’ll pipe the output through grep and filter out entries that have “pm” in their name.
ps -e | grep pm
This lists all of the entries with “pm” anywhere in their names.
We
can be more specific using the pidof command. We give pidof the name of the
process we’re interested in on the command line, and it tries to find a match.
If a match is found, pidof prints the PID of the matching process.
pidof pm
┌──(hackerboy㉿KumarAtulJaiswal)-[~/Desktop/pmap] └─$ pidof pm 58456 ┌──(hackerboy㉿KumarAtulJaiswal)-[~/Desktop/pmap] └─$
The pidof method is neater when you know the name of the process, but the ps
method will work even if only know part of the process name.
Using
pmap
With our test program running, and once we’ve identified its
PID, we can use pmap like this:
pmap 58456
┌──(hackerboy㉿KumarAtulJaiswal)-[~/Desktop/pmap] └─$ pmap 58456 58456: ./pm ┌──(hackerboy㉿KumarAtulJaiswal)-[~/Desktop/pmap] └─$
The memory mappings for the process are listed for us.
40919: ./pm 000056059f06c000 4K r---- pm 000056059f06d000 4K r-x-- pm 000056059f06e000 4K r---- pm 000056059f06f000 4K r---- pm 000056059f070000 4K rw--- pm 000056059fc39000 132K rw--- [ anon ] 00007f97a3edb000 8K rw--- [ anon ] 00007f97a3edd000 160K r---- libc.so.6 00007f97a3f05000 1616K r-x-- libc.so.6 00007f97a4099000 352K r---- libc.so.6 00007f97a40f1000 4K ----- libc.so.6 00007f97a40f2000 16K r---- libc.so.6 00007f97a40f6000 8K rw--- libc.so.6 00007f97a40f8000 60K rw--- [ anon ] 00007f97a4116000 4K r---- ld-linux-x86-64.so.2 00007f97a4117000 160K r-x-- ld-linux-x86-64.so.2 00007f97a413f000 40K r---- ld-linux-x86-64.so.2 00007f97a4149000 8K r---- ld-linux-x86-64.so.2 00007f97a414b000 8K rw--- ld-linux-x86-64.so.2 00007ffca0e7e000 132K rw--- [ stack ] 00007ffca0fe1000 16K r---- [ anon ] 00007ffca0fe5000 8K r-x-- [ anon ] ffffffffff600000 4K --x-- [ anon ] total 2756K
The first line is the process name and its PID. Each of the other lines shows
a mapped memory address, and the amount of memory at that address, expressed
in kilobytes. The next five characters of each line are called virtual memory
permissions. Valid permissions are:
- r: The mapped memory can be read by the process.
- w: The mapped memory can be written by the process.
- x: The process can execute any instructions contained in the mapped memory.
- s: The mapped memory is shared, and changes made to the shared memory are visible to all of the processes sharing the memory.
- R: There is no reservation for swap space for this mapped memory.
The final information on each line is the name of the source of the mapping. This can be a process name, library name, or a system name such as stack or heap.
The Extended Display
The -x (extended) option provides two extra columns.
pmap -x 58456
┌──(hackerboy㉿KumarAtulJaiswal)-[~] └─$ pmap -x 59078 59078: /bin/zsh Address Kbytes RSS Dirty Mode Mapping 0000556453c15000 92 92 0 r---- zsh 0000556453c2c000 596 596 0 r-x-- zsh 0000556453cc1000 136 64 0 r---- zsh 0000556453ce4000 8 8 8 r---- zsh 0000556453ce6000 24 24 24 rw--- zsh 0000556453cec000 80 36 36 rw--- [ anon ] 0000556454e5c000 1836 1796 1796 rw--- [ anon ] 00007f92590fd000 4 4 0 r---- regex.so 00007f92590fe000 4 4 0 r-x-- regex.so 00007f92590ff000 4 4 0 r---- regex.so 00007f9259100000 4 4 4 r---- regex.so 00007f9259101000 4 4 4 rw--- regex.so 00007f9259102000 100 44 0 r--s- Misc.zwc 00007f925911b000 144 44 0 r--s- Base.zwc 00007f925913f000 4 4 0 r---- stat.so 00007f9259140000 8 8 0 r-x-- stat.so 00007f9259142000 4 4 0 r---- stat.so 00007f9259143000 4 4 4 r---- stat.so 00007f9259144000 4 4 4 rw--- stat.so 00007f9259168000 4 4 0 r---- zleparameter.so 00007f9259169000 4 4 0 r-x-- zleparameter.so 00007f925916a000 4 4 0 r---- zleparameter.so 00007f925916b000 4 4 4 r---- zleparameter.so 00007f925916c000 4 4 4 rw--- zleparameter.so 00007f9259171000 96 60 0 r--s- Completion.zwc 00007f9259189000 12 12 0 r---- parameter.so 00007f925918c000 20 16 0 r-x-- parameter.so 00007f9259191000 8 8 0 r---- parameter.so 00007f9259193000 4 0 0 ----- parameter.so 00007f9259194000 4 4 4 r---- parameter.so 00007f9259195000 4 4 4 rw--- parameter.so 00007f9259196000 8 8 0 r---- zutil.so 00007f9259198000 20 20 0 r-x-- zutil.so 00007f925919d000 4 4 0 r---- zutil.so 00007f925919e000 4 0 0 ----- zutil.so 00007f925919f000 4 4 4 r---- zutil.so 00007f92591a0000 4 4 4 rw--- zutil.so 00007f92591a1000 32 32 0 r---- complete.so 00007f92591a9000 104 72 0 r-x-- complete.so 00007f92591c3000 12 12 0 r---- complete.so 00007f92591c6000 8 8 8 r---- complete.so 00007f92591c8000 4 4 4 rw--- complete.so 00007f92591c9000 92 92 0 r---- zle.so 00007f92591e0000 168 168 0 r-x-- zle.so 00007f925920a000 40 40 0 r---- zle.so 00007f9259214000 8 8 8 r---- zle.so 00007f9259216000 28 28 28 rw--- zle.so 00007f925921d000 4 4 4 rw--- [ anon ] 00007f9259222000 12 12 0 r---- libnss_files-2.33.so 00007f9259225000 24 24 0 r-x-- libnss_files-2.33.so 00007f925922b000 8 8 0 r---- libnss_files-2.33.so 00007f925922d000 4 4 4 r---- libnss_files-2.33.so 00007f925922e000 4 4 4 rw--- libnss_files-2.33.so 00007f925922f000 24 0 0 rw--- [ anon ] 00007f9259235000 2972 540 0 r---- locale-archive 00007f925951c000 8 8 8 rw--- [ anon ] 00007f925951e000 152 152 0 r---- libc-2.33.so 00007f9259544000 1312 1200 0 r-x-- libc-2.33.so 00007f925968c000 300 208 0 r---- libc-2.33.so 00007f92596d7000 4 0 0 ----- libc-2.33.so 00007f92596d8000 12 12 12 r---- libc-2.33.so 00007f92596db000 12 12 12 rw--- libc-2.33.so 00007f92596de000 36 20 20 rw--- [ anon ] 00007f92596e7000 60 60 0 r---- libm-2.33.so 00007f92596f6000 616 296 0 r-x-- libm-2.33.so 00007f9259790000 608 0 0 r---- libm-2.33.so 00007f9259828000 4 4 4 r---- libm-2.33.so 00007f9259829000 4 4 4 rw--- libm-2.33.so 00007f925982a000 56 56 0 r---- libtinfo.so.6.3 00007f9259838000 64 64 0 r-x-- libtinfo.so.6.3 00007f9259848000 56 56 0 r---- libtinfo.so.6.3 00007f9259856000 16 16 16 r---- libtinfo.so.6.3 00007f925985a000 4 4 4 rw--- libtinfo.so.6.3 00007f925985b000 8 8 0 r---- libdl-2.33.so 00007f925985d000 8 8 0 r-x-- libdl-2.33.so 00007f925985f000 4 0 0 r---- libdl-2.33.so 00007f9259860000 4 4 4 r---- libdl-2.33.so 00007f9259861000 4 4 4 rw--- libdl-2.33.so 00007f9259862000 12 12 0 r---- libcap.so.2.44 00007f9259865000 16 16 0 r-x-- libcap.so.2.44 00007f9259869000 8 0 0 r---- libcap.so.2.44 00007f925986b000 4 4 4 r---- libcap.so.2.44 00007f925986c000 4 4 4 rw--- libcap.so.2.44 00007f925986d000 8 8 8 rw--- [ anon ] 00007f9259872000 4 4 0 r---- terminfo.so 00007f9259873000 4 4 0 r-x-- terminfo.so 00007f9259874000 4 4 0 r---- terminfo.so 00007f9259875000 4 4 4 r---- terminfo.so 00007f9259876000 4 4 4 rw--- terminfo.so 00007f925987b000 28 28 0 r--s- gconv-modules.cache 00007f9259882000 16 16 16 rw--- [ anon ] 00007f9259886000 4 4 0 r---- ld-2.33.so 00007f9259887000 144 144 0 r-x-- ld-2.33.so 00007f92598ab000 40 40 0 r---- ld-2.33.so 00007f92598b5000 8 8 8 r---- ld-2.33.so 00007f92598b7000 8 8 8 rw--- ld-2.33.so 00007ffceadbc000 164 164 164 rw--- [ stack ] 00007ffceadf5000 16 0 0 r---- [ anon ] 00007ffceadf9000 8 4 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 10680 6648 2272 ┌──(hackerboy㉿KumarAtulJaiswal)-[~] └─$
The columns are given titles. We have already seen the “Address”, “Kbytes”,
“Mode”, and “Mapping” columns. The new columns are called “RSS” and
“Dirty.”
- RSS: This is the resident set size. That is, the amount of memory that is currently in RAM, and not swapped out.
- Dirty: “Dirty” memory has been changed since the process—and the mapping—started.
Show Me Everything
The -X (even more than extended) adds additional columns
to the output. Note the uppercase “X.” Another option called -XX (even more
than -X ) shows you everything pmap can get from the kernel. As -X is a subset
of -XX, we’ll describe the output from -XX .
pmap -XX 58456
┌──(hackerboy㉿KumarAtulJaiswal)-[~] └─$ pmap -XX 59078 59078: /bin/zsh Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible VmFlags Mapping 556453c15000 r--p 00000000 08:08 6302183 92 4 4 92 30 92 0 0 0 92 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd zsh 556453c2c000 r-xp 00017000 08:08 6302183 596 4 4 596 198 596 0 0 0 596 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me dw sd zsh 556453cc1000 r--p 000ac000 08:08 6302183 136 4 4 64 21 64 0 0 0 64 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd zsh 556453ce4000 r--p 000ce000 08:08 6302183 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw ac sd zsh 556453ce6000 rw-p 000d0000 08:08 6302183 24 4 4 24 24 0 0 0 24 24 24 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me dw ac sd zsh 556453cec000 rw-p 00000000 00:00 0 80 4 4 36 36 0 0 0 36 36 36 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd 556454e5c000 rw-p 00000000 00:00 0 1836 4 4 1800 1800 0 0 0 1800 1800 1800 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd [heap] 7f92590fd000 r--p 00000000 08:08 265771 4 4 4 4 1 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me sd regex.so
There’s a lot of information here. This is what the columns hold:
Address:
The start address of this mapping. This uses virtual memory addressing.
Perm:
The permissions of the memory.
Offset: If the memory is
file-based, the offset of this mapping inside the file.
Device: The Linux device number, given in major and minor
numbers. You can see the device numbers on your computer by running the lsblk
command.
Inode: The inode of the file the mapping is
associated with. For example, in our example, this could be the inode that
holds information about the pm program.
Size: The size of
the memory-mapped region.
KernelPageSize: The page size used
by the kernel.
MMUPageSize: The page size used by the memory
management unit.
Rss: This is the
resident set size. That is, the amount of memory that is currently in RAM, and
not swapped out.
Pss: This is the proportional share size.
This is the private shared size added to the (shared size divided by the
number of shares.)
Shared_Clean: The amount of memory shared
with other processes that has not been altered since the mapping was created.
Note that even if memory is shareable, if it hasn’t actually been shared it is
still considered private memory.
Shared_Dirty: The amount of
memory shared with other processes that has been altered since the mapping was
created.
Private_Clean: The amount of private memory—not
shared with other processes—that has not been altered since the mapping was
created.
Private_Dirty: The amount of private memory that
has been altered since the mapping was created.
Referenced: The amount of memory currently marked as referenced or accessed.
Anonymous: Memory that does not have a device to swap out to. That is, it isn’t
file-backed.
LazyFree: Pages that have been flagged as
MADV_FREE. These pages have been marked as available to be freed and
reclaimed, even though they may have unwritten changes in them. However, if
subsequent changes occur after the MADV_FREE has been set on the memory
mapping, the MADV_FREE flag is removed and the pages will not be reclaimed
until the changes are written.
AnonHugePages: These are
non-file backed “huge” memory pages (larger than 4 KB).
ShmemPmdMapped: Shared memory associated with huge pages. They may also be used by
filesystems that reside entirely in memory.
FilePmdMapped: The Page Middle Directory is one of the paging schemes available to the
kernel. This is the number of file-backed pages pointed to by PMD entries.
Shared_Hugetlb:
Translation Lookaside Tables, or TLBs, are memory caches used to optimize the
time taken to access userspace memory locations. This figure is the amount of
RAM used in TLBs that are associated with shared huge memory pages.
Private_Hugetlb: This figure is the amount of RAM used in TLBs that are associated with
private huge memory pages.
Swap: The amount of swap being
used.
SwapPss: The swap proportional share size. This is the
amount of swap made up of swapped private memory pages added to the (shared
size divided by the number of shares.)
Locked: Memory
mappings can be locked to prevent the operating system from paging out heap or
off-heap memory.
THPeligible: This is a flag indicating
whether the mapping is eligible for allocating transparent huge pages. 1 means
true, 0 means false. Transparent huge pages is a memory management system that
reduces the overhead of TLB page lookups on computers with a large amount of
RAM.
VmFlags: See the list of flags below.
Mapping: The name of the source of the mapping. This can be a process name, library
name, or system names such as stack or heap.
The VmFlags—virtual memory flags—will be a subset of the following list.
rd: Readable.
wr: Writeable.
ex:
Executable.
sh: Shared.
mr: May read.
mw: May write.
me: May execute.
ms:
May share.
gd: Stack segment grows down.
pf: Pure page frame number range. Page frame numbers are a list of the physical
memory pages.
dw: Disabled write to the mapped
file.
lo: Pages are locked in memory.
io:
Memory-mapped I/O area.
sr: Sequential read
advise provided (by the madvise() function.)
rr: Random read advise provided.
dc: Do not copy
this memory region if the process is forked.
de: Do not expand this memory region on remapping.
ac: Area is accountable.
nr: Swap space
is not reserved for the area.
ht: Area uses
huge TLB pages.
sf: Synchronous page fault.
ar:
Architecture-specific flag.
wf: Wipe this
memory region if the process is forked.
dd: Do
not include this memory region in core dumps.
sd: Soft dirty flag.
mm: Mixed map
area.
hg: Huge page advise flag.
nh: No huge page advise flag.
mg:
Mergeable advise flag.
bt: ARM64 bias
temperature instability guarded page.
mt: ARM64
Memory tagging extension tags are enabled.
um:
Userfaultfd missing tracking.
uw: Userfaultfd
wr-protect tracking.
One final neat trick is that you can use pmap and the pidof commands together,
combining the actions of finding the PID of the process and passing it to pmap
into one command:
pmap $(pidof pm)
┌──(hackerboy㉿KumarAtulJaiswal)-[~] └─$ pmap $(pidof pm) 58456: ./pm ┌──(hackerboy㉿KumarAtulJaiswal)-[~] └─$ 1 ⨯