LINUX.ORG.RU

C + random + array + segfault

 , , ,


0

1
#include <stdio.h>

main() {
    srand(time(NULL));
    int arr[9999999];
    int i;
    for(i=0; i<9999999; i++) {
        arr[i] = rand() % 99999999;
        printf("%d \n", arr[i]);
    }
}

решил откопать книгу по Си. Первый пример, ловлю segfault. Что не так? вывод strace:

execve("./a.out", ["./a.out"], [/* 47 vars */]) = 0
brk(0)                                  = 0x87a1000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7716000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=111260, ...}) = 0
mmap2(NULL, 111260, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76fa000
close(3)                                = 0
open("/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0\227\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1790836, ...}) = 0
mmap2(NULL, 1591836, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7575000
mprotect(0xb76f3000, 4096, PROT_NONE)   = 0
mmap2(0xb76f4000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17e) = 0xb76f4000
mmap2(0xb76f7000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb76f7000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7574000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75746c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb76f4000, 8192, PROT_READ)   = 0
mprotect(0xb7738000, 4096, PROT_READ)   = 0
munmap(0xb76fa000, 111260)              = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Ошибка сегментирования
ничего не понимаю :)

Deleted

Последнее исправление: CYB3R (всего исправлений: 2)

Ответ на: комментарий от emulek

в сишке локальные переменные объявляются в стеке, ВСЕГДА.
в сишке ты можешь и в куче выделить, и в стеке

так куда локальные переменные выделяются? через static выделяются в кучу, а без указания static просто выделяются в стек? malloc, как понял - динамическая память и является также кучей? но ведь делая static переменная в си, я не делаю malloc. Также как и в паскале, объявляя переменную я не делаю new/dispose. Но могу и new/dispose если хочется. В сишке как понял также.

Deleted
()
Ответ на: комментарий от emulek

функция f() может отдать другой функции указатель на свою static int x;, и тогда другая f2() может юзать этот x.

А вот если просто int x, то отдать указатель нельзя, ибо x будет уничтожена.

приведи пример, пожалуйста, самый простой, иллюстрирующий это.

«просто» возвращает int.

и int func - тоже возвращает int.

void значит НИЧЕГО
NULL это указатель, который НИКУДА не указывает.
nil, ЕМНИП это просто пустой объект. Хотя я не в курсе, что это.

это как NULL, если я правильно понял. nil - это ноль для указателя. Но ноль не как 0, а как отсутствие чего бы то не было.

Да, это важно при оптимизации, ибо функция может быть объявлена в другом модуле, и компилятор не увидит, нужно-ли кому-то возвращаемое значение, или нет. Ну и придётся что-то возвращать, и тратить время. Как например в printf(3), которая возвращает число напечатанных байт(а может символов, я не помню). Обычно эта информация не используется, но всё равно формируется и передаётся в коде.

Спасибо за разъяснение. Получается void для того, чтоб объявить процедуру вместо функции?

Deleted
()
Ответ на: комментарий от ziemin

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

Deleted
()
Ответ на: комментарий от Deleted

Подойди к вопросу со стороны «железа».

Рассмотрим процесс загрузки. Кстати тут очень важно понять, что программа не сама запускается, а её запускают!

То, как система шарит по диску, проверяет, что файл исполняемый, сверяет всякие контрольные суммы мы опустим.

Сначала в дело вступает загрузчик. Он парсит заголовок исполняемого файла (в случае очень древнего «прямого» формата coff создаёт). Оттуда он выясняет, из каких секций состоит программа. И резервирует под них память

Далее начинается встроенный загрузчик самой программы (это всё происходит ДО работы программы).

Он дополнительно выделяет память и инициализирует секции.

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

Но, для упрощения файл всё-таки делят на секции кода. Плюс в 86 (и других) процах можно установить у памяти признак исполняемости.

Но! Данные бывают разные. Например ты можешь замутить массив чисел в программе. Я имею в виду реально ввести в программу числа. Например:

const double data[] = { 3.14, 2.7, 1.6 ........};

Это инициализированные данные (уже подраздел данных).

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

Таким образом возникает ещё одно деление. Отдельно идут неинициализированные данные, которые установишь ты. Компилятор не дурак и два раза работу делать не будет.

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

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

Т.о. main это всего лишь обычная рядовая функция, которую вызывает настоящая запускаемая функция. Она находится вроде в библиотеке libstart или как-то так.

Тьфу-ты блин. Как-то размазанно получилось. Задавай свои ответы.

ziemin ★★
()
Последнее исправление: ziemin (всего исправлений: 1)
Ответ на: комментарий от anonymous

Зато из поисковиков кто-нить придёт. Кому-нибудь пригодится. А на меня стих нашёл :)

ziemin ★★
()
Ответ на: комментарий от emulek

ты вообще представляешь себе, что этот твой код будет вставляться В КАЖДЫЙ сраный int a???

У тебя мама-папа был? Что ты такой злой как собака? Ты что такое -S у gcc знаешь? Посмотри как на практике выделяется место в стеке, все не так страшно как ты думаешь ибо выделяется оно при входе в функцию одной командой которую я тебе привел выше. Достаточно добавить cmp перед вычитанием и вуаля.

quest ★★★★
()
Ответ на: комментарий от ziemin

Кстати. Следует очень чётко разделять программную и аппаратную модели. Многое из того, что я описал, реализовано чипом.

Но в любом случае программная модель более полная. Просто потому, что эмулирует недостающие части оборудования.

Плюс я ещё не расписал инициализацию библиотек. Это отдельная песня. Нужно понимать, что загрузка производится загрузчиком ОС (хотя можно и грузить динамически), и у них есть собственные инициализаторы.

ziemin ★★
()
Ответ на: комментарий от Deleted

в сишке локальные переменные объявляются в стеке, ВСЕГДА. в сишке ты можешь и в куче выделить, и в стеке

так куда локальные переменные выделяются?

В СТЕК. Неграмотный что-ли? Куча в сишке глобальна, и единственна.

через static выделяются в кучу, а без указания static просто выделяются в стек?

нет. Через static выделяются компилятором во время компиляции, в специальной секции бинарника.

В куче в рантайме функцией malloc(3).

malloc, как понял - динамическая память и является также кучей?

в си — да. Хотя в теории можно и в стеке сделать динамическую. И ещё где-то. Зависит от твоей упоротости, и упоротости аффтора ЯП.

но ведь делая static переменная в си, я не делаю malloc.

верно. Если сделать static строку «мудак», её можно потом нагрепать в бинарнике.

Также как и в паскале, объявляя переменную я не делаю new/dispose. Но могу и new/dispose если хочется. В сишке как понял также.

я не знаю, как оно там сейчас реализовано в паскакале.

emulek
()
Ответ на: комментарий от ziemin

эти разделы по разному называются и находятся в зависимости от типа исполняемого файла, ОС и архитектуры? Правильно ли понял что память одна, и разные методы работы с участками памяти называют стеком, кучей, участком кода опять же в зависимости от файла, ОС, архитектуры? Получается что у каждого соглашения (по работе с этим участком памяти) есть ограничения, и нужно указывать в языке куда что складывать, и когда мы складываем не те данные не в тот участок - программа работает некорректно (что случилось в моём случае)?

Deleted
()
Ответ на: комментарий от anonymous

А ты не видишь, что ему больше на ЛОРе поболтать охота, а не узнавать что либо?

мне интересно и то и это.

Deleted
()
Ответ на: комментарий от ziemin

Но в любом случае программная модель более полная. Просто потому, что эмулирует недостающие части оборудования.

какие например?

Deleted
()
Ответ на: комментарий от Deleted

Например сопроцессор. В некоторых случаях прерывания.

ziemin ★★
()
Ответ на: комментарий от Deleted

приведи пример, пожалуйста, самый простой, иллюстрирующий это.

#include <stdio.h>
#include <string.h>

int *f1()
{
	static int a[9001];
	memset(a, 1, sizeof(int)*9001);
	return a;
}

int *f2()
{
	static int a[9001];
	memset(a, 2, sizeof(int)*9001);
	return a;
}

int main()
{
	int *a1 = f1();
	int *a2 = f2();
	printf("a1=%x, a2=%x\n", a1[17], a2[17]);
	return 0;
}

попробуй убрать static, и посмотри что получится.

emulek
()
Ответ на: комментарий от emulek

В СТЕК. Неграмотный что-ли? Куча в сишке глобальна, и единственна.

просто я понял тебя так: что локальные переменные объявляются в стеке, но в отличие от паскаля, где есть только один вариант, в сишке можно объявлять их и в стеке, и в куче.

нет. Через static выделяются компилятором во время компиляции, в специальной секции бинарника.
В куче в рантайме функцией malloc(3).

так.

верно. Если сделать static строку «мудак», её можно потом нагрепать в бинарнике.

ага, а если я делаю «неинициализированную переменную», то она заполняется чем-то условным, а потом в коде, инструкцией помещается в неё что-то своё? переменная при этом остаётся в секции бинарника?

Deleted
()
Ответ на: комментарий от Deleted

эти разделы по разному называются и находятся в зависимости от типа исполняемого файла, ОС и архитектуры?

Я тут распинаюсь, а ты главного так и не понял? Это модель по которой загружается любой процесс. Может я чего и упустил, но в общих чертах это так.

Рано тебе сосредотачиваться на деталях. Сначала пойми в общем.

ziemin ★★
()
Ответ на: комментарий от Deleted

и int func - тоже возвращает int.

ты нерусский что-ли? void НИЧЕГО НЕ ВОЗВРАЩАЕТ.

это как NULL, если я правильно понял.

я не знаю, и знать не желаю, что там у тебя nil значит.

Советую и тебе забыть.

Спасибо за разъяснение. Получается void для того, чтоб объявить процедуру вместо функции?

в сишке есть только функции, они же «процедуры». Функция может возвращать значение, а может и не возвращать. Зависит от её описания.

emulek
()
Ответ на: комментарий от quest

У тебя мама-папа был? Что ты такой злой как собака? Ты что такое -S у gcc знаешь? Посмотри как на практике выделяется место в стеке, все не так страшно как ты думаешь

покажи, какой код получится с твоим ключиком.

Достаточно добавить cmp перед вычитанием

не. Ещё надо добавить cmp В КАЖДОМ доступе.

emulek
()
Ответ на: комментарий от Deleted

просто я понял тебя так: что локальные переменные объявляются в стеке, но в отличие от паскаля, где есть только один вариант, в сишке можно объявлять их и в стеке, и в куче.

ну так правильно, есть два варианта: в стеке и СТАТИЧЕСКИ. НЕ в куче!

В куче можно только через malloc(3), и всё.

ага, а если я делаю «неинициализированную переменную», то она заполняется чем-то условным, а потом в коде, инструкцией помещается в неё что-то своё? переменная при этом остаётся в секции бинарника?

не. Статическая неинициализированная рамером 11 байт заполняется нулями. А если ты её инициализировал «мудак», то заполняется байтами «мудак».

Потому строки всегда const. Ибо программы на сишке не могут менять свой код (только грязными хаками).

emulek
()
Ответ на: комментарий от emulek

Потому строки всегда const.

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

ziemin ★★
()
Ответ на: комментарий от ziemin

Потому строки всегда const.

Неправда.

правда. Я про сишку говорил. А если ты про архитектуру, то в современных x86 уже много лет тоже так. Писать в код было возможно только в 80х годах прошлого века. Но видно 30+ лет прогресса прошли мимо тебя.

emulek
()
Ответ на: комментарий от quest

а нельзя ли тут вставить код который будет проверять можно вычитать или нет?

а вот нельзя :-) лимиты неизвестны.

да даже с отключенными лимитами, при расширении пространства стека (тот-же mmap с другими флагами), при странично-сегментной памяти, можно получить стек 1005000 и даже записать пару байт. Но при записи/чтении 9999999 байт оттуда получишь сегфолт - реальных страниц нет.

MKuznetsov ★★★★★
()
Ответ на: комментарий от emulek

А если ты про архитектуру, то в современных x86 уже много лет тоже так

Какой же ты ограниченный. Написал же, что в принстонской архитектуре.

Но видно 30+ лет прогресса прошли мимо тебя.

Сам нагуглишь контроллер или мне? Их не меньше (а то и больше, чем ПК). А язык один - C.

ziemin ★★
()
Ответ на: комментарий от ziemin

Сам нагуглишь контроллер или мне?

в контроллере запись в код обычно просто бессмысленна, там же ROM(которая просто так, как RAM, не переписывается).

Не, ну может и есть какие-то чипы с RAM вместо ROM, но я не очень понимаю, на кой ляд они нужны.

Впрочем, я в этой вашей встройке не очень. Я уже много лет работаю исключительно с нормальными CPU.

emulek
()
Ответ на: комментарий от quest

Достаточно добавить cmp перед вычитанием и вуаля.

Я не понимаю, что ты доказываешь что-то, нифига в этом не понимая? С чем ты будешь сравнивать? Заранее верхушку стека ты никак не узнаешь. Она в разных системах разная и легко меняемая. Даже bottom в любой нити, кроме главной, будет в произвольном месте (что mmap вернет, то и будет). Ну а «начало» стека главной нити, как правило, в самом конце адресного пространства.

anonymous
()
Ответ на: комментарий от quest

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

anonymous
()
Ответ на: комментарий от MKuznetsov

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

подозреваю что это регулируется настройками overcommit memory

quest ★★★★
()
Ответ на: комментарий от emulek

покажи, какой код получится с твоим ключиком.

прошелся отладчиком, да походу все не так просто

не. Ещё надо добавить cmp В КАЖДОМ доступе.

опять же подозреваю что это регулируется настройками overcommit memory

quest ★★★★
()
Ответ на: комментарий от quest

слушай, мне лениво спорить и повторять очевидные вещи.

Думаешь ты самый умный, и никто до тебя до этого не додумался?

На самом деле, стек тем и ценен, что там никаких проверок нет, потому память там выделяется/освобождается быстрее всего. (для анонимуса, который в этом месте обязательно влезет: да, не считая регистров. Ясное дело, что «выделить» регистр быстрее. Не нужно мне про это рассказывать, я знаю).

А если ты хочешь проверки и т.п., то юзай кучу. Там всё есть. Но она таки медленнее.

emulek
()
Ответ на: комментарий от anonymous

Ну а «начало» стека главной нити, как правило, в самом конце адресного пространства.

Я думал что там отдельный сегмент который от 0 до X и при выделении памяти на стеке, при subq $40000000, %rsp мы вычитаем число большее чем есть в rsp. Как видно это не так.

quest ★★★★
()
Ответ на: комментарий от emulek

Тебя не удивляет что в std::vector к примеру есть [] а at()? А то что разные уровни оптимизации в компиляторе есть не удивляет?

А если ты хочешь проверки и т.п., то юзай кучу. Там всё есть. Но она таки медленнее.

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

quest ★★★★
()
Ответ на: комментарий от emulek

Думаешь ты самый умный, и никто до тебя до этого не додумался?

Я вообще был уверен что такой флаг есть и где-то когда-то давно я видел «stack overflow». В свете массивов переменного размера которые появились с C98 вроде, нехватка стека особенно опасна. Я то заюзаю кучу, а кто-то нет и найти проблему хочется быстро.

quest ★★★★
()
Ответ на: комментарий от quest

Ой, ну на уже. Только код не портабельный и под FreeBSD, но ты уже поймешь, как это можно сделать.

Да, pthreads почему-то не возвращает начало стека для главной нити, поэтому код, приводящий к переполнению, приходится запускать в отдельной:

http://pastebin.com/yR3uGmAA

Очисткой ресурсов я не занимался, так как это только демонстрация

stopitplease
()
Ответ на: комментарий от quest

А для одной нити ты можешь сделать так, опять же чисто платформозависимо:

http://pastebin.com/GzL6QQnx

Увы, pthread_attr_getstack дает верно тольео размер стека для главной нити.

stopitplease
()
Ответ на: комментарий от quest

Вывод такой:

> ./test 
Segfault in thread 0x80141ee00
Stack = 0x800000000000, Stack size = 0x00200000
RSP = 0x7ffffd9d7e00, Stack boundary = 0x7fffffe00000
Stack overflow
Segmentation fault (core dumped)
stopitplease
()
Ответ на: комментарий от quest

Тебя не удивляет что в std::vector к примеру есть [] а at()? А то что разные уровни оптимизации в компиляторе есть не удивляет?

нет.

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

не нужно создавать лишних проблем.

emulek
()
Ответ на: комментарий от stopitplease

но ты уже поймешь, как это можно сделать.

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

quest ★★★★
()
Ответ на: комментарий от quest

Смотри второй пример, там прям с главной нити ловит.

Непортабельных ньюанса три:

1) Архитектура

2) Структура mcontext_t в каждой оси своя

3) pthread_attr_get_np - непортабельная функция (_np это и значит). В линуксе, ЕМНИП, pthread_getattr_np

stopitplease
()

Даже не стал пробовать компилировать этот код с сверх широкими массами, но почему бы не выделять память malloc'ом ? :)

Sharezil
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.