3

У меня возникли трудности с пониманием того, как ОС передает данные из адресного пространства родительского процесса в адресное пространство дочернего процесса. А именно, в программе на C, где argc и argv сохраняются при передаче в main?Где хранится OS argv и argc при выполнении дочернего процесса?

Я понимаю, как argv является по существу двойным указателем. То, что я не понимаю, это то, что ОС делает с этими значениями после загрузки их в ядро. Создав адресное пространство для дочернего процесса, оно подталкивает эти значения в стек нового пространства? Очевидно, мы не хотим передавать указатели в другое адресное пространство.

Для записи я работаю с архитектурой MIPS32.

ответ

4

В Linux, по крайней мере, на архитектурах я играл с, процесс начинается с %esp указывая на что-то вроде:

argc | argv[0] | argv[1] | ... argv[argc - 1] | argv[argc] == NULL | envp[0] | envp[1] ... envp[?] == NULL 

Первая функция называется традиционно назван _start, и его работа состоит в том, чтобы вычислить (argc = %esp, argv = ((char *)%esp) + 1, envp = ((char *)%esp) + argc + 2), затем вызовите main с соответствующим соглашением о вызовах.

На x86 аргументы передаются в стек.

На amd64 они передаются в регистрах %rdi, %rsi и %rdx.

На трудоемкости, Google говорит мне, что есть несколько различных соглашения о вызове в использовании - в том числе O32, N32, N64 - но все они используют $a0, $a1, $a2 первым.

+0

Можете ли вы добавить доказательства или ссылки на соответствующие источники? – xmojmr

+0

Теперь как насчет указателей на аргументы (а именно указатель на argv)? Если сами аргументы бросаются в стек, указатель также помещается в стек или передается в регистр аргументов (я использую MIPS-архитектуру)? – audiFanatic

+0

@audiFanatic by "arguments" Я имею в виду аргументы функции для вызывающего соглашения: 'argc',' argv' и 'envp'; аргументы функции всегда являются значениями; прежде чем вычисляются значения, которые являются указателями (хотя довольно часто используется «pop% esp» для первого и избавляется от пучка + 4s или + 8s). – o11c

3

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

Когда ОС [Microsoft] создает процесс, он выделяет блок среды процесса для хранения данных, специфичных для этого процесса. Сюда входят, помимо прочего, аргументы командной строки, с которыми была вызвана программа. Этот блок среды процесса выделяется из адресного пространства целевого процесса, и указатель на него предоставляется в точку входа процесса. Блок среды процесса для дочернего процесса обычно инициализируется копированием блока среды родительского процесса в адресное пространство нового процесса - нет прямого обмена используемой памятью.

В случае программы на основе C точка входа не является функцией main(), которую предоставляет программатор. Скорее, это процедура, предоставляемая библиотекой времени выполнения C, которая отвечает за инициализацию среды выполнения перед передачей управления программисту main().

Существует много вещей, чтобы инициализировать, но один из аспектов является создание в ARGC и ARGV значения. Чтобы это сделать, среда выполнения проверит блок среды процесса, чтобы найти имя и параметры программы, с которыми он был вызван. Затем он (обычно) выделяет значения для argv из кучи процесса (то есть, используя что-то вроде malloc()) и присваивает argc количеству найденных параметров (плюс один, для имени программы).

Фактические значения для argc и argv помещаются в стек, как и любые другие параметры, переданные в C, поскольку вызов main() по времени выполнения C является обычным вызовом функции.

Итак, когда код, который вы пишете внутри main(), в дочернем процессе обращается к argv, он будет считывать значения из собственной кучи процесса. Источником этих значений является блок среды процесса (хранится ОС в локальном адресном пространстве), который был первоначально инициализирован путем копирования блока среды процесса из родительского процесса.

На платформах * nix все совсем по-другому. Основное различие для настоящего обсуждения заключается в том, что nix будет хранить аргументы командной строки для нового процесса непосредственно в стеке стека исходного потока процесса. (Здесь также хранятся переменные среды.) Так что на * nix, main вызывается с параметром argv, указывающим на значения, хранящиеся в самом стеке.

Вы можете получить некоторые из этого в man-странице для execve, а в файле Linux Programming Interface от Michael Kerrisk есть хорошее описание в разделе 6.4, которое вы можете найти в онлайн-отрывке.

+1

Это верно для Windows-подобных ОС, которые можно увидеть в [Wine source: /include/winternl.h](http://source.winehq.org/git/wine.git/blob/HEAD:/include/winternl .h) (поиск «CommandLine») и [источник ReactOS: /include/psdk/winternl.h](http://svn.reactos.org/reactos/trunk/reactos/include/psdk/winternl.h) (поиск «CommandLine»), отслеживая реализацию функции [MSDN: GetCommandLine] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms683156 (v = vs.85) .aspx). Но верно ли это и для Linux-подобных ОС? Можете ли вы добавить доказательства или связанные источники? – xmojmr