Как сборка используется в наши дни (например, с C / C ++)? - PullRequest
13 голосов
/ 04 октября 2010

Я понимаю, как компьютер работает на основных принципах, таких как, программа может быть написана на языке "высокого" уровня, таком как C #, C, и затем она разбивается на объектный код, а затем двоичный файл для понимания процессором.,Тем не менее, я действительно хочу узнать о сборке и о том, как она используется в современных приложениях.

Я знаю, что процессоры имеют разные наборы команд над базовым набором команд x86.Все ли языки ассемблера поддерживают все наборы команд?

Сколько существует языков ассемблера?Сколько из них хорошо работают с другими языками?

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

Как кто-то мог бы ссылаться на функции / подпрограммы в этом ассемблерном коде из языка, подобного C или C ++?

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

Существуют ли какие-либо рекомендуемые книги по языкам ассемблера / использующие их в современных программах?

Извините за количество вопросов, я надеюсь, что они достаточно общие, чтобы быть полезными для других людей, посколькудостаточно просто, чтобы другие могли ответить!

Ответы [ 6 ]

16 голосов
/ 04 октября 2010
Однако я действительно хочу узнать о сборке и о том, как она используется в современных приложениях.

На «нормальных» ПК, которые используются только для обработки, критичной ко времени, я бы сказал, что обработка мультимедиа в реальном времени все еще может принести немалую пользу от ручной сборки. На встраиваемых системах, где намного меньше лошадиных сил, у него может быть больше областей применения.

Однако, имейте в виду, что это не просто «эй, этот код медленный, я перепишу его в сборке, и он по волшебству будет работать быстро»: это должна быть тщательно написанная сборка, написанная зная что это быстро и что медленно в вашей конкретной архитектуре и с учетом всех тонкостей современных процессоров (ошибочные прогнозы ветвлений, неупорядоченные исполнения, ...). Зачастую сборка, написанная программистом сборки от новичка до носителя, будет на медленнее , чем конечный машинный код, сгенерированный хорошим современным оптимизирующим компилятором. Производительность в x86 часто очень сложна, и ее следует оставить людям, которые знают, что они делают =>, и большинство из них являются авторами компиляторов. :) Взгляните на это , например.

Я знаю, что процессоры имеют разные наборы команд над базовым набором команд x86. Все языки ассемблера поддерживают все наборы инструкций?

Я думаю, что вы здесь что-то путаете. Многие (= все современные) x86 процессоры поддерживают дополнительные инструкции и наборы инструкций, которые были введены после определения исходного набора x86 инструкций. На самом деле, почти все программное обеспечение для x86 в настоящее время компилируется для использования возможностей Post-Pentium; Вы можете запросить процессор, чтобы узнать, поддерживает ли он некоторые функции, используя инструкцию CPUID . Очевидно, что если вы хотите использовать мнемонику для какой-то более новой инструкции набора команд, ваш ассемблер (то есть программное обеспечение, которое переводит мнемонику в фактический машинный код) должен знать о них.

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

Сколько существует языков ассемблера?

Теоретически, по крайней мере один диалект для каждого семейства процессоров. Имейте в виду, что для одного и того же языка ассемблера существуют разные обозначения; например, следующие две инструкции - это то же самое, что написано в x86, написанное в AT & T и Intel:

mov $4, %eax          // AT&T notation

mov eax, 4            // Intel notation
Как можно было бы написать подпрограмму в ассемблере и затем скомпилировать ее в объектный / двоичный код?

Если вы хотите встроить подпрограмму в приложение, написанное на другом языке, вам следует использовать инструменты, предоставляемые этим языком, в C / C ++ вы бы использовали блоки asm.

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

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

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

Добавление

Я забыл: умение читать ассемблер, по крайней мере, немного, может быть очень полезным для отладки странных проблем, которые могут возникнуть, когда оптимизатор сломан / только в сборке выпуска / у вас естьиметь дело с heisenbugs / когда недоступна отладка на уровне исходного кода или другие подобные вещи;посмотрите комментарии здесь .

7 голосов
/ 05 октября 2010

Intel и x86 отлично справляются с обратной совместимостью, что, безусловно, помогло им, но в то же время очень больно. Внутренние устройства от 8088/8086 до 286–386, до 486, Pentium, Pentium Pro и т. Д. До настоящего времени являются чем-то вроде редизайна каждый раз. Вначале добавление механизмов защиты для операционных систем для защиты приложений друг от друга и от ядра, а затем - от повышения производительности путем добавления исполнительных модулей, суперскалярных и всего, что с ними поставляется, многоядерных процессоров и т. Д. То, что раньше было настоящим единым AX Регистрация в оригинальном процессоре превращается в то, кто знает, сколько разных вещей в современном процессоре. Первоначально ваша программа выполнялась в том порядке, в котором она написана, сегодня она нарезается кубиками, разрезается и выполняется параллельно таким образом, что намерения представленных инструкций выполняются, но выполнение может быть не в порядке и параллельно. Множество новых трюков скрыто за тем, что на поверхности кажется очень старым набором инструкций.

Набор команд изменился с 8/16-битных корней на 32-битные, на 64-битные, поэтому язык ассемблера также пришлось менять. Например, добавление EAX в AX, AH и AL. Иногда были добавлены другие инструкции. Но исходные инструкции загрузки, хранения, сложения, вычитания, и / или и т. Д. Все есть. Я долгое время не делал x86 и был шокирован, увидев, что синтаксис изменился и / или конкретный ассемблер испортил синтаксис x86. Существуют миллионы инструментов, поэтому, если один из них не соответствует используемой вами книге или веб-странице, найдется один из них.

Таким образом, если рассматривать язык ассемблера для этого семейства правильно и неправильно, язык ассемблера, возможно, изменил синтаксис и не обязательно является обратно совместимым, но набор команд или машинный язык или другие подобные термины (коды операций / биты сборки представляет) сказал бы, что большая часть исходного набора инструкций все еще поддерживается на современных процессорах x86. Возможно, определенные нюансы не будут работать, как и в случае с другими новыми функциями определенных поколений, но основные инструкции: загрузить, сохранить, сложить, вычесть, нажать, выбросить и т. Д. Все еще работают и будут работать. Я чувствую, что лучше "проехать по центру полосы движения", не разбираться в особенностях ghee whizz для микросхем или инструментов, использовать базовую скучность, работал с начала временного синтаксиса языка.

Поскольку каждое поколение в семье пытается использовать определенные функции, обычно производительность, способ передачи отдельных инструкций различным исполнительным блокам меняется ... в каждом поколении ... Чтобы вручную настроить ассемблер на производительность, попытка превзойти компилятор, в лучшем случае может быть трудной. Вам нужны подробные знания о конкретном процессоре, для которого вы настраиваете. К сожалению, с первых дней x86 до настоящего времени то, что заставляло код выполняться быстрее на одном чипе, часто заставляло следующее поколение работать слишком медленно. Возможно, это был замаскированный маркетинговый инструмент, не уверенный: «Купите новый горячий процессор, который стоит вдвое дороже того, который у вас есть сейчас, рекламирует вдвое большую тактовую частоту, но при этом ваша копия Windows работает на 30% медленнее. Через несколько лет, когда будет скомпилирована следующая версия Windows (а этот чип устарел), производительность будет удвоена ». Другим побочным эффектом этого является то, что в данный момент вы не можете взять одну программу на C и создать один двоичный файл, который быстро работает на всех процессорах x86, для производительности вам нужно настроить конкретный процессор, то есть вам нужно как минимум сообщить компилятору оптимизировать и для какой семьи оптимизировать. И, например, окна или офис, или что-то, что вы распространяете в виде двоичного файла, вы, вероятно, не можете или не хотите как-то хоронить несколько по-разному настроенных копий одной и той же программы в одном пакете или в одном двоичном файле ... двигаясь по центру дороги.

В результате всех улучшений аппаратного обеспечения в ваших интересах не пытаться настраивать выходные данные компилятора или ручного ассемблера на какой-либо конкретный чип.В среднем улучшения оборудования компенсируют отсутствие настройки компилятора, и, надеюсь, одна и та же программа будет работать чуть быстрее с каждым поколением.Один из поставщиков микросхем имел обыкновение стремиться к тому, чтобы завтрашние популярные скомпилированные двоичные файлы работали быстрее завтра, другой поставщик улучшил внутренние компоненты так, что если вы перекомпилируете сегодняшний исходный код для новых внутренних компонентов, вы сможете работать быстрее завтра.Эти действия между поставщиками не обязательно продолжаются, каждое поколение выполняет двоичные файлы медленнее, но завтра перекомпилируется исходный код с той же скоростью или медленнее.Он будет запускать переписанные программы завтрашнего дня быстрее, иногда с тем же компилятором, иногда вам нужен компилятор завтрашнего дня.Разве это не весело!

Итак, как мы узнаем, что конкретная скомпилированная или собранная вручную программа настолько быстра, насколько это возможно?На самом деле, для x86 вы не можете гарантировать, что это не так, запускать его на одном чипе в семействе, и он медленный, запускать на другом - он может быть быстрым.x86 или нет, кроме очень коротких программ или очень детерминированных программ, которые вы можете найти на микроконтроллере, вы не можете определенно сказать, что это самое быстрое из возможных решений.Например, кеширование очень сложно, если это вообще возможно, и память за ним, особенно на ПК, где пользователь может выбирать различные размеры, скорости, ранги, банки и т. Д. И настраивать параметры BIOS, чтобы изменить еще больше настроек.действительно не могу сказать компилятору настроиться на это.Таким образом, даже на одном и том же компьютере, одном и том же процессоре, в том же скомпилированном бинарном файле вы можете повернуть некоторые из этих ручек и заставить эту программу работать намного быстрее или намного медленнее.Менять семейства процессоров, менять наборы микросхем, материнские платы и т. Д. И нет возможности настроить так много переменных.Природа бизнеса на базе x86 стала слишком хаотичной.

Другие семейства чипов не так проблематичны.Некоторые, возможно, но не все.Так что это не общие утверждения, а специфичные для семейства чипов x86.Семейство x86 является исключением, а не правилом.Вероятно, последний набор ассемблера / инструкций, который вы хотели бы потратить на изучение.

Есть множество веб-сайтов и книг по этому предмету, не могу сказать, что один лучше, чем другой.Я узнал из первоначального набора книг 8088/86 от Intel, а затем из книг 386 и 486, после этого не искал книг Intel (или каких-либо других издевательств).Вам понадобится ссылка на набор инструкций и ассемблер, такой как nasm или gas (gnu ассемблер, часть binutils, которая поставляется с большинством наборов инструментов компилятора на основе gcc).Что касается интерфейса ассемблера C to / from, вы можете, если ничего не выяснить, экспериментировать, написать небольшую программу на C с несколькими небольшими функциями C, разобрать или скомпилировать в ассемблер и посмотреть, какие регистры и / или как работает стек.используется для передачи параметров между функциями.Сохраняйте свои функции простыми и используйте только несколько параметров, и ваш ассемблер, вероятно, будет работать нормально.Если нет, посмотрите на ассемблер функции, вызывающей ваш код, и выясните, где находятся ваши параметры.Это все где-то хорошо документировано, и в наши дни, вероятно, намного лучше, чем старые.В начале 8088/86 дней у вас были крошечные, маленькие, средние, большие и огромные модели компиляторов, и соглашения о вызовах могли отличаться от одного к другому.Как и один компилятор для следующего, watcom (ранее zortech и, возможно, другие имена) передавался по регистру, borland и microsoft передавались в стек и довольно близко, если не совпадали.Теперь с 32- и 64-разрядным плоским пространством памяти и стандартами вы можете использовать одну модель и не нужно запоминать все нюансы (только один набор нюансов).Встроенная сборка возможна, но варьируется от компилятора C к компилятору C, и заставить ее работать правильно и эффективно сложнее, чем просто писать ассемблер в своем собственном файле.gcc и, возможно, другие компиляторы позволят вам поместить файл ассемблера в командную строку компилятора C, как если бы это был просто другой файл C, и он выяснит, что вы ему дали, и передаст его ассемблеру для вас.То есть, если вы не хотите вызывать программу на ассемблере самостоятельно и помещать объект в командную строку компилятора C.

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

Не все компиляторы для всех процессоров хороши.Например, Gcc - это один размер, который подходит всем, как носок или бейсболка, который не подходит никому.Очень хорошо подходит для большинства целей, но не очень хорошо.Так что вполне возможно добиться большего успеха, чем компилятор с вручную настроенным ассемблером, но в среднем для большого количества кода вы не выиграете.Это относится к большинству процессоров, которые являются более детерминированными, а не только к семейству x86.Речь идет не о меньшем количестве инструкций, меньшее количество инструкций не обязательно означает более быстрое, чтобы превзойти даже средний компилятор в долгосрочной перспективе, вы должны понимать кеширование, выборку, декодирование, конечные автоматы выполнения, интерфейсы памяти, сами памяти и т. Д.Оптимизация компилятора отключена, поэтому очень легко создавать более быстрый код, чем компилятор, поэтому вы должны просто использовать оптимизатор, но также понимать, что это увеличивает риск ошибки компилятора.Вам нужно очень хорошо знать инструмент, который часто возвращается к разборке, чтобы понять, как ваш код C и используемый вами сегодня компилятор взаимодействуют друг с другом.Ни один компилятор не является полностью совместимым со стандартами, потому что сами стандарты нечетки, оставляя некоторые функции языка на усмотрение компилятора (езжайте по середине пути и не используйте эти части языка).

Подводя итог сути ваших вопросов, я бы порекомендовал написать кучу небольших функций или программ с некоторыми небольшими функциями, компилировать в ассемблер или компилировать в объект и дизассемблировать, чтобы увидеть, что делает компилятор. Обязательно используйте разные настройки оптимизации для каждой программы. Получите практические знания по чтению набора инструкций (если выходные данные компилятора или дизассемблера предоставлены в ассемблере, имеет много лишних ошибок, которые мешают удобочитаемости, вам нужно забыть об этом, вам практически не нужно ничего, если вы хотите написать ассемблер). Дайте себе 5-20 лет обучения и экспериментов, прежде чем вы сможете ожидать, что будете регулярно превосходить компилятор, если это ваша цель. К тому времени вы узнаете, что, особенно с этим семейством чипов, это бесполезное усилие, вы выигрываете несколько, но в основном проигрываете ... Было бы в вашу пользу скомпилировать (ассемблировать) тот же код для других семейств чипов, таких как arm и mips, и получить общее представление о том, что код C в целом хорошо компилируется, и что код C не компилируется хорошо, и делает ваше программирование на C лучше, чем пытается сделать ассемблер лучше. Также попробуйте другие компиляторы, такие как llvm. У Gcc есть много изюминок, которые многие считают стандартами языка Си, но вместо этого представляют собой нюансы или проблемы с конкретным компилятором. Умение читать и анализировать результаты сборки компиляторов и их опций предоставит эти знания. Поэтому я рекомендую вам поработать над чтением набора инструкций без необходимости учиться писать его с нуля.

3 голосов
/ 04 октября 2010

Вы должны смотреть на это с точки зрения аппаратного обеспечения, язык ассемблера создается с учетом возможностей процессора.Каждый раз, когда в CPU создается новая функция, создается соответствующая инструкция по сборке, чтобы ее можно было использовать.

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

РЕДАКТИРОВАТЬ:

Сколько существует языков ассемблера?Сколько из них хорошо работают с другими языками?

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

Как кто-то может написать программу в сборке, а затем скомпилировать ее в объектный / двоичный код?`

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

Каким образом кто-то может ссылаться на функции / подпрограммы в этом коде ассемблера с языка, подобного C или C ++?

C ++ и Cобеспечьте встроенную сборку, чтобы не нужно было связывать, но если вы хотите связать, вам нужно создать объект сборки, следуя тем же / подобным соглашениям о вызовах, что и основной язык.Например, некоторые языки при вызове функции передают аргументы функции в стеке в определенном порядке, поэтому вам придется делать то же самое.

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

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

1 голос
/ 04 октября 2010

Существует много разных языков ассемблера.Обычно для каждого набора команд процессора есть по крайней мере одна, то есть одна для каждого типа процессора.Следует также иметь в виду, что даже для одного процессора может быть несколько разных программ на ассемблере, которые могут использовать другой синтаксис, который с формальной точки зрения представляет собой другой язык.(для x86 есть masm, nasm, yasm, AT & T (какие * nix-ассемблеры, такие как GNU-ассемблер используют по умолчанию) и, возможно, многие другие)

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

Существуют и другие процессоры с совершенно другими инструкциями.

Чтобы изучить сборку, вам нужно начать с выбора целипроцессор и ассемблер, который вы хотите использовать.Я предполагаю, что вы собираетесь использовать x86, поэтому вам нужно будет решить, хотите ли вы начать с 16-битной сегментации, 32-битной или 64-битной.Многие книги и онлайн-учебники идут по 16-битному маршруту, где вы пишете программы для DOS.Если вы хотите писать части программ на C в ассемблере, то, возможно, вы захотите пойти по 32 или 64-битному маршруту.

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

Во многих местах люди все еще пишут на ассемблере.Это особенно часто встречается во встроенных, начальных загрузчиках (bios, u-boot, ...) и коде операционной системы, хотя многие разработчики в них никогда не пишут напрямую какую-либо сборку.Этот код может быть кодом запуска, который должен выполняться до того, как указатель стека будет установлен в полезное значение (или ОЗУ еще не используется по какой-либо другой причине), потому что они должны помещаться в небольшие пробелы и / или потому, что им нужноговорить с оборудованием способами, которые напрямую не поддерживаются в C или других языках более высокого уровня.Другие места, где сборка используется в ОС, - это блокировка записи (спин-блокировки, критические секции, мьютексы и семафоры) и переключение контекста (переключение с одного потока выполнения на другой).

Другие места, где обычно пишется сборка,в реализации некоторого библиотечного кода.Такие функции, как strcpy, часто реализуются в сборке для разных архитектур, поскольку зачастую существует несколько способов их оптимизации с помощью операций, специфичных для процессора, в то время как реализация C может использовать более общий цикл.Эти функции также используются повторно так часто, что их оптимизация вручную часто стоит усилий в долгосрочной перспективе.

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

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

0 голосов
/ 04 октября 2010

Я знаю, что процессоры имеют разные наборы команд над базовым набором команд x86.Все ли языки ассемблера поддерживают все наборы инструкций?

«Язык ассемблера» - это своего рода неправильное выражение, по крайней мере в том смысле, в котором вы его используете.Ассемблеры - это не язык (выпускники CS могут возразить), а скорее инструмент конвертера, который берет текстовое представление и генерирует из него двоичное изображение с близким соотношением 1: 1 между текстовыми элементами (мемноникой, метками и числами) и двоичнымэлементы.В элементах языка ассемблера нет более глубокой логики, потому что их возможности для цитирования и перенаправления заканчиваются в основном на уровне 1;Вы можете, например, использовать EAX только в одной инструкции за раз - следующее использование EAX в следующей инструкции не имеет отношения к его предыдущему использованию, КРОМЕ для неписаного логического соединения, которое имел в виду программист - это причина, почемуэто так легко создавать ошибки в ассемблере.

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

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

Как тогда кто-то может сослаться нафункции / подпрограммы в этом ассемблерном коде от языка, подобного C или C ++?

Вы должны объявить их в своей HLL - см. руководство по компиляторам.

Как мызнаете, код, который мы написали на ассемблере, самый быстрый, который может быть?

Нет способа узнать.Будьте счастливы от этого и используйте код.

0 голосов
/ 04 октября 2010

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

Сегодня я склонен видеть вставки ассемблера в коде языка более высокого уровня. Как именно это делается, зависит от вашего языка, а иногда и от компилятора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...