Por que meu processo demora muito para morrer?

1

Basicamente, estou usando o Linux 2.6.34 no PowerPC (Freescale e500mc). Eu tenho um processo (um tipo de máquina virtual que foi desenvolvido internamente) que usa cerca de 2,25 G de VM mlocked. Quando eu mato, percebo que leva mais de 2 minutos para terminar.

Eu investiguei um pouco. Primeiro, fechei todos os descritores de arquivos abertos, mas isso não pareceu fazer diferença. Então eu adicionei alguns printk no kernel e através dele eu descobri que todo atraso vem do kernel destravando meus VMAs. O atraso é uniforme nas páginas, o que verifiquei verificando repetidamente a contagem de páginas bloqueadas em / proc / meminfo. Verifiquei com programas que alocam tanta memória e todos eles morrem assim que eu os sinalizo.

O que você acha que eu deveria verificar agora? Obrigado pelas suas respostas.

Editar: eu tive que encontrar uma maneira de compartilhar mais informações sobre o problema, então eu escrevi este programa abaixo:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>

#define MAP_PERM_1              (PROT_WRITE | PROT_READ | PROT_EXEC)
#define MAP_PERM_2              (PROT_WRITE | PROT_READ)

#define MAP_FLAGS               (MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE)

#define PG_LEN                  4096
#define align_pg_32(addr)       (addr & 0xFFFFF000)
#define num_pg_in_range(start, end)     ((end - start + 1) >> 12)

inline void __force_pgtbl_alloc(unsigned int start)
{
        volatile int *s = (int *) start;
        *s = *s;
}

int __map_a_page_at(unsigned int start, int whichperm)
{
        int perm = whichperm ? MAP_PERM_1 : MAP_PERM_2;

        if(MAP_FAILED == mmap((void *)start, PG_LEN, perm, MAP_FLAGS, 0, 0)){
                fprintf(stderr,
                        "mmap failed at 0x%x: %s.\n",
                        start, strerror(errno));
                return 0;
        }

        return 1;
}

int __mlock_page(unsigned int addr)
{
        if (mlock((void *)addr, (size_t)PG_LEN) < 0){
                fprintf(stderr,
                        "mlock failed on page: 0x%x: %s.\n",
                        addr, strerror(errno));
                return 0;
        }

        return 1;
}

void sigint_handler(int p)
{
        struct timeval start = {0 ,0}, end = {0, 0}, diff = {0, 0};
        gettimeofday(&start, NULL);
        munlockall();
        gettimeofday(&end, NULL);
        timersub(&end, &start, &diff);

        printf("Munlock'd entire VM in %u secs %u usecs.\n",
                diff.tv_sec, diff.tv_usec);

        exit(0);
}

int make_vma_map(unsigned int start, unsigned int end)
{
        int num_pg = num_pg_in_range(start, end);

        if (end < start){
                fprintf(stderr,
                        "Bad range: start: 0x%x end: 0x%x.\n",
                        start, end);
                return 0;
        }

        for (; num_pg; num_pg --, start += PG_LEN){
                if (__map_a_page_at(start, num_pg % 2) && __mlock_page(start))
                        __force_pgtbl_alloc(start);
                else
                        return 0;
        }

        return 1;
}

void display_banner()
{
        printf("-----------------------------------------\n");
        printf("Virtual memory allocator. Ctrl+C to exit.\n");
        printf("-----------------------------------------\n");
}

int main()
{
        unsigned int vma_start, vma_end, input = 0;
        int start_end = 0; // 0: start; 1: end;

        display_banner();

        // Bind SIGINT handler.
        signal(SIGINT, sigint_handler);

        while (1){
                if (!start_end)
                        printf("start:\t");
                else
                        printf("end:\t");

                scanf("%i", &input);

                if (start_end){
                        vma_end   = align_pg_32(input);
                        make_vma_map(vma_start, vma_end);
                }
                else{
                        vma_start = align_pg_32(input);
                }
                start_end = !start_end;
        }

        return 0;
}

Como você pode ver, o programa aceita intervalos de endereços virtuais, cada intervalo sendo definido por início e fim. Cada intervalo é subdividido em VMAs com tamanho de página, dando permissões diferentes às páginas adjacentes. Interrompendo (usando SIGINT) o programa aciona uma chamada para munlockall () e o tempo para o procedimento ser concluído é devidamente anotado.

Agora, quando eu o executo no freescale e500mc com a versão Linux em 2.6.34 no intervalo 0x30000000-0x35000000, recebo um tempo total de munlockall () de quase 45 segundos. No entanto, se eu fizer a mesma coisa com intervalos iniciais menores em pedidos aleatórios (isto é, não necessariamente aumentando endereços) de modo que o número total de páginas (e VMAs bloqueados) seja aproximadamente o mesmo, observe o tempo total de munlockall () não mais que 4 segundos.

Eu tentei a mesma coisa em x86_64 com o Linux 2.6.34 e meu programa compilado com o parâmetro -m32 e parece que as variações, embora não tão pronunciadas como com ppc, ainda são 8 segundos para o primeiro caso e sob um segundo para o segundo caso.

Eu tentei o programa no Linux 2.6.10 de um lado e no 3.19, do outro e parece que essas diferenças monumentais não existem lá. Além do mais, munlockall () sempre completa em menos de um segundo.

Então, parece que o problema, seja lá o que for, existe apenas em torno da versão 2.6.34 do kernel Linux.

    
por Anirban Ghoshal 30.06.2015 / 14:05

1 resposta

2

Se você tiver excesso de memória, muito tmpfs poderá estar no disco. Talvez seja necessário enviar itens para processar o desligamento. mlock() provavelmente forçará muitas outras memórias para o disco. Como você indica que está sem disco, provavelmente está lendo na rede.

Execute sar reunindo todas as estatísticas enquanto o servidor está sendo desligado. ( sar pode não ser instalado por padrão.) Use os dados de captura da opção em um arquivo e um intervalo de 5 a 10 segundos. Você pode então examinar quais recursos foram o gargalo no lazer.

Editar: com base no seu comentário, você pode ter criado problemas de bloqueio na tabela de páginas. Quantas chamadas mlock você faz? Eles são todos necessários? Tente descartar a lista de segmentos de memória bloqueada antes de desligar o processo.

    
por 30.06.2015 / 14:31