|
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
|
|
Понимание процесса разворачивания стека24.03.2017, 12:26. Показов 5167. Ответов 18
Метки нет (Все метки)
Правильно ли я понимаю, что это приблизительно следующее:
Начнем с того что вызовы функций или методов могут быть вложенными и в самом конце завершается первая функция из общей цепочки. Если где то во вложенном вызове функции возникает исключение, то от стека функции в которой возникло исключение начинается поиск обработчика этого исключения. При этом все участки памяти (кадры) стека которые не содержат информации об обработчике исключения - освобождаются и в таком же ключе идет поиск вплоть до самого первой функции в поисках обработчика исключения. Если обработчик найден - очистка кадров останавливается и срабатывает обработчик исключения, а если вообще не найден обработчик - все занятая память на стеке освобождается и программа рушится. Правильно ли я понимаю процесс stack unwiпdiпg? Добавлено через 55 минут Еще читал что если объявить функцию как noexcept то компилятор не генерирует код который нужен для всего этого процесса даже в том случае если в функции есть throw. В этом случае вместо процесса разворачивания стека просто вызывается std::terminate в том случае если дело дойдет до throw - это верно?
1
|
|
| 24.03.2017, 12:26 | |
|
Ответы с готовыми решениями:
18
Код разворачивания? |
|
Форумчанин
8216 / 5047 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
|
||
| 24.03.2017, 12:38 | ||
|
noexcept говорит компилятору, что можно не сохранять порядок вызова функций и перемешивать их как угодно в целях оптимизации. При этом, если исключение всё же будет брошено, то вызовется std::terminate и нет никакой гарантии, что будут вызваны деструкторы всех созданных объектов.
Добавлено через 1 минуту
2
|
||
|
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
|
||
| 24.03.2017, 12:54 [ТС] | ||
|
В целом я верно понял смысл разворачивания стека? И еще вот что интересно. А делают ли так: Допустим есть функция которую мы хотим объявить как noexcept, сама она не кидает исключения но вызывает другие функции которые не являются noexcept. Но при этом мы согласны на то, что если и будет такое исключение(хотя маловероятно т.к например мы вызываем Сишные функции которые не являются noexcept) то пофиг пусть программа падает т.к для нас больший приоритет имеет наличие noexcept. Добавлено через 10 минут То есть несмотря на то что вызываемые функции не имеют гарантий noexcept, тем не менее мы знаем что исключений не будет т.к это СИшные функции а там нет исключений
0
|
||
|
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
|
||||||||
| 24.03.2017, 13:20 | ||||||||
1
|
||||||||
|
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
|
|
| 24.03.2017, 13:25 [ТС] | |
|
Renji,
Понятно, спасибо! Я имел ввиду не извращенные вещи ![]() Вообще за это я и ненавижу windows. Майкрософт постоянно пытается пропихнуть свои нестандартные придумки польза которых зачастую бывает сомнительной...
0
|
|
|
|
||||
| 24.03.2017, 21:09 | ||||
|
При пробрасывании exception'ов через stack unwinding делаются две вещи. На первом проходе по стеку ищется фрэйм стека, который содержит нужный нам catch. На втором проходе по стеку в каждом из фреймов вызываются деструкторы. И только после этого передаётся управление на catch Я не знаю, следует это из стандартов, или ещё откуда-то, но в современных linux'овых glibc ещё stack unwinding используется при отмене потока через pthread_cancel. В этом случае выполняется только второй проход, который идёт по стеку до точки начала потока и вызывает деструкторы Добавлено через 2 минуты
4
|
||||
|
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
|
|||||||
| 24.03.2017, 21:35 | |||||||
0
|
|||||||
|
|
|
| 24.03.2017, 22:06 | |
|
Под "проскакиваением через Сишный фрейм" я имею в виду то, что размотка стека должна продолжаться, а не ломаться. И чтобы она продолжилась, то фрэйм должен содержать информацию CFI (Call Frame Info). Это место зависит от конкретной архитектуры, но, тем не менее, в общем случае если компилятор инфу не предоставит, то размотка стека прекратится с каким-нибудь аварийным сообщением. Тупо потому, что размотчик стека не будет знать, как пойти дальше по стеку. В gcc на интеле опция -fexceptions по умолчанию включена и как-то там нужно было помучться, чтобы отключить (где-то на форуме я выкладывал результат, с ходу что-то не могу найти), а потому просто так не поэкспериментируешь
Добавлено через 28 секунд Как-то там надо было комбинировать с опциями -fno-exceptions -fno-unwind-tables или что-то типа того. Под рукой нету gcc
0
|
|
|
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
|
||
| 24.03.2017, 22:08 | ||
|
0
|
||
|
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
|
||
| 24.03.2017, 22:24 | ||
|
0
|
||
|
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
|
||
| 25.03.2017, 03:00 | ||
|
0
|
||
|
|
||
| 25.03.2017, 14:35 | ||
|
C++ $ cat t.cc void foo (void) { throw int(-1); } Code $ g++ t.cc -O1 -S $ cat t.s ... _Z3foov: .LFB0: .cfi_startproc subl $28, %esp .cfi_def_cfa_offset 32 movl $4, (%esp) call __cxa_allocate_exception movl $-1, (%eax) movl $0, 8(%esp) movl $_ZTIi, 4(%esp) movl %eax, (%esp) call __cxa_throw .cfi_endproc .LFE0: C++ extern "C" void * __cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) _GLIBCXX_NOTHROW { void *ret; thrown_size += sizeof (__cxa_refcounted_exception); ret = malloc (thrown_size); if (!ret) ret = emergency_pool.allocate (thrown_size); if (!ret) std::terminate (); memset (ret, 0, sizeof (__cxa_refcounted_exception)); return (void *)((char *)ret + sizeof (__cxa_refcounted_exception)); } По поводу смеси кода на Си и Си++. gcc под sparc в режиме Си по умолчанию НЕ делает никаких телодвижений по сохранению CFI (Call frame info). В gcc под intel они всё-таки включили по умолчанию, поэтому для интела нужно будет подавать дополнительную опцию -fno-asynchronous-unwind-tables, чтобы сымитировать поведение по умолчанию компилятора с "Си без поддержки проброса исключений". Во всяком случае в моём gcc-4.8.0 нужно использовать именно эту опцию C++ /* Файл t1.cc */ void foo (void) { throw int(1); } extern "C" void bar (void (*)(void)); int main (void) { try { bar (foo); } catch (int) { } return 0; } C /* Файл t2.c */ void bar (void (*fptr)(void)) { fptr(); } Code # Вариант без поддержки проброса исключений в модуле на Си $ g++ -c t1.cc $ gcc -c -fno-asynchronous-unwind-tables t2.c $ g++ t1.o t2.o $ ./a.out terminate called after throwing an instance of 'int' Aborted # Вариант с поддержкой проброса исключений в модуле на Си # (в компилятор Си подаём опцию -fexceptions) $ g++ -c t1.cc $ gcc -c -fno-asynchronous-unwind-tables -fexceptions t2.c $ g++ t1.o t2.o $ ./a.out <ok> Модификация файла t2.c для того, чтобы засветить возможность вызова деструкторов в коде на Си, через который пролетает C++ exception или проход по стеку в случае отмены потока C /* Файл t2.c */ #include <stdio.h> void qux (int *p) { printf ("qux\n"); } void bar (void (*fptr)(void)) { int x __attribute__((cleanup(qux))); fptr(); } Code $ g++ -c t1.cc $ gcc -c -fno-asynchronous-unwind-tables -fexceptions t2.c $ g++ t1.o t2.o $ ./a.out qux Если убрать опцию -fexceptions (и -fno-asynchronous-unwind-tables), то деструктор в Си не будет вызыван. Поскольку компилятор в режиме Си без опции -fexceptions не делает никаких телодвижений для обеспечения вызова деструктора при транзитном пролёте исключений
2
|
||
|
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
|
||
| 25.03.2017, 14:36 | ||
|
Лучше бы прописали в стандарт что память выделяется из thread_local буфера, вмещающего в себя не менее 4*sizeof(void*) байт (указатель на строчку для what+еще чего по мелочи). И разрешили компилятору запрещать бросать исключения превышающие по размеру вышеупомянутый буфер.
0
|
||
|
|
||
| 25.03.2017, 15:14 | ||
|
В стандарте вряд ли прописано, где да как должна выделяться память, это уже компилятор решает. Напиши аналогичный тест, но не с int'ом, а с std::bad_alloc (к сожалению, мои знания в Си++ не позволяют понять, как это сделать). Возможно, что код уже будет другим, т.е. без вызова __cxa_allocate_exception
Добавлено через 31 минуту C++ if (!ret) ret = emergency_pool.allocate (thrown_size);
0
|
||
|
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
|
|||||||||||||||||
| 04.04.2017, 01:22 | |||||||||||||||||
|
Evg, а можете осветить еще пару моментов?
1) вот, допустим, такое:
0
|
|||||||||||||||||
|
|
|
| 04.04.2017, 22:23 | |
|
Сам по себе код отфильтрованный код смотреть не особенно интересно. Его нужно смотреть его вместе с обвесом, который описывает Call Site'ы (диапазоны адресов, из-под которых может прилететь исключение) и Landing Pad'ы (точки, куда приземляется управление при пролёте исключения). Правда я не настолько хорошо во всём этом разбираюсь
Если взять более обобщённый случай (тут даже catch'ей нет): C++ void bar (void); void foo (void) throw (int) { bar(); } Code $ gcc-4.8.0 t.cc -O3 -S $ cat t.s Code .file "t.cc" .text .p2align 4,,15 .globl _Z3foov .type _Z3foov, @function _Z3foov: .LFB0: .cfi_startproc .cfi_personality 0,__gxx_personality_v0 .cfi_lsda 0,.LLSDA0 subl $28, %esp .cfi_def_cfa_offset 32 # Вокруг Call'а стоят метки, описывающие Call Site, они использованы # ниже в таблице, описывающей технику размотки стека при исключении # Через эти метки описан диапазон адресов, в какой программа бы вернулась # при нормальном выходе (через return) из Call'а .LEHB0: call _Z3barv .LEHE0: # Если мы выходим из Call'а через return, то мы просто выполняем # return в текущей процедуре (т.е. следующая за Call'ом операция) addl $28, %esp .cfi_remember_state .cfi_def_cfa_offset 4 ret # Метка .L4 описывает Landing Pad - точку, куда вернётся управление, # если из bar'а вернёмся не через return, а через исключение # Сюда должны прийти два параметра (скорее всего на регистрах): # N1. адрес объекта, который был брошен в исключении # N2. не помню что (какой-то selector), но, грубо говоря, некоторая информация, # на основании которой компилятор поймёт, прилетело исключение хорошего типа # (т.е. можно прокинуть его дальше) или плохого типа (т.е. надо сломаться). # Или что-то в этом роде .L4: .cfi_restore_state # Вероятно, по результату этой операции отработает нижеидущий je addl $1, %edx # Вероятно, подготовка параметра для двух нижеидущих call'ов # Вызовется только один из них, но у обоих одинаковый параметр movl %eax, (%esp) je .L3 .LEHB1: # Прилетело хорошее исключение, вызываем _Unwind_Resume, который # продолжит разматывать стек. В него передаётся параметр N1 call _Unwind_Resume .L3: # Прилетело плохое исключение, надо сломаться # В этот вызов передаётся только параметр N1 call __cxa_call_unexpected .LEHE1: .cfi_endproc .LFE0: .globl __gxx_personality_v0 .section .gcc_except_table,"a",@progbits .align 4 .LLSDA0: .byte 0xff .byte 0 .uleb128 .LLSDATT0-.LLSDATTD0 .LLSDATTD0: .byte 0x1 .uleb128 .LLSDACSE0-.LLSDACSB0 # Начало таблицы Landing Pad'ов .LLSDACSB0: # Запись в таблице, говорящяя, что если раскрутчик стека видит, # что если прилетело исключение из-под операции Call, которая нормально # вернулась бы в диапазон адресов .LEHB0 - .LEHE0 (т.е. так описывается # операция Call bar), то нужно пойти на Landing Pad, описанный меткой .L4 # Последняя единичка описывает Action 1 (см. ниже) .uleb128 .LEHB0-.LFB0 .uleb128 .LEHE0-.LEHB0 .uleb128 .L4-.LFB0 .uleb128 0x1 # Следующая запись в таблице .uleb128 .LEHB1-.LFB0 .uleb128 .LEHE1-.LEHB1 .uleb128 0 .uleb128 0 # Начало таблицы Action'ов .LLSDACSE0: # Action 1 # Тут закодирована информация, что нужно фильтровать тип 1 (см. ниже) .byte 0x7f .byte 0 .align 4 # Тип 1 (int). Описывается в виде указателя на typeinfo-объект .long _ZTIi # Middle-point таблицы типов. Вверх растут описания типов .LLSDATT0: .byte 0x1 .byte 0 .text .size _Z3foov, .-_Z3foov .ident "GCC: (GNU) 4.8.0" .section .note.GNU-stack,"",@progbits http://www.airs.com/blog/archives/464 http://mentorembedded.github.i... ptions.pdf Вторая ссылка уже битая. Если на работе остался pdf, то выложу. При прочтении обоих документов в несколько итераций можно понять общий принцип и понимать, как читать код из-под компилятора. На моей памяти код из-под clang'а читать намного удобнее, т.к. таблица там отмечена внятными комментариями. Но у clang'а и gcc немного разные модели того, как нужно ломаться в случае неправильного исключения, поэтому код будет немного разный Добавлено через 19 минут Перед меткой .L4 поправил комментарий (неправильно был описан параметр N1)
1
|
|
|
Вездепух
12944 / 6811 / 1821
Регистрация: 18.10.2014
Сообщений: 17,236
|
||||
| 05.04.2017, 03:37 | ||||
std::terminate без раскрутки стека. (Будет ли в таких случаях раскрутка - определяется реализацией.)
1
|
||||
|
|
|
| 06.04.2017, 09:32 | |
|
1
|
|
| 06.04.2017, 09:32 | |
|
Помогаю со студенческими работами здесь
19
Считать из стека верхний элемент и меняет значение переменной стека Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ВВЕДЕНИЕ
Выполняя задание на управление насосной группой заполнения резервуара,. . .
|
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
|
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога
Финальные проекты на Си и на C++:
hello-sdl3-c. zip
hello-sdl3-cpp. zip
Результат:
|
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога
MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
|
|
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд.
Даже если у вас. . .
|
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает
монорепозиторий в котором находятся все исходники.
При создании нового решения, мы просто добавляем нужные проекты
и имеем. . .
|
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение:
В этой книге («Подход, основанный на вариантах использования») Ивар утверждает,
что архитектура программного обеспечения — это
структуры,. . .
|
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога
Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
|