sigaction (7): semântica do membro si_code do siginfo_t

5

Eu tenho um programa de longa execução (torna-se um daemon com daemon (3) chamada) que sai no Sinal 11 (Violação de Segmentação) de vez em quando. Eu não posso dizer porque. Então, eu escrevi um manipulador SIGSEGV, definido usando a chamada do sistema sigaction() . Eu defino a função de manipulador para que ele tenha esse protótipo: void (*sa_sigaction)(int, siginfo_t *, void *) , o que significa que ele obtém um ponteiro para uma estrutura siginfo_t como um argumento formal.

Por ocasião de um SIGSEGV misterioso, o elemento si_code do siginfo_t tem um valor de 0x80, o que significa que, de acordo com a página de manual do sigaction, "O kernel" enviou o sinal. Isso está em um sistema RHEL da Red Hat: Linux blahblah 2.6.18-308.20.1.el5 #1 SMP Tue Nov 6 04:38:29 EST 2012 x86_64 x86_64 x86_64 GNU/Linux

Por que o kernel envia um SIGSEGV? Isso é do famoso OOM-Killer, ou existe algum outro motivo para obter um SIGSEGV? Como um mero usuário neste sistema, não consigo ver /var/log/message , e os administradores de sistema são mais do que um pouco indiferentes, provavelmente porque eles vêm de um ambiente Windows.

Um SIGSEGV gerado de propósito (desreferenciando um ponteiro NULL) não obtém um valor si_code de 0x80, ele obtém 0x1, que significa "endereço não mapeado para objeto".

    
por Bruce Ediger 04.04.2013 / 23:24

1 resposta

4

A semântica não documentada de si_code = SI_KERNEL com si_errno = 0 é,

  1. traps específicos do processador
  2. violação de memória do segmento do kernel (exceto para acesso ao semáforo)
  3. violações do formato de arquivo ELF e
  4. violações de pilha.

Todos os outros SIGSEGV s devem ter um si_errno definido para um valor diferente de zero. Leia sobre os detalhes.

Quando o kernel configura um processo de espaço do usuário, ele define uma tabela de páginas de memória virtual para o processo. Quando o agendador do kernel executa o processo, ele reconfigura a unidade de gerenciamento de memória da CPU (MMU) de acordo com a tabela de páginas do processo.

Quando um processo de espaço de usuário tenta acessar a memória que está fora de sua tabela de páginas, a CPU MMU detecta essa violação e gera uma exceção. Observe que isso acontece no nível hardware . O kernel ainda não está envolvido.

O kernel está configurado para lidar com exceções de MMU. Ele captura a exceção causada pela tentativa do processo em execução de acessar a memória fora de sua tabela de páginas. O kernel chama então do_page_fault() , que envia o sinal SIGSEGV para o processo. É por isso que o sinal vem do kernel e não do próprio processo ou de outro processo.

Esta é uma explicação altamente simplificada, é claro. A melhor explicação simples que eu vi sobre isso é a seção "Falhas de página" do artigo bonito de William Gatliff API da Unidade de Gerenciamento de Memória do Kernel do Linux .

Observe que em CPUs sem uma MMU, como as MPUs Blackfin, os processos do espaço do usuário do Linux geralmente podem acessar qualquer memória. ou seja, não há sinal SIGSEGV para violações de memória (somente para traps como estouro de pilha) e a depuração de problemas de acesso à memória pode ser complicada.

Eu comento o segundo jordanm sobre a configuração do ulimit e a inspeção do arquivo principal com gdb . Você pode fazer ulimit -c unlimited na linha de comando se executar o processo a partir de um shell ou usar o wrapper de chamada do sistema libc setrlimit ( man setrlimit ) em seu programa. Você pode definir o nome do arquivo principal e sua localização no arquivo /proc/sys/kernel/core_pattern . Veja o excelente glossário de A.P. Lawrence sobre isso em Controlando arquivos principais (Linux) . Para usar gdb no corefile, veja este pequeno tutorial no Steve.org.

Uma violação de segmentação com si_code SEGV_MAPERR (0x1) é provavelmente uma referência de ponteiro nulo, um acesso de memória inexistente como 0xfffffc0000004000 ou malloc e free problems. Corrupção de pilha ou processo excedendo seus limites de tempo de execução ( man getrlimit ) no caso de malloc e duplo livre ou livre de endereço não alocado no caso de free . Veja o elemento si_errno para mais pistas.

Uma violação de segmentação que ocorre como resultado do processo de espaço do usuário acessando a memória virtual acima do limite TASK_SIZE causará uma violação de segmentação com si_code de SI_KERNEL . Em outras palavras, o TASK_SIZE limit é o maior endereço virtual que qualquer processo pode acessar. Isso normalmente é de 3 GB, a menos que o kernel esteja configurado para suporte a memória alta. A área acima do limite TASK_SIZE é referida como o "segmento do kernel". Veja linux-2.6//arch/x86/mm/fault.c:__bad_area_nosemaphore(...) onde chama force_sig_info_fault(...) .

Para cada arquitetura, há também um número de traps específicos que causam um SISEGV com SI_KERNEL . Para x86, estes são definidos pelas macros DO_ERROR em linux-2.6//arch/x86/kernel/traps.c .

O manipulador OOM envia SIGKILL, não SIGSEGV, como pode ser visto na função linux-2.6//mm/oom_kill.c:oom_kill_process(...) na linha 498:

do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p, true);

para processos relacionados e linha 503:

do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true);

para o processo que foi a causa proximal da OOM.

Você pode obter mais informações observando o wait status do processo que foi eliminado de seu processo pai e possivelmente observando dmesg ou melhor, configurando o log do kernel e observando-o.

    
por 07.04.2013 / 07:38