O que é uma falha de segmentação?

599

O que é uma falha de segmentação? É diferente em C e C ++? Como as falhas de segmentação e os ponteiros oscilantes estão relacionados?

Rajendra Uppal
fonte
95
falha de segmentação faz com que o compilador se sinta mal .
Benjamin Crouzier
22
Se for esse o caso, por que, no meu caso, o compilador não reclamou de nada, tudo correu bem, mas em tempo de execução o sistema lança uma falha de segmentação (core dump)? T_T
Jim Raynor
3
Apenas um despejo de memória quando algo dá errado!
resultsway
7
@pinouchon: Engraçado, mas quando um compilador tem algo a ver com falhas seg? Não é mais o ambiente de tempo de execução?
Dhein
1
Normalmente chamado por tentativa de desreferenciar um ponteiro nulo; portanto, uma falha de segmentação geralmente é análoga a um Java NullPointerException.
Raedwald

Respostas:

674

A falha de segmentação é um tipo específico de erro causado pelo acesso à memória que "não pertence a você". É um mecanismo auxiliar que evita que você corrompa a memória e introduza erros de memória difíceis de depurar. Sempre que você obtém um segfault, sabe que está fazendo algo errado com a memória - acessando a variável que já foi liberada, gravando em uma parte somente leitura da memória, etc. A falha de segmentação é essencialmente a mesma na maioria dos idiomas que permitem mexer com No gerenciamento de memória, não há diferença principial entre segfaults em C e C ++.

Existem várias maneiras de obter um segfault, pelo menos nos idiomas de nível inferior, como C (++). Uma maneira comum de obter um segfault é desreferenciar um ponteiro nulo:

int *p = NULL;
*p = 1;

Outro segfault acontece quando você tenta gravar em uma parte da memória que foi marcada como somente leitura:

char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault

O ponteiro oscilante aponta para algo que não existe mais, como aqui:

char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling

O ponteiro poscila porque aponta para a variável de caractere cque deixou de existir após o término do bloco. E quando você tenta desreferenciar o ponteiro oscilante (como *p='A'), provavelmente obterá um segfault.

zoul
fonte
154
O último exemplo é particularmente desagradável, quando eu construo: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); retornar 0; } Com o gcc ou vários outros compiladores, 'parece' funcionar. Nenhum aviso ao compilar. Sem segfault. Isso ocorre porque o '}' fora do escopo, na verdade, não exclui os dados, apenas os marca como livres para serem usados ​​novamente. O código pode funcionar bem em um sistema de produção por anos, você altera outra parte do código, altera o compilador ou algo mais e BOOOOOM!
precisa saber é o seguinte
36
Desculpem a colisão, mas apenas uma nota lateral ... nenhum dos seus exemplos necessariamente causar um segfault, na verdade, é apenas um comportamento indefinido ;-)
oldrinb
18
@oldrinb: É impossível escrever código que necessariamente cause um segfault. Não menos importante, porque existem sistemas por aí que operam sem proteção de memória, portanto, não é possível dizer se um pedaço de memória realmente "pertence a você" e, portanto , não conhece segfaults, apenas comportamento indefinido ... (AmigaOS clássico, por exemplo)
DevSolar 29/05
7
@ ChrisHuang-Leaver, você precisa entender que cé local, significa que ele foi empurrado para a pilha depois {e retirado dela depois }. o ponteiro pendente é apenas uma referência a um deslocamento que agora está fora da pilha. é por isso que modificá-lo em um programa simples nunca disparará nenhum segfault. por outro lado, isso pode levar ao segfault em um caso de uso mais complexo, onde outras chamadas de função podem levar a pilha a crescer e conter os dados apontados pelo ponteiro pendente. escrevendo para que os dados (VARs locais) levaria a um comportamento indefinido (segfault & Co)
Ayman Khamouma
3
@ ChrisHuang-Leaver, normalmente quando você sai do escopo, o compilador precisa recuperar um pouco de espaço na pilha para liberar o espaço não utilizado, mas isso nem sempre acontece (com o gcc sendo um desses compiladores). Além disso, o espaço de pilha alocado é normalmente reutilizado novamente, por isso ouvi falar de nenhum sistema operacional que retorne páginas de pilha não utilizadas ao sistema, tornando esse espaço sujeito a um SIGSEGV, para que eu não espere que esse sinal seja confundido com a pilha.
Luis Colorado
111

Vale a pena notar que a falha de segmentação não é causada pelo acesso direto a outra memória do processo (é isso que estou ouvindo às vezes), pois simplesmente não é possível. Com a memória virtual, todo processo tem seu próprio espaço de endereço virtual e não há como acessar outro usando qualquer valor de ponteiro. A exceção a isso pode ser bibliotecas compartilhadas, que são o mesmo espaço de endereço físico mapeado para (possivelmente) diferentes endereços virtuais e memória do kernel, que é mapeada da mesma maneira em todos os processos (para evitar a liberação de TLB no syscall, eu acho). E coisas como shmat;) - são o que eu considero como acesso "indireto". No entanto, é possível verificar se eles estão localizados muito longe do código do processo e que geralmente podemos acessá-los (é por isso que eles estão lá,

Ainda assim, uma falha de segmentação pode ocorrer no caso de acessar nossa própria memória (processo) de maneira inadequada (por exemplo, tentando gravar em espaço não gravável). Mas o motivo mais comum para isso é o acesso à parte do espaço de endereço virtual que não é mapeado para o físico.

E tudo isso com relação aos sistemas de memória virtual.

konrad.kruczynski
fonte
Com a memória compartilhada / arquivos mapeados na memória, é possível que outra pessoa mexa com sua memória. No WIN32, existem APIs desagradáveis ​​como 'WriteProcessMemory' também!
paulm 17/02/2014
1
@ paulm: Sim, eu sei. Era isso que eu pensava em "E coisas como shmat;) - são o que eu considero como acesso 'indireto'".
Konrad.kruczynski
Em um sistema operacional de memória virtual, não há como (normalmente, portanto, implementadores de sistema operacional, não me chame por isso) para um processo acessar outra memória virtual de processo, não sendo algum tipo de chamada do sistema de anexação de memória que permite Acesso. Endereços de memória virtual normalmente significam coisas diferentes, dependendo do processo que está sendo considerado.
22616 Luis Luis
38

Uma falha de segmentação é causada por uma solicitação de uma página que o processo não listou em sua tabela de descritores ou uma solicitação inválida para uma página que listou (por exemplo, uma solicitação de gravação em uma página somente leitura).

Um ponteiro danificado é um ponteiro que pode ou não apontar para uma página válida, mas aponta para um segmento "inesperado" da memória.

Ignacio Vazquez-Abrams
fonte
10
Isso é verdade, mas seria realmente útil se você já não soubesse o que é uma falha de segmentação?
zoul
29

Para ser honesto, como outros pôsteres mencionaram, a Wikipedia tem um artigo muito bom sobre isso, então dê uma olhada lá. Esse tipo de erro é muito comum e costuma ser chamado de outras coisas, como violação de acesso ou falha de proteção geral.

Eles não são diferentes em C, C ++ ou qualquer outra linguagem que permita ponteiros. Esses tipos de erros geralmente são causados ​​por ponteiros que são

  1. Usado antes de ser inicializado corretamente
  2. Usado depois que a memória para a qual eles apontam foi realocada ou excluída.
  3. Usado em uma matriz indexada em que o índice está fora dos limites da matriz. Geralmente, isso é apenas quando você está fazendo cálculos de ponteiro em matrizes ou c-strings tradicionais, não em coleções baseadas em STL / Boost (em C ++.)
Componente 10
fonte
16

De acordo com a wikipedia:

Uma falha de segmentação ocorre quando um programa tenta acessar um local de memória que não tem permissão para acessar ou tenta acessar um local de memória de uma maneira que não é permitida (por exemplo, tentando gravar em um local somente leitura, ou substituir parte do sistema operacional).

Orhan Cinar
fonte
13

A falha de segmentação também é causada por falhas de hardware, neste caso, as memórias de RAM. Essa é a causa menos comum, mas se você não encontrar um erro no seu código, talvez um teste de memória possa ajudá-lo.

A solução neste caso, altere a RAM.

editar:

Aqui está uma referência: Falha na segmentação por hardware

Alejo Bernardin
fonte
3
Um teste rápido e sujo para falhas de RAM é executar o programa travando repetidamente em um loop. Se o programa não possui um não determinismo interno - ou seja, ele sempre produz a mesma saída para a mesma entrada, ou pelo menos deveria - mas, para alguma entrada em particular, ele falha às vezes , nem sempre, mas nunca: você deve comece a se preocupar com RAM ruim.
Zwol
8

A falha de segmentação ocorre quando um processo (executando uma instância de um programa) está tentando acessar o endereço de memória somente leitura ou o intervalo de memória que está sendo usado por outro processo ou acessar o endereço de memória inexistente (inválido). Problema de referência de oscilação (ponteiro) significa que tentar acessar um objeto ou variável cujo conteúdo já foi excluído da memória, por exemplo:

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here
Sohail xIN3N
fonte
4
A maneira correta de excluir uma matriz é delete [] arr;
Damian
8

A página Segmentation_fault da Wikipedia tem uma descrição muito boa, apenas apontando as causas e razões. Dê uma olhada no wiki para uma descrição detalhada.

Na computação, uma falha de segmentação (geralmente abreviada para segfault) ou violação de acesso é uma falha provocada por hardware com proteção de memória, notificando um sistema operacional sobre uma violação de acesso à memória.

A seguir, algumas causas típicas de uma falha de segmentação:

  • Desreferenciando ponteiros NULL - isso é especial no hardware de gerenciamento de memória
  • Tentativa de acessar um endereço de memória inexistente (fora do espaço de endereço do processo)
  • Tentativa de acessar a memória à qual o programa não tem direitos (como estruturas do kernel no contexto do processo)
  • Tentativa de gravar memória somente leitura (como segmento de código)

Por sua vez, geralmente são causados ​​por erros de programação que resultam em acesso inválido à memória:

  • Desreferenciando ou atribuindo a um ponteiro não inicializado (ponteiro curinga, que aponta para um endereço de memória aleatório)

  • Desreferenciando ou atribuindo a um ponteiro liberado (ponteiro pendente, que aponta para a memória que foi liberada / desalocada / excluída)

  • Um estouro de buffer.

  • Um estouro de pilha.

  • Tentativa de executar um programa que não é compilado corretamente. (Alguns compiladores produzirão um arquivo executável, apesar da presença de erros em tempo de compilação.)

Roy
fonte
6

Em palavras simples: falha de segmentação é o sistema operacional que envia um sinal ao programa dizendo que detectou um acesso ilegal à memória e está encerrando prematuramente o programa para evitar que a memória seja corrompida.

FilipeCanatto
fonte
3

"Falha na segmentação" significa que você tentou acessar a memória à qual não tem acesso.

O primeiro problema é com seus argumentos de main. A função principal deve ser int main(int argc, char *argv[]), e você deve verificar se argc é pelo menos 2 antes de acessar o argv [1].

Além disso, como você está passando um float para printf (que, a propósito, é convertido em um duplo ao passar para printf), você deve usar o especificador de formato% f. O especificador de formato% s é para seqüências de caracteres (matrizes de caracteres terminados em '\ 0').

Worm PHP ...
fonte
2

Uma falha de segmentação ou violação de acesso ocorre quando um programa tenta acessar um local de memória que não existe ou tenta acessar um local de memória de uma maneira que não é permitida.

 /* "Array out of bounds" error 
   valid indices for array foo
   are 0, 1, ... 999 */
   int foo[1000];
   for (int i = 0; i <= 1000 ; i++) 
   foo[i] = i;

Aqui eu [1000] não existe, então ocorre o segfault.

Causas de falha de segmentação:

it arise primarily due to errors in use of pointers for virtual memory addressing, particularly illegal access.

De-referencing NULL pointers  this is special-cased by memory management hardware.

Attempting to access a nonexistent memory address (outside processs address space).

Attempting to access memory the program does not have rights to (such as kernel structures in process context).

Attempting to write read-only memory (such as code segment).
Mohit Rohilla
fonte
2
Primeiro, a falha seg não tem nada a ver com o endereço que existe ou não. É sobre você acessá-lo onde você não tem permissão para fazê-lo. E no seu exemplo especial, é garantido até por norma que esse local existe. uma vez que o padrão diz no caso de matriz, deve-se fornecer um endereço válido para um apontador apontador em uma matriz bem alinhada dentro de seus limites AND 1 atrás .
Dhein
também é relocado com endereço, se você não tiver o endereço e se você tentar acessar esse endereço, também haverá seg. culpa. E no meu exemplo, é apenas para entender o ponto de vista.
Mohit Rohilla
2

Existem várias boas explicações sobre "Falha de segmentação" nas respostas, mas como na falha de segmentação geralmente há um despejo do conteúdo da memória, eu queria compartilhar onde a relação entre o "núcleo despejado" parte da parte de Falha de segmentação (núcleo despejado) e a memória vem de:

Entre 1955 e 1975 - antes da memória de semicondutores - a tecnologia dominante na memória de computadores usava pequenas rosquinhas magnéticas amarradas em fios de cobre. Os donuts eram conhecidos como "núcleos de ferrite" e a memória principal, conhecida como "memória de núcleo" ou "núcleo".

Retirado daqui .

Viktor Nonov
fonte
2

Existem definições suficientes de falha de segmentação. Gostaria de citar alguns exemplos que encontrei durante a programação, que podem parecer erros tolos, mas que desperdiçam muito tempo.

  1. você pode obter uma falha de segmentação no caso abaixo, enquanto a incompatibilidade do tipo argumet no printf

    #include<stdio.h> int main(){
    int a = 5; printf("%s",a); return 0; }

resultado : Segmentation Fault (SIGSEGV)

  1. quando você esqueceu de alocar memória para um ponteiro, mas tentando usá-lo.

     #include<stdio.h> 
     typedef struct{
       int a;
     }myStruct;   
    int main(){
      myStruct *s;
      /* few lines of code */
      s->a = 5;
      return 0;
    }

resultado : Segmentation Fault (SIGSEGV)

NPE
fonte
1

O significado simples de Segmentation faulté que você está tentando acessar alguma memória que não lhe pertence. Segmentation faultocorre quando tentamos ler e / ou gravar tarefas em um local de memória somente leitura ou tentamos liberar memória. Em outras palavras, podemos explicar isso como algum tipo de corrupção de memória.

Abaixo menciono erros comuns cometidos por programadores que levam a Segmentation fault.

  • Use de scanf()maneira errada (esqueceu de colocar &).
int num;
scanf("%d", num);// must use &num instead of num
  • Use ponteiros de maneira errada.
int *num; 
printf("%d",*num); //*num should be correct as num only
//Unless You can use *num but you have to point this pointer to valid memory address before accessing it.
  • Modificando uma literal de seqüência de caracteres (o ponteiro tenta gravar ou modificar uma memória somente leitura).
char *str;  

//Stored in read only part of data segment
str = "GfG";      

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Tente acessar um endereço que já está liberado.
// allocating memory to num 
int* num = malloc(8); 
*num = 100; 

// de-allocated the space allocated to num 
free(num); 

// num is already freed there for it cause segmentation fault
*num = 110; 
  • Estouro de pilha -: falta de memória na pilha
  • Acessando uma matriz fora dos limites '
  • Use especificadores de formato incorretos ao usar printf()e scanf()'
Kalana
fonte