| Linux-Smaps documentation | view source | Contained in the Linux-Smaps distribution. |
Linux::Smaps::VMA objects
Linux::Smaps - a Perl interface to /proc/PID/smaps
use Linux::Smaps; my $map=Linux::Smaps->new($pid); my @vmas=$map->vmas; my $private_dirty=$map->private_dirty; ...
The /proc/PID/smaps files in modern linuxes provides very detailed information about a processes memory consumption. It particularly includes a way to estimate the effect of copy-on-write. This module implements a Perl interface.
The content of the smaps file is a set of blocks like this:
0060a000-0060b000 r--p 0000a000 fd:01 531212 /bin/cat Size: 4 kB Rss: 4 kB Pss: 4 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 4 kB Referenced: 4 kB Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB
Each one describes a virtual memory area of a certain process. All those areas together describe its complete address space. For the meaning of the items refer to your Linux documentation.
The set of information announced by the kernel depends on its version. Early
versions (around Linux 2.6.14) lacked for example Pss, Referenced,
Swap, KernelPageSize and MMUPageSize. Linux::Smaps provides an
interface to all of the components. It creates accessor methods dynamically
depending on what the kernel reveals. The Shared_Clean entry for example
mutates to the Linux::Smaps::VMA->shared_clean accessor. Method
names are built by simply lowercasing them. The actual set of methods is
created when the first smaps file is parsed. Subsequent update
or Linux::Smaps->new operations expect exactly the same file format.
That means you cannot parse smaps files from different kernel versions
by the same perl interpreter.
creates and initializes a Linux::Smaps object. On error an exception is
thrown. new() may fail if the smaps file is not readable or if the file
format is wrong.
new() without parameter is equivalent to new('self') or
new(pid=>'self'). With the procdir parameter the mount point of
the proc filesystem can be set if it differs from the standard /proc.
The filename parameter sets the name of the smaps file directly. This way
also files outside the standard /proc tree can be analyzed.
returns an uninitialized object. This makes new() simply skip the update()
call after setting all parameters. Additional parameters like pid,
procdir or filename can be passed.
get/set parameters.
If a filename is set update() reads that file. Otherwize a file name is
constructed from $self->procdir, $self->pid and the name
smaps. The constructed file name is not saved in the Linux::Smaps
object to allow loops like this:
foreach (@pids) {
$smaps->pid=$_;
$smaps->update;
process $smaps;
}
reinitializes the object; rereads the underlying file. Returns the object
or undef on error. The actual reason can be obtained via lasterror().
writes to the corresponding /proc/PID/clear_refs file. Thus, the amount
of memory reported as Referenced by the process is reset to 0 for
all VMAs.
Returns the object or undef on failure.
Example:
# how much memory is referenced while updating the Linux::Smaps object? perl -MLinux::Smaps -le ' my $s=Linux::Smaps->new; print $s->referenced; print $s->clear_refs->update->referenced ' 2556 840 # how much memory is used while the shell is inactive? perl -MLinux::Smaps -le ' my $s=Linux::Smaps->new(shift); print $s->referenced; print $s->clear_refs->update->referenced ' $$ 1468 0
update() and new() return undef on failure. lasterror() returns
a more verbose reason. Also $! can be checked.
returns a list of Linux::Smaps::VMA objects each describing a vm area,
see below.
these methods compute the sums of the corresponding values of all vmas.
size, rss, shared_clean, shared_dirty, private_clean and
private_dirty methods are unknown until the first call to
Linux::Smaps::update(). They are created on the fly. This is to make
the module extendable as new features are added to the smaps file by the
kernel. As long as the corresponding smaps file lines match
^(\w+):\s*(\d+) kB$ new accessor methods are created.
At the time of this writing at least one new field (referenced) is on
the way but all my kernels still lack it.
these are shortcuts to the corresponding Linux::Smaps::VMA objects.
In array context these functions return a list of Linux::Smaps::VMA
objects representing named or unnamed VMAs or simply all VMAs. Thus, in
array context all() is equivalent to vmas().
In scalar context these functions create a fake Linux::Smaps::VMA object
containing the summaries of the size, rss, shared_clean,
shared_dirty, private_clean and private_dirty fields.
returns a list of vma names, i.e. the files that are mapped.
$other is assumed to be also a Linux::Smaps instance. 3 arrays are
returned. The first one ($new) is a list of vmas that are contained in
$self but not in $other. The second one ($diff) contains a list of pairs
(2-element arrays) of vmas that differ between $self and $other. The
3rd one ($old) is a list of vmas that are contained in $other but not in
$self.
Vmas are identified as corresponding if their vma_start fields match.
They are considered different if they differ in one of the following fields:
vma_end, r, w, x, mayshare, file_off, dev_major,
dev_minor, inode, file_name, shared_clean, shared_diry,
private_clean and private_dirty.
Linux::Smaps::VMA objectsnormally these objects represent a single vm area:
start and end address
describe the file area that is mapped.
the same as vma_end - vma_start but in kB.
what part is resident.
shared means page_count(page)>=2 (see Linux kernel), i.e. the page
is shared between several processes. private pages belong only to one
process.
dirty pages are written to in RAM but not to the corresponding file.
size, rss, shared_clean, shared_dirty, private_clean and
private_dirty methods are unknown until the first call to
Linux::Smaps::update. They are created on the fly. This is to make
the module extendable as new features are added to the smaps file by the
kernel. As long as the corresponding smaps file lines match
^(\w+):\s*(\d+) kB$ new accessor methods are created.
At the time of this writing at least one new field (referenced) is on
the way but all my kernels still lack it.
use strict;
use Linux::Smaps;
my $x="a"x(1024*1024); # a long string of "a"
if( fork ) {
my $s=Linux::Smaps->new($$);
my $before=$s->all;
$x=~tr/a/b/; # change "a" to "b" in place
#$x="b"x(1024*1024); # assignment
$s->update;
my $after=$s->all;
foreach my $n (qw{rss size shared_clean shared_dirty
private_clean private_dirty}) {
print "$n: ",$before->$n," => ",$after->$n,": ",
$after->$n-$before->$n,"\n";
}
wait;
} else {
sleep 1;
}
This script may give the following output:
rss: 4160 => 4252: 92 size: 6916 => 7048: 132 shared_clean: 1580 => 1596: 16 shared_dirty: 2412 => 1312: -1100 private_clean: 0 => 0: 0 private_dirty: 168 => 1344: 1176
$x is changed in place. Hence, the overall process size (size and rss)
would not grow much. But before the tr operation $x was shared by
copy-on-write between the 2 processes. Hence, we see a loss of shared_dirty
(only a little more than our 1024 kB string) and almost the same growth of
private_dirty.
Exchanging the tr-operation to an assingment of a MB of "b" yields the
following figures:
rss: 4160 => 5276: 1116 size: 6916 => 8076: 1160 shared_clean: 1580 => 1592: 12 shared_dirty: 2432 => 1304: -1128 private_clean: 0 => 0: 0 private_dirty: 148 => 2380: 2232
Now we see the overall process size grows a little more than a MB.
shared_dirty drops almost a MB and private_dirty adds almost 2 MB.
That means perl first constructs a 1 MB string of b. This adds 1 MB to
size, rss and private_dirty and then copies it to $x. This
takes another MB from shared_dirty and adds it to private_dirty.
The proc filesystem reports a page as shared if it belongs multiple processes and as private if it belongs to only one process. But there is an exception. If a page is currently paged out (that means it is not in core) all its attributes including the reference count are paged out as well. So the reference count cannot be read without paging in the page. In this case a page is neither reported as private nor as shared. It is only included in the process size.
Thus, to exaclty measure which pages are shared among N processes at least one of them must be completely in core. This way all pages that can possibly be shared are in core and their reference counts are accessible.
The mlockall(2) syscall may help in this situation. It locks all pages of a process to main memory:
require 'syscall.ph';
require 'sys/mmap.ph';
0==syscall &SYS_mlockall, &MCL_CURRENT | &MCL_FUTURE or
die "ERROR: mlockall failed: $!\n";
This snippet in one of the processes locks it to the main memory. If all processes are created from the same parent it is executed best just before the parent starts to fork off children. The memory lock is not inherited by the children. So all private pages of the children are swappable.
Not an Exporter;
Linux Kernel.
Torsten Foertsch, <torsten.foertsch@gmx.net>
Copyright (C) 2005-2011 by Torsten Foertsch
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available.
| Linux-Smaps documentation | view source | Contained in the Linux-Smaps distribution. |