Como encontrar o processo com uma página no kernel do Linux?

2

Estou trabalhando em um projeto sobre o kernel Linux mm (gerenciamento de memória) e com uma página, eu preciso encontrar o processo ao qual a página pertence.

No entanto, não consegui encontrar nenhuma maneira sem modificar o kernel. Então, estou realmente me perguntando se é possível fazer isso sem modificar o kernel.

Em detalhe, quando tenho um VMA, também é possível converter o VMA na página correspondente, quero saber a que processo o VMA ou a página pertence.

por exemplo, em mm / page_io.c (da árvore fonte do kernel do linux):

    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.
                 */

Estou aguardando sua ótima resposta. Obrigada!

    
por nickeys 30.01.2017 / 07:41

1 resposta

2

Processar páginas de memória

@ G-Man está correto que o sistema de arquivos /proc pode lhe fornecer as informações. E ele também está correto que /proc/<pid>/maps dará algumas informações. Por exemplo, aqui está um pouco de /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]

O problema aqui é que obtemos as páginas virtuais nesta saída e nenhuma relação com as páginas físicas pode ser encontrada a partir daí.

Por outro lado, /proc também possui um /proc/<pid>/pagemap , que é um mapeamento da memória virtual para as páginas de memória reais (incluindo swap). Vamos ver o que o man 5 proc nos diz sobre isso:

/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, temos um inteiro de 64 bits para cada página de memória e um monte de sinalizadores que nos informam o que a página faz. Para ler /proc/<pid>/pagemap , precisamos de permissões de root. Além disso, o arquivo é apenas uma lista de inteiros de 64 bits, então eu vou usar:

[~]# 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  ................
...

A saída acima é apenas do meu processo de shell. Há muita saída porque é um mapa de toda a memória do processo. E todas as partes que não estão mapeadas no momento simplesmente contêm zeros.

Vamos pegar a página c85e 0600 0000 00a0 (em 0x2120 da saída xxd) que está em uma máquina little-endian, então vamos invertê-la para a000 0000 0006 5ec8 . Como o primeiro byte é 0xa0, o bit 63 é definido, portanto, é uma página na RAM. A numeração de bits é baseada em zero, então o bit 63 é o último bit (não o bit 64). E os últimos 54 bits são 0x65ec8 (com um monte de zeros na frente), que é o índice da página atual.

Agora, a página 2844 0500 0000 0081 (em 0x3650 da saída xxd) pode ser invertida para 8100 0000 0005 4428 . Lá, o primeiro byte é 0x81 e seu bit 62 é definido, portanto, é uma página no SWAP. Não tenho certeza sobre o motivo que o bit 56 está definido: talvez algo indocumentado ou um erro no homem (que observa o bit 55). No entanto, os bits 54-0 nos dizem outras coisas: 0x28 nos dá o tipo de swap, ou seja, 0x8; e o deslocamento está em 0x5442 >> 1 (porque o último bit faz parte do tipo de troca), ou seja, 0x2a21.

(Espero não ter estragado o cálculo flag / little-endian desta vez. Eu estraguei a contagem de bits na primeira vez que escrevi isso.)

Página para processar o problema

O único problema com o acima é que isso é um mapa do processo - > página. Não é realmente página - > processo. Mas você pode pesquisar todos os /proc/kpagecount para ver todas as páginas atualmente usadas (tudo o que tiver uma contagem > 0, que é outra lista de inteiros de 64 bits, um por página). Em seguida, pesquise todos os diretórios proc/<pid> . Isso se torna um problema se os processos começarem e terminarem rapidamente, para que você não encontre o /proc/<pid> . Mas isso não deve ser importante, a menos que você esteja tentando impor políticas de segurança ou algo semelhante. (nunca confie em /proc para políticas de segurança, porque as coisas mudam muito rápido).

Extra

Agora você pode usar o índice 0x54428 para procurar os sinalizadores de página em /proc/kpageflags , será o inteiro 0x54428th 64bit. Isso também é descrito em man 5 proc .

    
por 31.01.2017 / 03:04