A semântica não documentada de si_code = SI_KERNEL
com si_errno = 0
é,
- traps específicos do processador
- violação de memória do segmento do kernel (exceto para acesso ao semáforo)
- violações do formato de arquivo ELF e
- 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.