2

I'm working on a project about Linux kernel mm(memory management) and with a page, I need to find the process which the page belongs to.

However, I could not find any way without modifying the kernel. So, I'm really wondering whether it is possible to do that without modifying kernel.

In detail, when I have a VMA then, it is also possible to convert the VMA into the corresponding page, I want to know what process the VMA or page belongs to.

for example, in mm/page_io.c (of linux kernel source tree):

    int __swap_writepage(struct page *page, struct writeback_control *wbc,
        void (*end_write_func)(struct bio *, int))
{
        struct bio *bio;
        int ret, rw = WRITE;
        struct swap_info_struct *sis = page_swap_info(page);
...
        ret = bdev_write_page(sis->bdev, swap_page_sector(page), page, wbc);
        if (!ret) {
                count_vm_event(PSWPOUT);

                /* I should figure out what process is having the page above.
                 * But it is hard to know, because page is managed in LRU and
                 * it is not directly related to its process. What hints I have
                 * are page struct and some data structures which I could
                 * infer from the page only.
                 */

I'm waiting for your great answer. Thank you!

  • 1
    I don't know enough for a great answer, but I have a suggestion: look at /proc.  For example, read /proc/nnn/maps for every process. – G-Man Says 'Reinstate Monica' Jan 30 '17 at 23:31
1

Process memory pages

@G-Man is correct that the /proc filesystem can provide you with the information. And he is also correct that /proc/<pid>/maps will give some info. For example, here is some /proc/<pid>/maps output:

7f2c09a0c000-7f2c09a0d000 r--p 00022000 08:03 3804420                    /usr/lib/ld-2.24.so
7f2c09a0d000-7f2c09a0e000 rw-p 00023000 08:03 3804420                    /usr/lib/ld-2.24.so
7f2c09a0e000-7f2c09a0f000 rw-p 00000000 00:00 0 
7ffc46cf9000-7ffc46d1a000 rw-p 00000000 00:00 0                          [stack]
7ffc46d86000-7ffc46d88000 r--p 00000000 00:00 0                          [vvar]
7ffc46d88000-7ffc46d8a000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

The problem here is that we get the virtual pages in this output, and no relation to the physical pages can be found from there.

On the other hand, /proc also has a /proc/<pid>/pagemap which is a mapping of virtual memory into the actual memory pages (including swap). Let's see what man 5 proc tells us about it:

/proc/[pid]/pagemap (since Linux 2.6.25)
       This  file  shows the mapping of each of the process's virtual pages into physical page frames or swap area.
       It contains one 64-bit value for each virtual page, with the bits set as follows:

            63     If set, the page is present in RAM.

            62     If set, the page is in swap space

            61 (since Linux 3.5)
                   The page is a file-mapped page or a shared anonymous page.

            60-56 (since Linux 3.11)
                   Zero

            55 (Since Linux 3.11)
                   PTE is soft-dirty (see the kernel source file Documentation/vm/soft-dirty.txt).

            54-0   If the page is present in RAM (bit 63), then these bits provide the page frame number, which can
                   be  used  to  index  /proc/kpageflags and /proc/kpagecount.  If the page is present in swap (bit
                   62), then bits 4-0 give the swap type, and bits 54-5 encode the swap offset.

OK, so we have one 64bit integer for each memory page, and a bunch of flags that tell us what the page does. To read /proc/<pid>/pagemap we need root permissions. Also the file is just a list of 64 bit integers so I'll use:

[~]# cat /proc/950/pagemap |xxd |less
...
000020c0: 0585 0600 0000 00a0 0285 0600 0000 00a0  ................
000020d0: 0385 0600 0000 00a0 6692 0600 0000 00a0  ........f.......
000020e0: 6792 0600 0000 00a0 488e 0600 0000 00a0  g.......H.......
000020f0: 498e 0600 0000 00a0 1c93 0600 0000 00a0  I...............
00002100: c45e 0600 0000 00a0 c55e 0600 0000 00a0  .^.......^......
00002110: c65e 0600 0000 00a0 c75e 0600 0000 00a0  .^.......^......
00002120: c85e 0600 0000 00a0 c95e 0600 0000 00a0  .^.......^......
00002130: ca5e 0600 0000 00a0 f05e 0600 0000 00a0  .^.......^......
00002140: f15e 0600 0000 00a0 f25e 0600 0000 00a0  .^.......^......
00002150: f35e 0600 0000 00a0 f45e 0600 0000 00a0  .^.......^......
00002160: f55e 0600 0000 00a0 f65e 0600 0000 00a0  .^.......^......
...
000035c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000035d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000035e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000035f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00003600: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00003610: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00003620: 0000 0000 0000 0000 c460 0400 0000 0081  .........`......
00003630: f02a 0400 0000 0081 b6f7 0100 0000 0081  .*..............
00003640: 804b 0300 0000 0081 0770 0400 0000 0081  .K.......p......
00003650: 2844 0500 0000 0081 3d9b 0400 0000 0081  (D......=.......
00003660: 192f 0400 0000 0081 813c 0300 0000 0081  ./.......<......
00003670: b1f7 0100 0000 0081 40a7 0300 0000 0081  ........@.......
00003680: ee58 0400 0000 0081 97c8 0300 0000 0081  .X..............
00003690: 9afa 0100 0000 0081 0000 0000 0000 0000  ................
000036a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000036b0: 4d80 0400 0000 0081 0bb7 0400 0000 0081  M...............
000036c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
...

The output above is just from my shell process. There is a lot of output there because it is a map of the entire memory of the process. And all parts that are not currently mapped simply contain zeros.

Let's take the page c85e 0600 0000 00a0 (at 0x2120 of the xxd output) which is on a little-endian machine so let's invert it to a000 0000 0006 5ec8. Since the first byte is 0xa0 then the bit 63 is set, so it is a page in RAM. The bit numbering is zero based so the bit 63 is the last bit (not the bit 64). And the last 54 bits are 0x65ec8 (with a bunch of zeros in front) which is the index of the actual page.

Now, the page 2844 0500 0000 0081 (at 0x3650 of the xxd output) can be inverted to 8100 0000 0005 4428. There the first byte is 0x81 and its bit 62 is set so it is a page in SWAP. Am not sure about the reason that bit 56 is set: maybe an something undocumented or a mistake in the man (which notes the bit 55). Nevertheless bits 54-0 tell us other things: 0x28 gives us the swap type i.e. 0x8; and the offset is at 0x5442 >> 1 ('cause the last bit is part of the swap type) i.e. 0x2a21.

(I hope I did not screw up the flag/little-endian calculation this time. I did screw up the bit count the first time I wrote this.)

Page to process problem

The only problem with the above is that that is a map of process -> page. Not really page -> process. But you can search every /proc/kpagecount to see all pages that are currently used (everything that has a count > 0, that is another list of 64 bit integers one per page). And then search all proc/<pid> directories. This becomes a problem if processes start and finish quickly so that you can't find their /proc/<pid>. But that should not be important unless you're trying to enforce security policies or something similar. (never trust /proc for security policies, because things change too fast).

Extra

Now you can use the 0x54428 index to search for the page flags in /proc/kpageflags it will be the 0x54428th 64bit integer. This is also described in man 5 proc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.