То, что я написал некоторое время назад для образовательных целей ...
Рассмотрим следующую c-программу:
int q[200];
main(void) {
int i;
for(i=0;i<2000;i++) {
q[i]=i;
}
}
после компиляции и выполнения, создается дамп ядра:
$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
теперь использует gdb для анализа после смерти:
$ gdb -q ./a.out core
Program terminated with signal 11, Segmentation fault.
[New process 7221]
#0 0x080483b4 in main () at s.c:8
8 q[i]=i;
(gdb) p i
$1 = 1008
(gdb)
да, программа не работала с ошибкой, когда кто-то писал за пределами 200 выделенных элементов, вместо этого она зависала при i = 1008, почему?
Введите страницы.
В UNIX / Linux можно определить размер страницы несколькими способами, один из них - использовать системную функцию sysconf () следующим образом:
#include <stdio.h>
#include <unistd.h> // sysconf(3)
int main(void) {
printf("The page size for this system is %ld bytes.\n",
sysconf(_SC_PAGESIZE));
return 0;
}
, который дает вывод:
Размер страницы для этой системы составляет 4096 байт.
или можно использовать утилиту командной строки getconf следующим образом:
$ getconf PAGESIZE
4096
вскрытие
Оказывается, что ошибка происходит не при i = 200, а при i = 1008, давайте выясним, почему. Запустите GDB, чтобы выполнить анализ после смерти:
$gdb -q ./a.out core
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 4605]
#0 0x080483b4 in main () at seg.c:6
6 q[i]=i;
(gdb) p i
$1 = 1008
(gdb) p &q
$2 = (int (*)[200]) 0x804a040
(gdb) p &q[199]
$3 = (int *) 0x804a35c
q заканчивается по адресу 0x804a35c, точнее, последний байт q [199] находился в этом месте. Размер страницы, как мы видели ранее, составляет 4096 байт, а размер 32-разрядного слова компьютера позволяет разделить виртуальный адрес на 20-разрядный номер страницы и 12-разрядное смещение.
q [] закончилось номером виртуальной страницы:
0x804a = 32842
смещение:
0x35c = 860
так было еще:
4096 - 864 = 3232
байтов осталось на той странице памяти, на которой был выделен q []. Это пространство может содержать:
3232/4 = 808
целые числа, и код рассматривал его так, как если бы он содержал элементы q в позиции от 200 до 1008.
Мы все знаем, что этих элементов не существует, и компилятор не жаловался, как и hw, поскольку у нас есть права на запись для этой страницы. Только когда i = 1008, q [] ссылался на адрес на другой странице, для которого у нас не было разрешения на запись, виртуальная память обнаружила это и вызвала ошибку.
Целое число хранится в 4 байтах, это означает, что эта страница содержит 808 (3236/4) дополнительных фальшивых элементов, означающих, что все еще совершенно законно получить доступ к этим элементам от q [200], q [201] вплоть до к элементу 199 + 808 = 1007 (q [1007]) без вызова ошибки сегмента. При доступе к q [1008] вы открываете новую страницу, для которой права различаются.