В какой момент происходит segfault? - PullRequest
3 голосов
/ 01 июня 2009

Имеет ли следующий код segault в массиве [10] = 22 или массиве [9999] = 22?
Я просто пытаюсь выяснить, будет ли весь код выполняться до того, как произойдет сбой. (на языке Си).

#include <stdio.h>
int main(){

int array[10];
int i;
for(i=0; i<9999; ++i){
    array[i] = 22;
}    
return 0;    
}

Ответы [ 8 ]

13 голосов
/ 01 июня 2009

Это зависит ... Если память после массива [9] чиста, то не может произойти, пока, конечно, не будет достигнут сегмент памяти, который занят.

Попробуйте код и добавьте:

 printf("%d\n",i);

в цикле, и вы увидите, когда он падает и горит. Я получаю различные результаты, в диапазоне от 596 до 2380.

9 голосов
/ 01 июня 2009

Использовать отладчик ?

$ gcc -g seg.c -o so_segfault
$ gdb so_segfault
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) run
Starting program: /.../so_segfault 
Program received signal SIGSEGV, Segmentation fault.
0x080483b1 in main () at seg.c:7
7       array[i] = 22;
(gdb) print i
$1 = 2406
(gdb) 

На самом деле, если вы запустите это снова, вы увидите, что segfault не всегда происходит при одном и том же значении i. Не вызывает сомнений то, что это происходит, когда i> = 10, но нет способа определить значение для i, для которого произойдет сбой, потому что это не является детерминированным: это зависит от того, как распределена память. Если память свободна до массива [222] (иначе никакие другие программы его не используют), она будет работать до i = 222, но может также аварийно завершить работу при любом другом значении i> = 10.

8 голосов
/ 01 июня 2009

Ответ может быть. Язык C не говорит ничего о том, что должно произойти в этом случае. Это неопределенное поведение. Компилятору не требуется обнаруживать проблему, делать что-либо для ее решения, завершать программу или что-то еще. И поэтому он ничего не делает.

Вы пишете в память, которая не принадлежит вам, и на практике может произойти одно из трех:

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

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

4 голосов
/ 01 июня 2009

Когда и если ваш код падает, не является детерминированным. Это будет зависеть от того, на какой платформе вы запускаете код.

array - это переменная стека, поэтому ваш компилятор зарезервирует для нее 10 * sizeof(int) байт в стеке. В зависимости от того, как компилятор упорядочивает другие локальные переменные и как растет ваш стек, i может прийти сразу после array. Если вы последуете совету Даниила и вставите оператор printf, вы можете заметить интересный эффект. На моей платформе, когда i = 10, array[10] = 22 clobbers i и следующее назначение - array[23].

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

Если вы вместо этого выделили array в куче (используя malloc()), тогда вы получите SIGSEGV, когда выйдете за пределы границы страницы. Даже 10-байтовое распределение вернет целую страницу. Размеры страниц зависят от платформы. Обратите внимание, что некоторые отладчики malloc могут попытаться пометить массив как недопустимый, но вы не получите SIGSEGV, если не задействуете аппаратное обеспечение, когда вы запустите конец страницы.

3 голосов
/ 01 июня 2009

От того, где ваш код будет зависать, зависит от того, какой компилятор вы используете, удачи и других деталей связывания рассматриваемой программы. Скорее всего, вы не segfault для i == 10. Даже если это находится за пределами вашего массива, вам почти наверняка будет выделена память для вашего процесса в этом месте. Однако, продолжая выходить за пределы массива, вы в конечном итоге оставляете память, выделенную для вашего процесса, и затем принимаете segfault.

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

Это не очень детерминистично.

2 голосов
/ 01 июня 2009

ошибка сегментации происходит при доступе вне выделенной памяти процессов, это не легко предсказать. Когда я == 10, это вне массива .. но все еще может быть в памяти процесса. Это зависит от того, как распределялась память процессов, что-то, что невозможно (обычно) узнать (зависит от менеджера памяти ОС). Таким образом, segfault может произойти при любом из i = 10 - 9999 или не произойти вообще.

0 голосов
/ 01 июня 2009

В более общем смысле вы можете выяснить, где происходит ошибка сегментации в системах Linux, с помощью отладчика. Например, чтобы использовать gdb, скомпилируйте вашу программу с символами отладки, используя флаг -g, например ::

gcc -g segfault.c -o segfault

Затем вызовите gdb с вашей программой и любыми аргументами, используя флаг --args, .g.:

gdb --args ./segault myarg1 myarg2 ...

Затем, когда запускается отладчик, введите run, и ваша программа должна работать до получения SIGSEGV и сообщать вам, где она находится в исходном коде, когда она получила этот сигнал.

0 голосов
/ 01 июня 2009

Я предлагаю использовать GDB для исследования таких проблем: http://www.gnu.org/software/gdb/documentation/

...