2009-10-21 5 views
16

Программа скомпилирована с некоторого языка в ASM -> Машинный код (непосредственно исполняемый файл). Когда люди говорят, что это зависит от платформы, значит, что созданные двоичные файлы будут выполняться (правильно) только на процессорах с такой же архитектурой набора инструкций, как x86, x86-64. Он может (неправильно)/не может (вообще) работать на других процессах из-за разницы в ISA. Правильно?Как выполняется программа? Где действуют операционные системы?

Теперь понятие двоичных файлов меня сбивает с толку. Все дело в «Кодексе машинного языка» & «ЦП». Где ОС вступает в игру? Я имею в виду, что скомпилированный двоичный файл имеет прямые инструкции для CPU, когда он загружается в память. & Процессор выполняет одну инструкцию за раз. Я не мог видеть роль операционной системы в любом месте, кроме управления процессами link text. Он должен работать на ЦПУ той же ISA независимо от операционной системы. правильно?

Однако это не тот случай. Если я создам код для x86 на машине Windows. Он не будет работать на машине Mac x86 или машине Linux x86.

Мне что-то не хватает. Пожалуйста, очистите мое замешательство.

ответ

13

Для начала современный процессор имеет (по крайней мере) два режима, в котором он запускает ядро ​​самой операционной системы («режим ядра») и режим, в котором он запускает программы («пользовательский режим»)). Когда в пользовательском режиме CPU не может делать много чего.

Например, щелчок мыши обычно замечается в ядре, а не в пользовательском режиме. Однако ОС отправляет событие в пользовательский режим, а оттуда - в правильную программу. Другой подход также требует сотрудничества: программа не может свободно рисовать на экране, но ей необходимо пройти через OS и режим ядра, чтобы использовать ее часть.

Аналогичным образом, запуск программы обычно является сотрудничеством. Часть оболочки операционной системы также является программой пользовательского режима. Он получает щелчок мыши и определяет, что это щелчок мыши, предназначенный для запуска процесса. Затем оболочка сообщает операционной системе ядра о запуске нового процесса для этой программы.

Когда в режиме ядра необходимо запустить новый процесс, он сначала выделяет память для бухгалтерии, а затем переходит к загрузке программы. Это включает в себя извлечение инструкций из двоичного файла, а также подключение программы к ОС. Обычно это требует поиска точки входа (классически int main(int argc, char** argv)) двоичного кода и всех точек, где программа хочет вызвать ОС.

Различные операционные системы используют разные способы подключения программ к ОС. В результате процесс загрузки отличается, и форматы файлов для двоичных файлов могут также отличаться. Это не абсолютное; формат ELF для двоичных файлов используется для ряда операционных систем, а Microsoft использует свой формат PE во всех своих текущих операционных системах. В обоих случаях формат описывает точный формат двоичного файла, поэтому ОС может решить, можно ли подключить программу к ОС. Например, если это двоичный файл Win32, он будет в формате PE, поэтому Linux не будет загружать это, Windows 2000 будет, как и Windows 7-64. С другой стороны, двоичный файл Win64 также находится в формате PE, но Windows 2000 отвергает его.

+0

Re "* поэтому Linux не будет загружать это *", но что мешает Linux-программу иметь возможность запускать это? – Pacerier

+0

Действительно, что мешает Mac реализовать реализатора PE, чтобы все приложения Windows могли запускаться на Mac из коробки? – Pacerier

+0

@Pacifier: Деньги, в основном. Тем не менее, Microsoft _did_ реализует подсистему ELF (подсистема Windows для Linux). – MSalters

0

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

Например, чтобы создать окно в Microsoft Windows, вам понадобится DLL для создания окна.

Если вы хотите написать API самостоятельно, вы будете использовать API, который предоставляет ОС. Именно здесь ОС вступает в игру.

+1

На высоком уровне это правильно. Однако вы не можете «написать API самостоятельно», так как ОС не позволяет напрямую обращаться к аппаратной или табличной таблице. Таким образом, на каком-то уровне вам все равно придется создавать системные вызовы, специфичные для ОС. –

7

Два способа:

В первую очередь ответ «системные вызовы». Всякий раз, когда вы вызываете функцию, которая должна выполнять любые операции ввода-вывода, взаимодействовать с устройствами, выделять память, процессы вилки и т. Д., Эта функция должна выполнять «системный вызов». Хотя сама команда syscall является частью X86, доступные системные вызовы и параметры для них зависят от ОС.

Даже если ваша программа не делает никаких системных вызовов (что я не уверен, возможно, и, конечно, не очень полезно), форматы, которые обтекают машинный код, различны для разных ОС. Таким образом, форматы файлов exe (PE) и исполняемого файла linux (обычно ELF) различаются, поэтому exe-файл не будет выполняться в Linux.

EDIT: это детали низкого уровня. Ответ на более высокий уровень заключается в том, что все, что нужно для доступа к файлам, консоли/графическому интерфейсу, распределению памяти и т. Д., Зависит от ОС.

+2

Итак, 1. Компилятор при компиляции HLL в ML изменяет функцию fopen() или acess printer funcion на конкретный «системный вызов» операционной системы, который отличается для разных ОС. правильно? 2. не только компилирует HLL на «CPU ISA» и «системный вызов ОС», а также выполняет работу этого файла с преобразованием PE/ELF в зависимости от ОС. правильно? – claws

+2

Нет, он по-прежнему вызывает fopen(). Где-то в fopen есть инструкция «syscall». Команда syscall изменяет процессор на «режим ядра», который удаляет все виды защиты и позволяет системе фактически получить доступ к оборудованию. Ваша программа работает в защищенном режиме и вообще не может получить доступ к оборудованию. –

+1

> Хотя сама команда syscall является частью X86, доступные системные вызовы и параметры для них зависят от ОС. Где я могу их найти? Я просто хочу взглянуть на различные системные вызовы разных ОС для одной и той же функции, скажем, «Открытие файла». Я работаю в поисковых системах, но не могу найти то, что я точно ищу. – claws

3

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

Под GNU/Linux, например, вы должны заполнить регистры и вызвать int 80h для доступа к «службе» (фактически называемой «syscall»).

Ваша программа не будет работать на другой ОС также потому, что для исполняемых файлов существуют разные форматы файлов, например Win имеет COFF/PE, Linux имеет формат файла ELF (как и любой другой формат файла, это также содержит мета данные ", например формат файла HTML (или SGML)).

+3

NB: Эта «служба» - это своего рода функция низкого уровня, доступная в режиме ядра, и ее нельзя путать с «службой Windows» (также как демон на * nix OS). –

1

ОС обеспечивает (а) среду, в которой работает ваш машинный код, и (б) стандартные службы.Без (a) ваш код никогда не сможет выполнить в первую очередь, и без (b) вам придется реализовать абсолютно все самостоятельно и напрямую использовать оборудование.

+0

А так почему бы не использовать код непосредственно на оборудовании?** Тогда это будет работать через ОС? ** – Pacerier

9

Он не будет работать на других процессорах, поскольку 01010110011 означает что-то на x86 и что-то еще на ARM. x86-64 оказывается обратно совместимым с x86, поэтому он может запускать программы x86.

Двоичный в определенном формате, что ОС понятном (Windows = PE, Mac/Linux = ELF)

При любом нормальном двоичном, ваши ОС загружает его в память и заполняет несколько полей с определенными значениями , Эти «определенные значения» являются адресами для функций api, которые существуют в общих библиотеках (dll, so), таких как kernel32 или libc. Адреса API необходимы, поскольку сам двоичный файл не знает, как получить доступ к жестким дискам, сетевым картам, геймпадам и т. Д. Программа использует эти адреса для вызова определенных функций, существующих в вашей ОС или в других библиотеках.

В сущности, в двоичном коде отсутствуют некоторые важные части, которые необходимо заполнить ОС, чтобы все работало. Если ОС заполняет неправильные части, двоичный файл не будет работать, поскольку они не могут общаться друг с другом. Это произойдет, если вы замените файл user32.dll другим файлом или попытаетесь запустить исполняемый файл linux на mac osx.

Так как же libc знает, как открыть файл?

libc использует системные вызовы, которые являются низкоуровневым доступом к основным функциям ОС.Это похоже на вызов функции, за исключением того, что вы делаете это, заполняя некоторые регистры процессора, а затем запуская прерывание (специальная инструкция CPU)

Так как же ОС знает, как открывать файлы?

Это одна из вещей, которые делает ОС. Но как он знает, как разговаривать с жестким диском? Я не знаю точно, как работает этот материал, но я полагаю, что ОС делает это, записывая/считывая определенные ячейки памяти, которые отображаются в BIOS.

Итак, как BIOS знает, как разговаривать с жестким диском?

Я тоже этого не знаю, я никогда не программировал на этом уровне. Я полагаю, что BIOS жестко подключен к разъемам жесткого диска и способен отправить правильную последовательность 1 и 0, чтобы поговорить «SATA» с жестким диском. Вероятно, можно сказать только простые вещи, такие как «читать этот сектор»

Так как же жесткий диск знает, как читать сектор?

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

+1

Отличный пост, спасибо. Одна незначительная нота: я думаю, что OS X использует MACH-O вместо ELF: http://en.wikipedia.org/wiki/Mach-O Не уверен в этом, поскольку я новичок в этой теме. – Stephen

+0

@Martin, Re "* если вы заменили user32.dll другим файлом *", но вопрос в том, почему программа не будет работать, если у вас есть функции «user32.dll», встроенные ** в ** вашу программу ? – Pacerier

+0

@Pacerier 'user32.dll' в свою очередь зависит от других DLL, поэтому вам нужно будет внедрить их в свою программу. Если вы это сделаете, вы получите «статически связанный» исполняемый файл, который, я уверен, будет отклонен загрузчиком в Windows. Вы можете запустить статически связанный исполняемый файл в Linux, но он неизбежно будет содержать системные вызовы, чтобы делать что-нибудь полезное (через инструкцию syscall на x86), который по сути является вызовом функции в режиме ядра. API-интерфейс ядра должен вести себя так, как ожидалось (например, реализовать интерфейс ядра Linux), чтобы исполняемый файл мог корректно вести себя. – Martin

0

Также я хочу добавить, что ОС управляет запуском программы. Он готовит пространство процесса и инициализирует его, чтобы программа могла начать, загружает инструкции программы и дает управление программе.

1

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

Кроме того, это будет целесообразно для архитектуры набора заданных инструкций с несколькими исключениями (например, следует соблюдать осторожность относительно допущений относительно размеров указателей, примитивных типов, структурных макетов, реализаций классов на C++ и т. Д.).

Формат файла будет определять необходимые функции/данные, доступные для просмотра/просмотра, чтобы операционная система выполняла ваш код как процесс и загружала процесс в требуемое состояние. Если вы знакомы с разработкой для C/C++ под Windows, концепция подсистемы определяет уровень начальной загрузки, предоставленные ресурсы и подпись точки входа (обычно main(int, char **) для большинства систем).

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

Сборочные языки должны кодировать конкретной ISA. Они используют инструкции, характерные для семейства типов процессоров. Эти инструкции могут работать в других семействах процессоров, , если эти процессоры поддерживают данный набор команд. Например, x86-код будет работать в определенной степени, в операционной системе amd64 и определенно работать на процессоре amd64, работающем под управлением операционной системы x86.

C абстрагирует большую часть специфики ISA. Несколько очевидных исключений включают в себя размеры указателя и контенту. Различные известные интерфейсы будут предоставлены на ожидаемый уровень через libc, например printf, main, fopen и другие. К ним относятся ожидаемые регистры и состояния стека для выполнения этих вызовов, позволяя C-коду работать в разных операционных системах и архитектурах без изменений.Другие интерфейсы могут быть предоставлены либо напрямую, либо путем привязки платформы к ожидаемому интерфейсу, чтобы увеличить переносимость кода C.

Python и другие аналогичные «виртуализированные» языки работают на еще одном уровне абстракции и снова за некоторыми исключениями, например, функции, которые не существуют на определенных платформах или различия в кодировке символов, могут выполняться без изменений на многочисленные системы. Это достигается за счет обеспечения единого интерфейса для различных комбинаций ISA и операционной системы за счет производительности и размера исполняемого файла.

0

аналогия:

Допустим, вы нанимаете дворецкий из другой страны. Он не понимает ни слова, которое вы говорите, поэтому вы получаете устройство, напоминающее звездообразный переводчик. Теперь он может понять ваш язык высокого уровня, потому что, когда вы говорите, он слышит свой (довольно грубый) язык.

Теперь предположим, что вы хотите, чтобы он шел от А до Б. Вы бы не говорили прямо с его ногами или ногами, вы бы попросили его на его лице! Он контролирует свое тело. Если 1) вы надлежащим образом передаете ваш запрос и 2) он решает, что он подпадает под его служебные обязанности, он перейдет от А к Б.

Теперь вы получаете нового слугу из той же страны, что и последняя (потому что вы бы предпочли не покупать новый трансляционный переводчик). Вы хотите, чтобы он тоже шел от А до Б. Но этот слуга требует, чтобы вы говорили громче и говорите, пожалуйста, спрашивая. Вы мириться с этим, потому что он более гибкий: вы можете попросить его перейти от A к B через C, если хотите - предыдущий дворецкий мог это сделать, но тащил ноги и жаловался.

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

В случае, если не ясно, дворецкие - компьютеры с той же ISA, но с разными операционными системами. Переводчик - это инструмент для перекрестного компилятора, ориентированный на их ISA.

+0

Re "потому что вы предпочтете не покупать новый« звезда-трек-переводчик », на что ссылается переводчик? – Pacerier

+0

Также я думаю, что полезно расширить эту аналогию с тем, что происходит, когда у вас тоже есть другая ISA? – Pacerier

+0

@Pacerier Переводчик был бы кросс-компилятором, ориентированным на их ISA. Дело в том, что, хотя переводчик создает x86 или какой-либо другой машинный язык, вам нужно по-разному формулировать свои инструкции, потому что ядра имеют свои собственные интерфейсы. Это означает ссылку на разные библиотеки и использование любого бинарного формата, который они используют. Пользовательские программы не запускаются сами, вам нужно поговорить с ядрами/дворецками, чтобы все было сделано. – jiggunjer

Смежные вопросы