Как Ассамблея используется в современный день (например, с C/С++)?
Я понимаю, как компьютер работает на основных принципах, например, программа может быть написана на языке с высоким уровнем, например С#, C, а затем разбита на объектный код, а затем двоичная для процессора, чтобы понять, Тем не менее, я действительно хочу узнать о сборке и о том, как она используется в современных приложениях.
Я знаю, что процессоры имеют разные наборы команд над базовым набором команд x86. Поддерживают ли все языки ассемблера все наборы инструкций?
Сколько ассемблерных языков существует? Сколько из них хорошо работает с другими языками?
Как кто-то собирается писать процедуру в сборке, а затем компилировать ее в объект/двоичный код?
Как кто-то тогда ссылается на функции/подпрограммы внутри этого кода сборки с языка, такого как C или С++?
Как мы узнаем, что код, который мы написали в сборке, является самым быстрым из возможных?
Существуют ли какие-либо рекомендуемые книги по языкам ассемблера/их использование с помощью современных программ?
Извините за количество вопросов, я надеюсь, что они достаточно общие, чтобы быть полезными для других людей, а также достаточно просты, чтобы другие могли ответить!
Ответы
Ответ 1
Тем не менее, я действительно хочу узнать о сборке и о том, как она используется в современных приложениях.
На "нормальных" ПК он использовался только для критически важной обработки, я бы сказал, что обработка мультимедиа в реальном времени может по-прежнему немного выиграть от ручной сборки. На встроенных системах, где есть намного меньше мощности, он может иметь больше областей использования.
Однако имейте в виду, что это не просто "эй, этот код медленный, я переписал его в сборке, и по волшебству он пойдет быстро": он должен быть тщательно написанным собранием, написанным, зная, что мешает вашей конкретной архитектуре, и с учетом всех тонкостей современных процессоров (отраслевые неверные предсказания, нестандартные исполнения,...). Часто сборка, написанная программистом для начинающих и средних, будет медленнее, чем конечный машинный код, созданный хорошим современным оптимизирующим компилятором. Производительность на x86 часто очень сложна, и ее следует оставлять людям, которые знают, что они делают = > , и большинство из них являются компиляторами.:) Посмотрите это, например.
Я знаю, что процессоры имеют разные наборы команд над базовым набором команд x86. Поддерживают ли все языки ассемблера все наборы инструкций?
Я думаю, вы что-то путаете здесь. Многие (= все современные) процессоры x86
поддерживают дополнительные инструкции и наборы инструкций, которые были введены после того, как был определен набор команд x86
. Фактически, почти все программное обеспечение x86 теперь скомпилировано для использования функций пост-Pentium; вы можете запросить процессор, чтобы узнать, поддерживает ли он некоторые функции с помощью инструкции CPUID. Очевидно, что если вы хотите использовать мнемонику для какой-либо новой инструкции набора инструкций, ваш ассемблер (т.е. Программное обеспечение, которое переводит мнемонику в фактический машинный код), должен знать о них.
Если вместо этого вы говорите о других наборах инструкций для других семейств процессоров (не x86), каждый ассемблер должен поддерживать инструкции, которые может выполнять целевой процессор. Не все инструкции языка ассемблера имеют прямую замену в других, и в целом перенос ассемблерного кода из архитектуры в другую обычно является трудной и трудной работой.
Сколько языков ассемблера существует?
Теоретически, по крайней мере один диалект для каждого семейства процессоров. Имейте в виду, что существуют разные обозначения для одного и того же языка ассемблера; например, следующие две инструкции - это те же элементы x86, которые записаны в AT & T и нотации Intel:
mov $4, %eax // AT&T notation
mov eax, 4 // Intel notation
Как кто-то собирается писать процедуру в сборке, а затем компилировать ее в объект/двоичный код?
Если вы хотите встроить процедуру в приложение, написанное на другом языке, вы должны использовать инструменты, которые предоставляет вам язык, в C/С++ вы должны использовать блоки asm
.
Если вы хотели написать целое приложение в сборке, вам придется писать только в сборке, следуя синтаксическим правилам ассемблера, который вы хотели бы использовать.
Как мы узнаем, что код, который мы написали в сборке, является самым быстрым, возможно, это возможно?
В теории, поскольку он является самым близким к голым металлам, поэтому вы можете заставить машину делать именно то, что вы хотите, без компилятора учитывать особенности языка, которые в каком-то конкретном случае не имеют значения. На практике, поскольку машина часто намного сложнее, чем язык ассемблера, поскольку, как я сказал, часто ассемблерный язык будет медленнее, чем машинный код, генерируемый компилятором, который учитывает многие тонкости, которые средний программист не знает.
Добавление
Я забыл: знать, что читать сборку, по крайней мере немного, может быть очень полезно при отладке странных проблем, которые могут возникнуть, когда оптимизатор сломан/только в сборке релизов/вам нужно иметь дело с heisenbugs/when отладка на исходном уровне недоступна или другие подобные вещи; взгляните на комментарии здесь.
Ответ 2
Intel и x86 имеют большую обратную совместимость, что, безусловно, помогло им, но в то же время сильно пострадало. Внутренности 8088/8086 до 286 до 386, до 486, pentium, pentium pro и т.д. До настоящего времени - это несколько редизайн каждый раз. В начале добавления механизмов защиты для операционных систем для защиты приложений друг от друга и ядра, а затем в производительности путем добавления исполнительных блоков, суперскалярных и все, что с ним связано, многоядерных процессоров и т.д. То, что раньше было реальным, одним AX Регистрация в исходном процессоре превращается в того, кто знает, сколько разных вещей в современном процессоре. Первоначально ваша программа была выполнена в порядке написания, сегодня она нарезана кубиками и нарезана и выполняется параллельно таким образом, что намерение представленных инструкций выполнено, но выполнение может быть не в порядке и параллельно. Много и много новых трюков, зарытых за то, что на поверхности кажется очень старым набором инструкций.
Набор команд изменился с корней 8/16 бит на 32 бит, до 64 бит, поэтому язык ассемблера также должен был измениться. Например, добавление EAX в AX, AH и AL. Иногда добавлялись другие инструкции. Но оригинальная загрузка, сохранение, добавление, вычитание и, или, и т.д. Инструкции, есть. Я не делал x86 в течение длительного времени и был потрясен, увидев, что синтаксис изменился, и/или конкретный ассемблер испортил синтаксис x86. Там есть инструменты zillion, поэтому, если вы не согласны с книгой или веб-страницей, которую вы используете, есть один, который будет там.
Таким образом, мышление в терминах языка ассемблера для этого семейства является правильным и неправильным, язык ассемблера мог бы изменить синтаксис и не обязательно иметь обратную совместимость, но набор команд или машинный язык или другие подобные термины (коды операций/бит сборки представляет) скажет, что большая часть исходного набора команд по-прежнему поддерживается на современных процессорах x86. 286 конкретных нюансов, возможно, не работают, как и другие новые функции определенных поколений, но основные инструкции, загрузка, сохранение, добавление, вычитание, push, pop и т.д. Все еще работают и будут продолжать работать. Я чувствую, что лучше "спуститься по центру полосы", не попадайте в чип или специальные функции ghee whiz, используйте базовое расточение, работая с начала синтаксиса языка.
Поскольку каждое поколение в семье пытается использовать определенные функции, обычно производительность, способ передачи отдельных инструкций различным исполнительным блокам... на каждое поколение... Чтобы настроить настройку ассемблера на производительность, пытаясь выйти из компилятора, может быть сложно в лучшем случае. Вам нужны подробные знания о конкретном процессоре, который вы настраиваете. С ранних x86 дней до настоящего времени, к сожалению, то, что сделало код более быстрым на одном чипе, часто приводило к тому, что следующее поколение будет работать слишком медленно. Возможно, это был маскирующий инструмент маркетинга, не уверенный: "Купите горячий новый процессор, который стоит в два раза больше, чем тот, который у вас есть сейчас, рекламирует удвоенную тактовую частоту, но работает на той же копии окон на 30% медленнее. лет, когда следующая версия Windows скомпилирована (и этот чип устарел), он будет удваиваться по производительности". Другим побочным эффектом этого является то, что на данный момент вы не можете взять одну программу на C и создать один двоичный файл, который быстро запускается на всех процессорах x86, для производительности вам нужно настроить для конкретного процессора, что означает, что вам нужно хотя бы сообщить компилятору оптимизировать и для какой семьи оптимизировать. И как окна или офис, или что-то, что вы распространяете в виде двоичного кода, вы, вероятно, не можете или не хотите каким-то образом похоронить несколько по-разному настроенных копий одной и той же программы в одном пакете или в одном двоичном... диске вниз по центру дороги.
В результате всех усовершенствований аппаратного обеспечения может быть в ваших интересах не пытаться настроить вывод компилятора или ручной ассемблер на какой-либо один чип в частности. В среднем аппаратные улучшения компенсируют отсутствие настройки компилятора, и ваша же программа, надеюсь, просто работает немного быстрее каждого поколения. Один из поставщиков чипов, используемый для того, чтобы сделать сегодняшние популярные скомпилированные двоичные файлы быстрее, быстрее, другой производитель улучшил внутренности, так что, если вы перекомпилировали исходный источник для новых внутренних компонентов, вы могли бы быстрее работать завтра. Эти действия между поставщиками не обязательно продолжаются, каждое поколение запускает предыдущие бинарные файлы медленнее, но tomorrows перекомпилирует источник с той же скоростью или медленнее. Он будет работать быстрее, перезаписанные программы быстрее, иногда с тем же компилятором, иногда вам нужен компилятор tomorrows. Разве это не весело!
Итак, как мы узнаем, какая конкретная скомпилированная или собранная вручную программа выполняется так быстро, как это возможно? Мы не на самом деле, на самом деле для x86 вы можете гарантировать, что это не так, запустите его на одном чипе в семье, и он медленный, запустите его на другом, он может быстро вспыхнуть. x86 или нет, кроме очень коротких программ или очень детерминированных программ, например, вы найдете на микроконтроллере, вы не можете сказать, что это самое быстрое решение. Например, кэш-память очень сложна, даже если ее можно настроить, и ее память, особенно на ПК, где пользователь может выбирать различные размеры, скорости, ранги, банки и т.д., А также изменять настройки BIOS, чтобы изменить еще больше настроек, вы на самом деле не могу сказать, чтобы компилятор настроился на это. Таким образом, даже на одном и том же компьютере один и тот же скомпилированный двоичный файл имеет возможность повернуть некоторые ручки и сделать эту программу намного быстрее или намного медленнее. Измените семейства процессоров, измените чипсеты, материнские платы и т.д. И нет никакого способа настроить столько переменных. Характер бизнеса x86 pc стал слишком хаотичным.
Другие семейства чипов не так проблематичны. Некоторые, возможно, но не все. Таким образом, это не общие утверждения, а специфические для семейства чипов 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 и используемый вами компилятор взаимодействуют друг с другом. Никакой компилятор не совместим со стандартами, поскольку сами стандарты нечеткие, оставляя некоторые функции языка на усмотрение компилятора (проезжайте по середине дороги и не используйте эти части языка).
В нижней части характера ваших вопросов я бы рекомендовал написать кучу небольших функций или программ с небольшими функциями, скомпилировать ассемблер или скомпилировать объект и разобрать, чтобы узнать, что делает компилятор. Обязательно используйте разные настройки оптимизации для каждой программы. Получите рабочее знание чтения набора команд (получив выход asm компилятора или дизассемблера, имеет много дополнительного пуха, который становится на пути читаемости, вам нужно пройти мимо этого, вам не нужно почти ничего, если вы хотите писать ассемблер). Дайте себе 5-20 лет обучения и экспериментов, прежде чем вы можете ожидать, что вы будете превосходить компилятор на регулярной основе, если это ваша цель. К тому времени вы узнаете, что, особенно с этим семейством чипов, это бесполезное усилие, вы выигрываете несколько, но в основном проигрываете... Было бы в ваших интересах собрать (для ассемблера) тот же код другим семействам микросхем, как рука и mips, и получить общее представление о том, что C-код хорошо компилируется в целом и что C-код не компилируется хорошо, и сделать ваше программирование на C лучше, чем пытаться сделать ассемблер лучше. Также попробуйте другие компиляторы, такие как llvm. У Gcc есть много причуд, которые многие считают стандартами на языке C, но вместо этого являются нюансами или проблемами с конкретным компилятором. Возможность читать и анализировать сборку выходных данных компиляторов и их варианты предоставит эти знания. Поэтому я рекомендую вам работать над чтением знаний набора инструкций, не обязательно обязательно научиться писать его с нуля.
Ответ 3
Вам нужно взглянуть на него с аппаратной точки зрения, язык сборки создается в зависимости от того, что может сделать процессор. Каждый раз, когда создается новая функция в ЦП, создается соответствующая инструкция сборки, чтобы ее можно было использовать.
Таким образом, сборка очень зависит от процессора, языки высокого уровня, такие как С++, обеспечивают абстракции от этого, чтобы мы не могли думать о деталях, таких как инструкции процессора, а также компилятор генерирует оптимизированный ассемблерный код.
EDIT:
Сколько ассемблерных языков существует? Сколько из них хорошо работает с другими языки?
столько, сколько есть разных типов ЦП. Второй вопрос я не понял. Сборка сама по себе не взаимодействует с каким-либо другим языком, выходом, машинным кодом.
Как кто-то начнет писать в сборке, а затем скомпилировать его в объект/двоичный код? `
Принцип похож на запись на любом другом скомпилированном языке, вы создаете текстовый файл с инструкциями по сборке, используйте ассемблер для компиляции его в машинный код. Затем свяжите его с возможными библиотеками времени выполнения.
Как кто-то тогда ссылается на функции/подпрограммы в этом код сборки с языка C или С++?
С++ и C предоставляют встроенную сборку, поэтому нет необходимости связывать, но если вы хотите связать, вам нужно создать объект сборки, следуя тем же/похожим соглашениям вызова, что и язык хоста. Например, некоторые языки при вызове функции перетаскивают аргументы функции в стек в определенном порядке, поэтому вам придется делать то же самое.
Как мы знаем код, который мы написали в сборке это возможно быстрее всего может быть?
Потому что он ближе всего к реальному оборудованию. Когда вы имеете дело с языками более высокого уровня, вы не знаете, что компилятор сделает с вашим циклом for. Однако чаще всего они делают хорошую и лучшую работу по оптимизации кода, чем может сделать человек (конечно, в особых обстоятельствах вы, вероятно, можете получить лучший результат).
Ответ 4
Существует много разных языков ассемблера. Обычно для каждого набора команд процессора имеется по крайней мере один, что означает один для каждого типа процессора. Одна вещь, которую вы также должны иметь в виду, состоит в том, что даже для одного процессора может быть несколько различных ассемблерных программ, которые могут использовать другой синтаксис, который из формального представления представляет собой другой язык. (для 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 может использовать более общий цикл. Эти функции также повторно используются так часто, что оптимизация их вручную часто стоит усилий в долгосрочной перспективе.
Другое, связанное, место, где написано много сборки, находится в составе компиляторов. Компиляторы должны знать, как реализовать вещи, и многие из них производят сборку, поэтому в них встроены шаблоны сборки (или что-то подобное) для использования в генерации выходного кода.
Даже если вы никогда не пишете какую-либо сборку, зная, что инструкции и регистры вашей целевой системы часто полезны. Они могут помочь в отладке, но они также могут помочь в написании кода. Знание целевого процессора может помочь вам лучше писать (меньший и/или более быстрый) код для него (даже на языке более высокого уровня), и знакомство с несколькими разными процессорами поможет вам написать код, который будет хорош для многих процессоров, потому что вы будете знать, как работают процессоры.
Ответ 5
Мы делаем это в нашей работе в реальном времени (больше, чем мы должны). Небольшая часть сборки также может быть весьма полезна, когда вы разговариваете с оборудованием, и требуются конкретные машинные инструкции (например: все записи должны быть 16-битными, или вы будете шланги рядом с регистрами).
То, что я обычно вижу сегодня, - это вставки сборки в код языка более высокого уровня. Как именно это делается, зависит от вашего языка, а иногда и от компилятора.
Ответ 6
Я знаю, что у процессоров разные наборы инструкций выше базового x86 набор инструкций. Все сборки языки поддерживают всю инструкцию наборы?
"Язык ассемблера" - это своего рода неправильное обозначение, по крайней мере, так, как вы его используете. Ассемблеры - это не язык (выпускники CS могут возражать), но и больше инструментов конвертера, которые воспринимают текстовое представление и генерируют из него двоичное изображение с близкой к соотношению 1:1 между текстовыми элементами (memnonics, label и numbers) и двоичными элементы. За элементами ассемблерного языка нет более глубокой логики, потому что их возможности для цитирования и перенаправления заканчиваются главным образом на уровне 1; вы можете, например, использовать EAX только по одной инструкции за раз - следующее использование EAX в следующей инструкции не имеет отношения к ее предыдущему использованию EXCEPT для неписаного логического соединения, которое имел в виду программист - вот почему так легко создавать ошибки в ассемблере.
Как кто-то начнет писать в сборке, а затем компилировать его в объект/двоичный код?
Нужно было бы установить самый низкий общий знаменатель наборов инструкций и закодировать функцию времени ожидаемых архитектур, на которых должен работать код. IOW, если вы не кодируете определенную аппаратную платформу, которая определена на момент написания (например, игровая консоль, встроенная плата), вы больше не делаете этого.
Как кто-то тогда ссылается на функций/подпрограмм внутри код сборки с языка C или С++?
Вам нужно объявить их в своем HLL - см. справочник по компиляторам.
Как мы знаем код, который мы написали в сборке это возможно быстрее всего может быть?
Невозможно узнать. Будьте довольны этим и кодом.