Изменения

Перейти к: навигация, поиск

Выполнение программы

4985 байт добавлено, 19:51, 11 июля 2011
Нет описания правки
==Последовательное выполнение==
При старте процесса создается единица выполнения кода {{- --}} ''поток''. Поток имеет точку Потоку передается точка входа {{--- }} адрес нахождения первой команды программы в адресном пространстве, и счетчик команд. Каждая команда занимает несколько байт, не обязательно постоянное число. Можно считать, что поток передает команду и ее аргументы процессору, тот выполняет ее и сообщает результат, после этого поток переходит на следующую команду с помощью счетчика команд. Каждая команда в адресном пространстве занимает несколько байт, не обязательно постоянное число.
{{TODO|t=запилить пруфлинк на то, как на самом деле}}
{{TODO|t=можно написать про переключение потоков}}
{{TODO==Вызов функций=====Адрес возврата===[[Файл:Func.png|right|frame|t=запилить картинку <center>Вызов функции</center>]]Представим, что в нашем коде есть функция, не принимающая аргументов, ничего не возвращающая, иничего не выполняющая: void f() { }Для ее вызова в адресном пространстве будет использоваться команда <tex>call f</или пример tex>, которая переведет поток на дизассемблированном коденачало функции <tex>f</tex>, а для возврата обратно в место, откуда функция была вызвана {{---}}команда <tex>ret</tex>. Возникает вопрос: как найти адрес команды, к которой надо перейти после выполнения <tex>ret</tex>? Можно сохранить этот адрес в переменной, но, в таком случае, нельзя будет вызывать функции из других функций, или создавать рекурсии: void f() { } void g() {TODO|t f(); }Поэтому, в адресном пространстве существует ''стек'', на который при вызове функции кладется адрес возврата. Команда <tex>ret</tex> удаляет текущий адрес возврата с вершины стека, и устанавливает счетчик команд по этому адресу.===Передача параметров===запилить пруфлинк на Представим, что теперь нам необходимо вызвать чуть более сложную функцию: void f(int a, int b) { }Передавать аргументы внутрь функции удобно, также используя стек. Во всех конвенциях вызова C++ сначала передаются аргументы в обратном порядке, а потом - адрес возврата, тоесть, вызов <tex>f(239, 566)</tex> будет исполнен как push 566 push 239 call f, где <tex>push</tex> <tex>n</tex> {{---}} команда "положить на самом делестек n". Адрес возврата положится на стек при вызове команды <tex>call</tex>.===Конвенции вызова=======Отличия конвенций====Заметим, что мы положили аргументы на стек, но не удалили их оттуда, поэтому при следующем вызове <tex>ret</tex> будет взят неверный адрес возврата. В С++ существует несколько конвенций вызова функций, рассмотрим 2 из них: <tex>\_\_stdcall</tex> и <tex>\_\_cdecl</tex>. При использовании <tex>\_\_stdcall</tex> аргументы удалятся из стека при выполнении команды <tex>ret</tex>, а при использовании <tex>\_\_cdecl</tex> после возврата из функции необходимо переместить вершину стека командой <tex>add</tex>. Рассмотрим пример: void __cdecl f(int a, int b) { } void __cdecl g(int a, int b, int c) { } int main() { f(2, 3); g(1, 2, 3); return 0; }При выполнении этого кода можно будет сэкономить на количестве команд, не удаляя аргументы <tex>2</tex> и <tex>3</tex> со стека: push 3 push 2 call f push 1 call g add esp, 12====Конфликт конвенций====По умолчанию в С++ используются конвенции вызова <tex>\_\_cdecl</tex> и <tex>\_\_thiscall</tex>, которая используется для методов класса и отличается тем, что кладет один аргумент в регистр процессора.
Посмотрим, что произойдет, если искусственно создать конфликт конвенций вызова: void __decl f(int a) { } int main() { void __stdcall (*g)(int); g =(void __stdcall (*)(int)) &f; g(239); return 0; }Так как <tex>g</tex> задана конвенция вызова <tex>\_\_stdcall</tex>, после ее вызова вершина стека не будет сдвигаться. Однако, <tex>f</tex> задана конвенция вызова <tex>\_\_cdecl</tex>, поэтому аргумент не удалится со стека при вызове <tex>ret</tex>, и при следующем запросе адреса возврата будет выдан адрес <tex>239</tex>, скорее всего, не являющийся валидным.=Вызов функций=Размещение локальных переменных==
97
правок

Навигация