They all access the process details maintained in the process directory /proc.
In this article I'll take a look into the pmap command (the link pointing to the current Solaris documentation at Oracle who inherited Solaris after acquiring Sun Microsystems) which allows to investigate the process memory layout in various ways (refer to the link for examples). A user can investigate his own processes, the root user can investigate any process.
pmap output for one process
In the first part of the discussion I will look into the pmap details of one process. As one can see from the output below pmap can answer those questions:
I am using the pmap -x pid
command to get a listing of all components and their address mapping.
Here is the pmap -x
example for a simple 'sleep 50' command.
647: /bin/sleep 50 Address Kbytes RSS Anon Locked Mode Mapped File 08046000 8 8 4 - rw--- [ stack ] 08050000 4 4 - - r-x-- sleep 08061000 4 4 - - rw--- sleep 08062000 8 8 - - rw--- [ heap ] D1C90000 56 24 - - r-x-- methods_unicode.so.3 D1CAD000 4 4 4 - rwx-- methods_unicode.so.3 D1CB0000 1772 36 - - r-x-- de_DE.UTF-8.so.3 D1E7A000 4 4 4 - rwx-- de_DE.UTF-8.so.3 D1E80000 1080 664 - - r-x-- libc.so.1 D1F90000 24 12 12 - rwx-- [ anon ] D1F9E000 32 32 28 - rwx-- libc.so.1 D1FA6000 8 8 8 - rwx-- libc.so.1 D1FC0000 4 4 4 - rwx-- [ anon ] D1FC4000 160 160 - - r-x-- ld.so.1 D1FF0000 4 4 4 - rwx-- [ anon ] D1FF4000 4 4 - - rwxs- [ anon ] D1FFC000 8 8 8 - rwx-- ld.so.1 D1FFE000 4 4 4 - rwx-- ld.so.1 -------- ------- ------- ------- ------- total Kb 3188 992 80 -
Some Notes:
- r--: data, read-only
- rw-: data
- rwx: data, executable
- r-x: this is code, it cannot be overwritten
What I want to do now is simplify and condense the pmap output by
The table below shows calculations for libc.so.1 and [ anon ] rows: how to get from RSS and Anon to shared and private and how to merge multiple data lines into one. There should be only one code line anyway so nothing needs to be done here (other than maybe introduce a check to find out if this is really the case).
RSS Anon Shared Private Shared
MergedPrivate
MergedNew name libc.so.1 r-x 664 0 664 0 664 0 libc.so.1 code libc.so.1 rwx 32 28 4 28 4
= 4 + 036
= 28 + 8libc.so.1 data 8 8 0 8 [ anon ] rwx 12 12 0 12 4
=0 + 0 + 0 + 420
= 12 + 4 + 4 + 0 [ anon ] data 4 4 0 4 4 4 0 4 4 0 4 0
Here is how I want the 'pmap -x' output to look like:
678: /bin/sleep 50 Shared Private Type Mapped File ------------ ------------ ---- ---------- 4 20 data [ anon ] 8 0 data [ heap ] 4 4 data [ stack ] 0 4 data de_DE.UTF-8.so.3 36 0 code de_DE.UTF-8.so.3 0 12 data ld.so.1 160 0 code ld.so.1 4 36 data libc.so.1 664 0 code libc.so.1 0 4 data methods_unicode.so.3 24 0 code methods_unicode.so.3 4 0 code sleep 4 0 data sleep ------------ ------------ ---- ---------- 912 80 TotalLooking at the total line you'll see that adding shared and private
912 + 80 = 992
which is the RSS total in the original pmap output.
Here is a little nawk script to show how it can be done.
NR==1 { header = $0; # first line } $1~/----/ { exit; # no more processing after this line } NR>2 { # Capture 4 columns of interest rss = $3; if(rss=="-") rss = 0; private = $4; if(private=="-") private = 0; mode = substr($6,1,3); file = $7 " " $8 " " $9 " " $10; # Some calculations shared = rss - private; type = "data"; if(mode=="r-x") type = "code"; # Accumulate totals for each (file,type) combination sharedTotal[file,type] +=shared; privateTotal[file,type] +=private; } END { if( header=="" ) exit; print header; printf "%12s %12s %4.4s %s\n", "Shared", "Private", "Type", "Mapped File"; printf "%12s %12s %4.4s %s\n", "------------", "------------", "----", "----------"; shared = 0; private = 0; command = "sort +3"; for( ij in sharedTotal ) { split(ij, a, SUBSEP); printf "%12d %12d %4.4s %s\n", sharedTotal[ij], privateTotal[ij], a[2], a[1] | command ; shared += sharedTotal[ij]; private += privateTotal[ij]; } close(command); printf "%12s %12s %4.4s %s\n", "------------", "------------", "----", "----------"; printf "%12d %12d %4.4s %s\n", shared, private, "", "Total"; }
Note the interesting use of the pipe in printf "..." | command
in the 'for' loop which will sort the printed lines by mapped filename, a construct which does not exist in the old awk.
Also it is necessary to close the file descriptor before printing the footer lines, otherwise they would appear first and the sorted lines would be printed at the very end while finishing the program.
Of course bigger programs lead to bigger pmap output naturally e.g. firefox created more than 600 lines.
Comparing pmap for two (or more) processes
Now what you really want to do is apply this memory check to all of your processes and do a comparison of the totals.When you compare the entries for two different process some of the mapped files will appear in both lists ( libc.so.1 will probably be on each process map). Looking at the shared and private memory there is a significant distinction. The private memory is really private and belongs to just one process whereas the shared memory is shared between processes. The consequences for counting memory are: private memory can simply be counted per process and the total is the sum of all whereas shared memory of two processes is
In order to determine that one has to go through the list of mapped files and check for each of them whether they are unique to the process or shared with the second one.
This idea can be applied to more processes too of course.
This little shell script runs the awk script from above for every pid belonging to USER and stores its output in a file. Another awk script prints the 'Total' line of these files and sums up the values for shared and private and print an overall total.
#!/bin/sh PSLIST=`/bin/ps -u $USER -o pid | sed 1d` [ -z "$PSLIST" ] && exit 1 # Run 'pmap -x' for each process and condense its output with the script above for pid in $PSLIST ; do pmap -x $pid | nawk -f pmapx.awk > pmapx.$pid done # Sort the filenames numerically FILENAMES=`/bin/ls pmapx.* | sort -t. +1n` nawk ' BEGIN { newFile = 1 } newFile==1 { cmd = $0; newFile = 0; next; } /^-----/ { # The dashed lines serve as separators pmap = ++pmap %2; # pmap alternates between 1 and 0 next } pmap==1 { # There is some pmap output to be parsed file = $4 " " $5 " " $6 " " $7; type = $3; # Find the biggest shared if( $1 > shared[type,file] ) shared[type,file] = $1; } /Total/ { # Use the 'Total' line to get the already accumulated private memory private += $2; printf "%12d %12d %s\n", $1, $2, cmd; # Now expect a new file newFile = 1; } END { for( ij in shared ) sharedTotal += shared[ij]; printf "%12s %12s %s\n", "------------", "------------", "---------------"; printf "%12d %12d %s\n", sharedTotal, private, "Total" } ' $FILENAMES
This will lead to this output (shortened a little).
First of all it lists pmap errors as they occur for processes which cannot be examined.
Then the totals of the condensed 'pmap -x' files are shown together with process id and name.
At the end there is a total line but - as explained above - the total shared is not equal to the sum of the shared memory entries in the list whereas the private total is equal to the sum of the private memory in the list.
pmap: cannot examine 627: permission denied ... 1048 24 828: /bin/ksh /usr/dt/bin/Xsession 2180 84 863: /usr/bin/iiimx -iiimd 2740 556 864: iiimd -nodaemon -desktop -udsfile /tmp/.iiim-andreash/:0.0 -vardir /ex 3588 2552 867: /usr/lib/gconfd-2 8 ... 2312 44 920: /usr/dt/bin/sdt_shell -c unsetenv _ PWD; unsetenv DT; 1284 24 922: -csh -c unsetenv _ PWD; unsetenv DT; setenv DISPLAY : 1032 20 934: /bin/ksh /usr/dt/config/Xsession2.jds 15324 404 936: /usr/bin/gnome-session 1752 36 943: /usr/bin/gnome-keyring-daemon 3376 232 948: /usr/lib/bonobo-activation-server --ac-activate --ior-output-fd=23 5072 276 950: gnome-smproxy --sm-client-id default0 10120 436 952: /usr/lib/gnome-settings-daemon --oaf-activate-iid=OAFIID:GNOME_Setting 9556 2960 964: /usr/bin/metacity --sm-client-id=default1 15176 23648 1050: /usr/bin/gnome-terminal ... 1560 32 10640: /bin/bash /usr/bin/firefox 1588 28 10652: /bin/bash /usr/lib/firefox/run-mozilla.sh /usr/lib/firefox/firefox-bin 40628 98560 10656: /usr/lib/firefox/firefox-bin 1060 60 22265: sh 1248 48 28233: csh 1524 48 29139: vi ------------ ------------ --------------- 72260 148444 Total
The root user could run this script for all users in order to get an overview of all users.