Язык программирования низкого уровня
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
Язык программирования низкого уровня — это язык программирования , который практически не абстрагируется компьютера от архитектуры набора команд ; команды или функции языка структурно аналогичны инструкциям процессора. Обычно это относится либо к машинному коду , либо к языку ассемблера . Из-за низкой (отсюда и этого слова) абстракции между языком и машинным языком языки низкого уровня иногда описываются как «близкие к аппаратному обеспечению». Программы, написанные на языках низкого уровня, как правило, относительно непереносимы из-за оптимизации для определенного типа системной архитектуры. [1] [2] [3] [4]
Языки низкого уровня могут конвертироваться в машинный код без компилятора или интерпретатора — языки программирования второго поколения. [5] [6] используйте более простой процессор, называемый ассемблером , и полученный код запускается непосредственно на процессоре. Программу, написанную на языке низкого уровня, можно заставить работать очень быстро и при небольшом объеме памяти . Эквивалентная программа на языке высокого уровня может быть менее эффективной и использовать больше памяти. Языки низкого уровня просты, но считаются трудными в использовании из-за множества технических деталей, которые программист должен помнить. Для сравнения, язык программирования высокого уровня изолирует семантику выполнения компьютерной архитектуры от спецификации программы, что упрощает разработку. [1]
Машинный код
[ редактировать ]Машинный код — это форма, в которой код, который может быть непосредственно выполнен, хранится на компьютере. машинного языка Он состоит из инструкций , хранящихся в памяти, которые выполняют такие операции, как перемещение значений в ячейки памяти и из них, арифметическую и логическую логику, а также проверку значений и, на основе теста, либо выполнение следующей инструкции в памяти, либо выполнение инструкция в другом месте.
Машинный код обычно хранится в памяти в виде двоичных данных. Программисты почти никогда не пишут программы непосредственно в машинном коде; вместо этого они пишут код на ассемблере или языках программирования более высокого уровня. [1]
Хотя на машинных языках написано немного программ, программисты часто научаются читать их, работая с дампами ядра или осуществляя отладку с передней панели.
Пример функции в шестнадцатеричном представлении машинного кода x86-64 для вычисления n-го числа Фибоначчи , где каждая строка соответствует одной инструкции:
89 f8 85 ff 74 26 83 ff 02 76 1c 89 f9 ba 01 00 00 00 be 01 00 00 00 8d 04 16 83 f9 02 74 0d 89 d6 ff c9 89 c2 eb f0 b8 01 00 00 c3
Язык ассемблера
[ редактировать ]Языки второго поколения предоставляют один уровень абстракции поверх машинного кода. На заре программирования на таких компьютерах, как TX-0 и PDP-1 , первое, что MIT сделали хакеры , — это написали ассемблеры. [7] В языке ассемблера мало семантики или формальной спецификации, поскольку он представляет собой лишь отображение удобочитаемых символов, включая символические адреса, в коды операций , адреса , числовые константы, строки и т. д. Обычно одна машинная инструкция представлена как одна строка ассемблерного кода, обычно называемая мнемоникой. [8] Ассемблер создает объектные файлы , которые могут связываться с другими объектными файлами или загружаться самостоятельно.
Большинство ассемблеров предоставляют макросы для генерации общих последовательностей инструкций.
Пример: тот же калькулятор чисел Фибоначчи , что и выше, но на языке ассемблера x86-64 с использованием синтаксиса AT&T :
fib:
movl %edi, %eax ; put the argument into %eax
testl %edi, %edi ; is it zero?
je .return_from_fib ; yes - return 0, which is already in %eax
cmpl $2, %edi ; is 2 greater than or equal to it?
jbe .return_1_from_fib ; yes (i.e., it's 1 or 2) - return 1
movl %edi, %ecx ; no - put it in %ecx, for use as a counter
movl $1, %edx ; the previous number in the sequence, which starts out as 1
movl $1, %esi ; the number before that, which also starts out as 1
.fib_loop:
leal (%rsi,%rdx), %eax ; put the sum of the previous two numbers into %eax
cmpl $2, %ecx ; is the counter 2?
je .return_from_fib ; yes - %eax contains the result
movl %edx, %esi ; make the previous number the number before the previous one
decl %ecx ; decrement the counter
movl %eax, %edx ; make the current number the previous number
jmp .fib_loop ; keep going
.return_1_from_fib:
movl $1, %eax ; set the return value to 1
.return_from_fib:
ret ; return
В этом примере кода регистрам процессора x86-64 присваиваются имена и ими можно управлять напрямую. Функция загружает свой 32-битный аргумент из %edi
в соответствии с бинарным интерфейсом приложения System V для x86-64 и выполняет его расчет, манипулируя значениями в %eax
, %ecx
, %esi
, и %edi
регистрируется до тех пор, пока не завершится и не вернется. Обратите внимание, что в этом языке ассемблера нет понятия возврата значения. Результат сохраняется в %eax
зарегистрируйте, опять же в соответствии с двоичным интерфейсом приложения System V, ret
Инструкция просто удаляет верхний 64-битный элемент стека и вызывает выборку следующей инструкции из этого места (этой инструкцией обычно является инструкция, следующая сразу за той, которая вызвала эту функцию), при этом результат функции сохраняется в %eax
. Язык ассемблера x86-64 не налагает никаких стандартов на передачу значений в функцию или возврат значений из функции (и фактически не имеет понятия функции); они определяются двоичным интерфейсом приложения , таким как System V ABI для определенного набора команд.
Сравните это с той же функцией в C :
unsigned int fib(unsigned int n)
{
if (!n)
{
return 0;
}
else if (n <= 2)
{
return 1;
}
else
{
unsigned int f_nminus2, f_nminus1, f_n;
for (f_nminus2 = f_nminus1 = 1, f_n = 0; ; --n)
{
f_n = f_nminus2 + f_nminus1;
if (n <= 2)
{
return f_n;
}
f_nminus2 = f_nminus1;
}
}
}
Этот код по структуре похож на пример на языке ассемблера, но есть существенные различия с точки зрения абстракции:
- Входные данные (параметр
n
) — это абстракция, не определяющая место хранения на оборудовании. На практике компилятор C следует одному из многих возможных соглашений о вызовах , чтобы определить место хранения входных данных. - Локальные переменные
f_nminus2
,f_nminus1
, иf_n
представляют собой абстракции, которые не определяют какое-либо конкретное место хранения на оборудовании. Компилятор C решает, как их хранить для целевой архитектуры. - Функция return указывает возвращаемое значение, но не определяет, как оно возвращается. Компилятор C для любой конкретной архитектуры реализует стандартный механизм возврата значения. Компиляторы для архитектуры x86 обычно (но не всегда) используют
%eax
зарегистрируйтесь для возврата значения, как в примере с ассемблером (автор примера с ассемблером решил использовать двоичный интерфейс приложения System V для соглашения x86-64, но язык ассемблера этого не требует).
Эти абстракции делают код C компилируемым без изменений на любой архитектуре, для которой написан компилятор C. Код языка ассемблера x86 специфичен для архитектуры x86-64 и двоичного интерфейса приложения System V для этой архитектуры.
Низкоуровневое программирование на языках высокого уровня.
[ редактировать ]В конце 1960-х и 1970-х годах языки высокого уровня , которые включали некоторую степень доступа к функциям низкоуровневого программирования, такие как PL/S , BLISS , BCPL , расширенный ALGOL и ESPOL (для больших систем Берроуза ) и C. были введены . Одним из методов для этого является встроенная ассемблерная программа , в которой ассемблерный код встроен в язык высокого уровня, поддерживающий эту функцию. в зависимости от архитектуры Некоторые из этих языков также позволяют директивам оптимизации компилятора регулировать способ использования компилятором архитектуры целевого процессора.
Хотя такой язык, как C, является высокоуровневым, он не полностью абстрагирует возможность управления памятью, как другие языки. [9] В языке высокого уровня, таком как Python, программист не может напрямую обращаться к памяти из-за абстракций между интерпретатором и машиной. Таким образом, C может обеспечить больший контроль, предоставляя инструменты управления памятью с помощью таких инструментов, как выделение памяти (malloc). [10]
Кроме того, как упоминалось выше, следующий блок C взят из компилятора GNU и показывает возможность встроенной сборки C. Согласно документации GCC, это простое копирование и добавление кода. Этот код отображает взаимодействие между языком высокого уровня, например C, и его аналогом ассемблера среднего/низкого уровня. Хотя это не делает C изначально языком низкого уровня, эти средства выражают взаимодействие более прямым способом. [11]
int src = 1;
int dst;
asm ("mov %1, %0\n\t"
"add $1, %0"
: "=r" (dst)
: "r" (src));
printf("%d\n", dst);
Ссылки
[ редактировать ]- ^ Jump up to: а б с «3.1: Структура программ низкого уровня» . Рабочая сила LibreTexts . 05.03.2021 . Проверено 03 апреля 2023 г.
- ^ «Что такое язык низкого уровня?» . Гики для Гиков . 2023-11-19 . Проверено 27 апреля 2024 г.
- ^ «Язык низкого уровня? Что нужно знать | Lenovo US» . www.lenovo.com . Проверено 27 апреля 2024 г.
- ^ «Языки низкого уровня — Классификация языков программирования и трансляторов — AQA — GCSE Computer Science Revision — AQA» . BBC Bitesize . Проверено 27 апреля 2024 г.
- ^ «Поколение языков программирования» . Гики для Гиков . 22 октября 2017 г. Проверено 27 апреля 2024 г.
- ^ «Что такое языки поколений?» . www.computerhope.com . Проверено 27 апреля 2024 г.
- ^ Леви, Стивен (1994). Хакеры: герои компьютерной революции . Книги о пингвинах. п. 32. ISBN 0-14-100051-1 .
- ^ «Машинный язык/Язык ассемблера/Язык высокого уровня» . www.cs.mtsu.edu . Проверено 27 апреля 2024 г.
- ^ Керниган, Брайан В.; Ричи, Деннис М. (2014). Язык программирования Си . Серия программного обеспечения Prentice-Hall (2-е изд., 52-е печатное изд.). Река Аппер-Сэддл, Нью-Джерси: PTR Прентис-Холл. ISBN 978-0-13-110362-7 .
- ^ «malloc(3) — страница руководства Linux» . man7.org . Проверено 21 апреля 2024 г.
- ^ «Расширенный Asm (с использованием коллекции компиляторов GNU (GCC))» . gcc.gnu.org . Проверено 27 апреля 2024 г.