У чому полягає помилка сегментації?


598

У чому полягає помилка сегментації? Чи відрізняється він у С та С ++? Як пов’язані помилки сегментації та звисаючі покажчики?



22
Якщо це так, чому в моєму випадку компілятор нічого не скаржився, все пройшло гладко, але під час роботи система видає помилку сегментації (core dump)? T_T
Джим Рейнор

3
Просто скидання пам’яті, коли щось піде не так!
Результати шляху

7
@pinouchon: Смішно, але коли компілятор має щось спільне з помилками Seg? Хіба це не навколишнє середовище часу роботи?
dhein

1
Зазвичай викликається спробою скинути нульовий покажчик, тому помилка сегментації часто є аналогом Java NullPointerException.
Raedwald

Відповіді:


673

Помилка сегментації - це специфічна помилка, викликана доступом до пам'яті, яка "не належить вам". Це допоміжний механізм, який запобігає пошкодженню пам’яті та впровадженню важких помилок пам’яті. Щоразу, коли ви отримуєте сегментарне меню, ви знаєте, що ви робите щось не так з пам'яттю - доступ до змінної, яка вже звільнилася, запис у частину пам'яті, лише для читання, і т.д. в управлінні пам'яттю немає принципової різниці між сегментами segfault в C і C ++.

Існує багато способів отримати сегментарій, принаймні, на мовах нижчого рівня, таких як C (++). Поширений спосіб отримати сегментарний стандарт - це знецінення нульового вказівника:

int *p = NULL;
*p = 1;

Ще один сегмент за замовчуванням відбувається, коли ви намагаєтесь записати на частину пам'яті, яка була позначена як лише для читання:

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'), ви, ймовірно, отримаєте сегмент за замовчуванням.


154
Останній приклад особливо неприємний, коли я будую: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); повернути 0; } І з gcc, або з декількома іншими компіляторами він, здається, працює. Немає попереджень про компіляцію. Без сегмента за замовчуванням. Це тому, що "}" поза сферою дії фактично не видаляє дані, а позначає їх як безкоштовне для повторного використання. Код може працювати нормально у виробничій системі протягом багатьох років, ви змінюєте іншу частину коду, змінюєте компілятор чи щось інше і BOOOOOM!
Кріс Хуанг-Лівер

36
Вибачте за зауваження, але лише бічну зауваження ... жоден із ваших прикладів не обов'язково викликає сегментацію, адже це просто невизначена поведінка ;-)
oldrinb

18
@oldrinb: Неможливо написати код, який обов'язково викликає segfault. Не в останню чергу тому, що там є системи, які працюють без захисту пам’яті, тому не можна сказати, чи дійсно частина пам'яті «належить тобі», і, таким чином , не знає segfault, лише невизначене поведінку ... (наприклад, класичний AmigaOS)
DevSolar

7
@ ChrisHuang-Leaver, вам потрібно зрозуміти, що cце локально, це означає, що його було натиснуто на стек після {і вискочив з нього після }. звисаючий покажчик - це лише посилання на зміщення, яке зараз поза стеком. ось чому зміна його у простій програмі ніколи не запускає жодних segfault. з іншого боку, це може призвести до segfault у складнішому випадку використання, коли інші виклики функцій можуть привести стек до зростання та містити дані, на які вказує звисаючий покажчик. Запис на ці дані (місцеві варси) призведе до невизначеної поведінки (segfault & Co)
Айман Хамума

3
@ ChrisHuang-Leaver, зазвичай, коли ви виходите за межі, компілятору доводиться відновлювати деякий простір стеку, щоб звільнити невикористаний простір стека, але це відбувається не завжди (коли gcc є одним із цих компіляторів). Крім того, виділений простір стека зазвичай повторно використовується, тому я не чув жодної операційної системи, яка б повертала невикористані сторінки стека в систему, роблячи цей простір об'єктом для a SIGSEGV, тому я не очікую, що такий сигнал буде керуватися стеком.
Луїс Колорадо

111

Варто зазначити, що помилка сегментації не викликана прямим доступом до іншої пам'яті процесу (це те, що я чую іноді), оскільки це просто неможливо. У віртуальній пам'яті кожен процес має власний віртуальний адресний простір, і немає жодного способу отримати доступ до іншого, використовуючи будь-яке значення вказівника. Винятком з цього можуть бути спільні бібліотеки, які є однаковим фізичним адресним простором, відображеним на (можливо) різні віртуальні адреси та пам'ять ядра, яка навіть відображається однаково у кожному процесі (я думаю, щоб уникнути TLB промивання на syscall, я думаю). І такі речі, як shmat;) - це те, що я вважаю "непрямим" доступом. Однак можна перевірити, що вони зазвичай розташовані далеко від коду процесу, і ми зазвичай маємо доступ до них (саме тому вони там,

Тим не менш, помилка сегментації може статися в разі неправильного доступу до власної (технологічної) пам’яті (наприклад, спроби запису в неписаний простір). Але найпоширенішою причиною цього є доступ до тієї частини віртуального адресного простору, яка зовсім не відображається на фізичну.

І все це стосовно систем віртуальної пам'яті.


З картами спільної пам’яті / пам’яттю, що спільно зберігається, хтось ще може возитися з вашою пам’яттю. У WIN32 є такі неприємні API, як "WriteProcessMemory"!
палм

1
@paulm: Так, я знаю. Це те, що я мав на увазі в "І такі речі, як shmat;) - це те, що я вважаю" непрямим "доступом".
konrad.kruczynski

У операційній системі віртуальної пам'яті немає способу (як правило, тому, будь ласка, інженери операційної системи, не спалахуйте мене на це), щоб отримати доступ до іншої віртуальної пам'яті процесу, не будучи якимось системним викликом приєднання пам'яті, який дозволяє вам доступ. Адреси віртуальної пам'яті зазвичай означають різні речі залежно від процесу, який розглядається.
Луїс Колорадо

38

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

Висячий покажчик - це вказівник, який може або не може вказувати на дійсну сторінку, але вказує на "несподіваний" сегмент пам'яті.


10
Це правда, але чи справді це допоможе вам, якби ви вже не знали, що таке сегментація?
zoul

29

Якщо чесно, як згадували інші афіші, у Вікіпедії є дуже гарна стаття з цього приводу, тож подивіться там. Цей тип помилок дуже поширений і його часто називають іншими речами, такими як Порушення доступу або Помилка загального захисту.

Вони не відрізняються C, C ++ або будь-якою іншою мовою, яка дозволяє вказувати. Ці помилки, як правило, викликаються вказівниками, які є

  1. Використовується перед належною ініціалізацією
  2. Використовується після перерозподілення або видалення пам'яті, на яку вони вказують.
  3. Використовується в індексованому масиві, де індекс знаходиться поза межами масиву. Це, як правило, лише тоді, коли ви займаєтеся математикою вказівника на традиційних масивах або c-рядках, а не на основі колекцій на основі STL / Boost (в C ++.)

16

За вікіпедією:

Помилка сегментації виникає, коли програма намагається отримати доступ до місця пам'яті, до якого не дозволено, або намагається отримати доступ до місця пам'яті способом, який не дозволений (наприклад, спроба запису в місце, доступне лише для читання, або перезаписати частину операційної системи).


13

Помилка сегментації також спричинена збоями в апаратному забезпеченні, в цьому випадку - оперативною пам'яттю. Це менш поширена причина, але якщо ви не знайдете помилки у своєму коді, можливо, повідомлення може допомогти вам.

Рішенням в цьому випадку є зміна ОЗУ.

редагувати:

Тут є посилання: Помилка сегментації апаратними засобами


3
Швидкий і брудний тест на несправну оперативну пам’ять - це запускати свою програму збоїв знову і знову в циклі. Якщо програма не має внутрішнього недетермінізму - тобто вона завжди створює однаковий вихід для одного і того ж входу, або, принаймні, це належить, але, для певного конкретного введення, воно виходить з ладу іноді , не завжди, але ніколи: тоді вам слід почати турбуватися про погану ОЗУ.
zwol

8

Помилка сегментації виникає, коли процес (запущений екземпляр програми) намагається отримати доступ до адреси пам'яті лише для читання або діапазону пам'яті, який використовується іншим процесом, або отримати доступ до неіснуючої (недійсної) адреси пам'яті. Проблема Dangling Reference (pointer) означає, що намагаються отримати доступ до об'єкта чи змінної, вміст якої вже видалено із пам'яті, наприклад:

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here

4
Правильний спосіб видалити масив - видалити [] arr;
Даміян

8

Сторінка Segmentation_fault у Вікіпедії має дуже приємний опис про неї, лише вказуючи на причини та причини. Загляньте у вікі для детального опису.

При обчисленні помилка сегментації (часто скорочена до сегмента) або порушення доступу - це помилка, яка виникає за допомогою апаратного забезпечення із захистом пам’яті, повідомляючи операційну систему (ОС) про порушення доступу до пам'яті.

Нижче наведено кілька типових причин помилки сегментації:

  • Перенаправлення покажчиків NULL - це спеціально застосовано апаратне забезпечення управління пам'яттю
  • Спроба отримати доступ до неіснуючої адреси пам'яті (поза адресним простором процесу)
  • Спроба отримати доступ до пам'яті програма не має прав на (наприклад, структури ядра в контексті процесу)
  • Спроба записати пам'ять лише для читання (наприклад, сегмент коду)

Вони, в свою чергу, часто викликані помилками програмування, які призводять до недійсного доступу до пам'яті:

  • Перенаправлення або присвоєння неініціалізованому покажчику (дикий покажчик, який вказує на випадкову адресу пам'яті)

  • Перенаправлення або присвоєння звільненому вказівнику (звисаючий вказівник, який вказує на пам'ять, яку було звільнено / розібрано / видалено)

  • Переповнення буфера.

  • Переповнення стека.

  • Спроба виконати програму, яка не компілюється правильно. (Деякі компілятори будуть видавати виконуваний файл, незважаючи на наявність помилок часу компіляції.)


6

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


3

"Помилка сегментації" означає, що ви намагалися отримати доступ до пам'яті, до якої ви не маєте доступу.

Перша проблема - ваші основні аргументи. Основна функція повинна бути int main(int argc, char *argv[]), і ви повинні перевірити, чи є arc принаймні 2, перш ніж звертатися до argv [1].

Крім того, оскільки ви переходите поплавком до printf (який, до речі, при переході до printf перетворюється в подвійний), вам слід використовувати специфікатор формату% f. Специфікатор формату% s призначений для рядків (масиви символів, що закінчуються '\ 0').


2

Помилка сегментації або порушення доступу виникає, коли програма намагається отримати доступ до місця пам'яті, яке не існує, або намагається отримати доступ до місця пам'яті у спосіб, який заборонено.

 /* "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 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).

2
Перш за все, помилка seg не має нічого спільного з адресою, яка існує чи не існує. Йдеться про те, що ви отримуєте доступ до нього там, де вам не дозволяють цього робити. І у вашому спеціальному прикладі навіть стандартно гарантується, що це місце існує. оскільки стандарт говорить у випадку масиву, слід вказати, що існує дійсна адреса для покажчика pointg на добре вирівняному масиві в його межах І 1 позаду .
dhein

вона також знімається з адресою, якщо у вас немає адреси, і якщо ви намагаєтеся отримати доступ до цієї адреси, також є seg. вина. І в моєму прикладі це лише для розуміння точки зору.
Мохіт Рохілла

2

У відповідях є кілька хороших пояснень "помилки сегментації", але оскільки при помилках сегментації часто виникає дамп вмісту пам'яті, я хотів поділитися тим, де взаємозв'язок між "ядрою, демпінгованою" частиною, у розбитті сегментації (ядро скинуто) та пам'ять походить від:

Приблизно з 1955 по 1975 рік - до напівпровідникової пам'яті - домінуючої технології в пам'яті комп'ютера використовували крихітні магнітні пончики, нанизані на мідні дроти. Пончики були відомі як "феритові ядра" і основна пам'ять, таким чином, відома як "основна пам'ять" або "серцевина".

Взято звідси .


2

Визначень несправності сегментації досить, я хотів би навести кілька прикладів, на які я зіткнувся під час програмування, які можуть здатися дурними помилками, але витрачатимуть багато часу.

  1. ви можете отримати помилку сегментації у нижньому випадку, тоді як аргумент невідповідності типу printf

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

вихід: Segmentation Fault (SIGSEGV)

  1. коли ви забули виділити пам'ять вказівнику, але намагаєтесь її використовувати.

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

вихід: Segmentation Fault (SIGSEGV)


1

Просте значення Segmentation faultполягає в тому, що ви намагаєтеся отримати доступ до деякої пам'яті, яка вам не належить. Segmentation faultвиникає, коли ми намагаємося читати та / або писати завдання в пам'яті, лише для читання, або намагаємось звільнити пам'ять. Іншими словами, ми можемо пояснити це як якесь пошкодження пам’яті.

Нижче я згадую про поширені помилки, які роблять програмісти Segmentation fault.

  • Використовувати scanf()неправильно (забув поставити &).
int num;
scanf("%d", num);// must use &num instead of num
  • Використовуйте покажчики неправильно.
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.
  • Модифікація рядкового літералу (вказівник намагається записати або змінити пам'ять лише для читання.)
char *str;  

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

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Спробуйте дістатися через вже звільнену адресу.
// 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; 
  • Переповнення стека -: не вистачає пам'яті на стеку
  • Доступ до масиву поза межами "
  • Використовуйте неправильні специфікатори формату при використанні printf()та scanf()'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.