Что такое ошибка сегментации?

431

Что такое ошибка сегментации? Различие в C и С++? Как связаны связанные с сегментацией ошибки и оборванные указатели?

  • 70
    ошибка сегментации заставляет компилятор чувствовать себя плохо .
  • 20
    Если это так, то почему в моем случае компилятор ничего не жаловал, все прошло гладко, но во время выполнения система выдает ошибку сегментации (дамп ядра)? T_T
Показать ещё 3 комментария
Теги:
segmentation-fault

12 ответов

548
Лучший ответ

Ошибка сегментации - это определенная ошибка, вызванная доступом к памяти, которая "не принадлежит вам". Это вспомогательный механизм, который мешает вам разлагать память и вводить жесткие ошибки памяти. Всякий раз, когда вы получаете segfault, вы знаете, что делаете что-то не так с памятью - доступ к переменной, которая уже была освобождена, запись в часть только для чтения в памяти и т.д. Ошибка сегментации по сути одинакова на большинстве языков, которые позволяют вам возиться с управления памятью, нет принципиальной разницы между segfaults в C и С++.

Существует много способов получить segfault, по крайней мере, на языках более низкого уровня, таких как C (++). Общим способом получения segfault является разыменование нулевого указателя:

int *p = NULL;
*p = 1;

Другое segfault происходит, когда вы пытаетесь записать часть памяти, помеченную как только для чтения:

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

Висячий указатель указывает на вещь, которая больше не существует, например здесь:

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

Указатель p зависает, потому что он указывает на символьную переменную c, которая перестала существовать после завершения блока. И когда вы пытаетесь разыменовать висячий указатель (например, *p='A'), вы, вероятно, получите segfault.

  • 135
    Последний пример особенно неприятен, когда я собираю: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); вернуть 0; } С gcc или несколькими другими компиляторами он «работает». Нет предупреждений о компиляции. Нет сегфо Это потому, что '}' выходит за рамки, фактически не удаляет данные, просто помечает их как свободные для повторного использования. Код может хорошо работать в производственной системе в течение многих лет, вы изменяете другую часть кода, меняете компилятор или что-то еще и BOOOOOM!
  • 28
    Извините за удар, но только примечание ... ни один из ваших примеров не обязательно вызывает segfault, на самом деле это просто неопределенное поведение ;-)
Показать ещё 6 комментариев
94

Стоит отметить, что ошибка сегментации не вызвана прямым доступом к другой памяти процесса (это то, что я иногда слышу), поскольку это просто невозможно. С виртуальной памятью каждый процесс имеет собственное виртуальное адресное пространство, и нет доступа к другому, используя любое значение указателя. Исключение из этого могут быть разделяемые библиотеки, которые представляют собой одно и то же физическое адресное пространство, сопоставляемое (возможно) с разными виртуальными адресами и памятью ядра, которые, как я думаю, даже сопоставляются одинаково в каждом процессе (во избежание TLB-промывки в syscall). И такие вещи, как shmat;) - вот что я считаю "косвенным" доступом. Тем не менее, можно проверить, что они, как правило, расположены далеко от кода процесса, и мы обычно можем получить к ним доступ (вот почему они есть, тем не менее доступ к ним ненадлежащим образом приведет к ошибке сегментации).

Тем не менее, ошибка сегментации может произойти в случае неправильного доступа к нашей собственной (процессуальной) памяти (например, попытка записи в незаписываемое пространство). Но наиболее распространенной причиной этого является доступ к части виртуального адресного пространства, которое вообще не отображается на физическое.

И все это в отношении систем виртуальной памяти.

  • 0
    С файлами общей памяти / карты памяти кто-то другой может связываться с вашей памятью. В WIN32 также есть неприятные API, такие как «WriteProcessMemory»!
  • 1
    @Paulm: Да, я знаю. Это то, что я имел в виду в «И такие вещи, как шмат;) - это то, что я считаю« косвенным »доступом».
Показать ещё 1 комментарий
27

Ошибка сегментации вызвана запросом страницы, которую процесс не указал в своей таблице дескриптора, или неверным запросом для страницы, которую она указала (например, запрос на запись на странице только для чтения).

Висячий указатель - это указатель, который может или не указывать на действительную страницу, но указывает на "неожиданный" сегмент памяти.

  • 9
    Это правда, но действительно ли это поможет вам, если вы уже не знаете, что такое ошибка сегментации?
25

Честно говоря, как отмечали другие плакаты, в Википедии есть очень хорошая статья об этом так что посмотрите там. Этот тип ошибки очень часто встречаются и часто называются другими вещами, такими как Нарушение доступа или Обнаружение общей защиты.

Они ничем не отличаются на C, С++ или любом другом языке, который позволяет указывать. Такие ошибки обычно вызваны указателями

  • Используется перед правильной инициализацией
  • Используется после того, как память, на которую они указывают, была перераспределена или удалена.
  • Используется в индексированном массиве, где индекс находится за пределами границ массива. Обычно это происходит только тогда, когда вы выполняете математику указателя на традиционных массивах или c-строках, а не на основе STL/Boost (на С++.)
22

В то время как ответ Zoul объясняет, что такое ошибка сегментации, я обнаружил, что такие ошибки могут быть особенно трудно поймать, особенно если вы новичок в низкоуровневых языках, таких как С++ или C. Вот некоторые из распространенных способов для получения ошибки сегментации в вашей программе:

Неправильная строка управления форматом в операторах printf или scanf

Строка управления форматом должна иметь одинаковое количество спецификаторов преобразования (%s, %d и т.д.), поскольку printf или scanf имеют аргументы для печати или чтения. То же самое относится к fprintf и fscanf.

Не использовать & для аргументов scanf

Функция scanf принимает в качестве аргументов строку управления форматом и адреса переменных, в которые она поместит данные, которые она читает. Оператор & (адрес) используется для указания адреса переменной.

Ссылки на массив вне границ

Убедитесь, что вы не нарушили границы любого массива, который вы используете; т.е. вы не индексировали массив со значением, меньшим, чем индекс его самого нижнего элемента, или больше, чем индекс его самого высокого элемента. Valgrind может оказаться полезным для обнаружения таких ссылок - вы можете использовать Valgrind с флагом --tool=exp-sgcheck.

Доступ к неинициализированным указателям

Перед обращением к переменной указателя должен быть присвоен действительный адрес. Убедитесь, что вы указали все указатели на допустимую область памяти.

Неправильное использование операторов & (адрес) и * (разыменование)

Вам нужно быть осторожным при их использовании, особенно при передаче параметров по ссылке/с помощью указателей.

Ограничения оболочки

Иногда ошибки сегментации не вызваны ошибками в программе, а вызваны тем, что ограничения системной памяти установлены слишком низко. Обычно это ограничение на размер стека вызывает такую ​​проблему (переполнение стека). Чтобы проверить пределы памяти, используйте команду ulimit в bash.

Отладка с использованием gdb

Вы можете использовать отладчик gdb, чтобы просмотреть обратную трассировку файла core, сбрасываемого вашей программой. Всякий раз, когда программы segfault, они обычно выгружают содержимое памяти во время сбоя в файл core (core dumped). Скомпилируйте свою программу с помощью флага -g, запустите в gdb и используйте bt (backtrace).

14

Согласно википедии:

Ошибка сегментации возникает, когда программа пытается получить доступ к памяти местоположение, которое не допускается доступ или попытки доступа к памяти местоположение не допускается (например, попытка написать только для чтения или для перезаписи часть операционной системы).

10

Ошибка сегментации также вызвана аппаратными сбоями, в этом случае память RAM. Это менее распространенная причина, но если вы не нашли ошибку в своем коде, возможно, memtest может вам помочь.

Решение в этом случае изменит ОЗУ.

изменить:

Здесь есть ссылка: Ошибка сегментации аппаратным обеспечением

  • 3
    Быстрая и грязная проверка на наличие неисправного ОЗУ заключается в том, чтобы запускать программу сбоя снова и снова в цикле. Если у программы нет внутреннего недетерминизма, то есть она всегда выдает один и тот же вывод для одного и того же ввода, или, по крайней мере, так оно и должно быть, - но для какого-то конкретного ввода происходит сбой иногда , не всегда, но не никогда: тогда вам следует начать беспокоиться о плохой оперативной памяти.
6

Ошибка сегментации возникает, когда процесс (исполняемый экземпляр программы) пытается получить доступ к адресу памяти только для чтения или диапазону памяти, который используется другим процессом или к доступу к несуществующему (недопустимому) адресу памяти. Ошибка ссылки (указатель) означает, что попытка доступа к объекту или переменной, содержимое которой уже было удалено из памяти, например:

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here
  • 4
    Правильный способ удаления массива это delete [] arr;
4

Wikipedia Segmentation_fault страница имеет очень хорошее описание об этом, просто указывая причины и причины. Посмотрите в wiki для подробного описания.

При вычислении ошибка сегментации (часто сокращается до segfault) или нарушение доступа - это ошибка, вызванная аппаратным обеспечением с защитой памяти, уведомление операционной системы (ОС) о нарушении доступа к памяти.

Ниже приводятся некоторые типичные причины ошибки сегментации:

  • Разметка NULL-указателей - это специальная оболочка с помощью аппаратного обеспечения управления памятью
  • Попытка получить доступ к несуществующему адресу памяти (внешнее адресное пространство процесса)
  • Пытаясь получить доступ к памяти, программа не имеет прав (например, структуры ядра в контексте процесса).
  • Попытка написать постоянную память (например, сегмент кода)

Это, в свою очередь, часто вызвано ошибками программирования, которые приводят к недопустимому доступу к памяти:

  • Выделение или назначение неинициализированному указателю (дикий указатель, указывающий на случайный адрес памяти)

  • Разыменование или назначение освобожденному указателю (оборванный указатель, который указывает на освобожденную/удаленную память/удаленную)

  • Переполнение буфера.

  • Переполнение стека.

  • Попытка выполнить программу, которая не компилируется правильно. (Некоторые компиляторы выдают исполняемый файл, несмотря на наличие ошибок времени компиляции.)

3

Простыми словами: ошибка сегментации - это операционная система, отправляющая сигнал программе заявив, что обнаружил незаконный доступ к памяти и преждевременно прекращает программу для предотвращения память от повреждения.

0

В ответах есть несколько хороших объяснений "ошибки сегментации", но поскольку с ошибкой сегментации часто возникает дамп содержимого памяти, я хотел бы рассказать, где взаимосвязь между "сбрасываемой" частью в ошибке сегментации (сбрасывание ядра) и память происходит от:

С 1955 по 1975 год - до полупроводниковой памяти - в доминирующей технологии в компьютерной памяти использовались крошечные магнитные пончики, нанизанные на медные провода. Донки были известны как "ферритовые сердечники" и основная память, известная как "основная память" или "ядро".

Взято отсюда.

0

Ошибка сегментации или нарушение прав доступа происходит, когда программа пытается получить доступ к недоступной ячейке памяти или пытается получить доступ к ячейке памяти способом, который не разрешен.

 /* "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;

Здесь я [1000] не существует, поэтому возникает segfault.

Причины сбоя сегментации:

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 process’s 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).
  • 2
    Прежде всего, ошибка seg не имеет ничего общего с адресом, который существует или не существует. Речь идет о том, что вы получаете доступ к нему там, где вам не разрешено это делать. И в вашем особом примере даже гарантируется, что это место существует. так как стандарт говорит в случае массива, необходимо учитывать, что существует правильный адрес для указателя pointg на хорошо выровненном массиве в пределах его границ И 1 позади .
  • 0
    он также высвобождается с адресом, если у вас нет адреса и если вы пытаетесь получить доступ к этому адресу, также присутствует seg. неисправность. И в моем примере это только для понимания точки зрения.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню