Операционная система это система программирования на языке низкого уровня

Низкоуровневый язык программирования-это язык программирования. Который практически не абстрагируется от архитектуры набора команд компьютера—команд или функций в языковой карте. Структурно сходных с инструкциями процессора. Как правило. Это относится либо к машинному коду, либо к языку ассемблера. Из-за низкой (отсюда и слово) абстракции между языком и машинным языком языки низкого уровня иногда описываются как Программы. Написанные на языках низкого уровня. Как правило. Относительно непереносимы, из-за того. Что он оптимизирован для определенного типа архитектуры системы.

Языки низкого уровня могут преобразовываться в машинный код без компилятора или интерпретатора –

языки программирования второго поколения используют более простой процессор. Называемый ассемблером,-и полученный код выполняется непосредственно на процессоре. Программу, написанную на языке низкого уровня. Можно заставить работать очень быстро. С небольшим объемом памяти. Эквивалентная программа на языке высокого уровня может быть менее эффективной и использовать больше памяти. Языки низкого уровня просты. Но считаются сложными в использовании из-за многочисленных технических деталей. Которые программист должен помнить.

Для сравнения, а язык программирования высокого уровня изолирует семантику выполнения компьютерной архитектуры от спецификации программы. Что упрощает разработку.

Машинный код

Передняя панель миникомпьютера PDP-8/E. Ряд переключателей внизу можно использовать для переключения в программе машинного языка.

Машинный код-это единственный язык. Который компьютер может обрабатывать непосредственно без предварительного преобразования. В настоящее время программисты почти никогда не пишут программы непосредственно в машинном коде. Потому что это требует внимания к многочисленным деталям. Которые язык высокого уровня обрабатывает автоматически.

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

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

Хотя немногие программы написаны на машинном языке. Программисты часто становятся искусными в чтении его через работу с дампами ядра или отладку с передней панели.

Пример: Функция в шестнадцатеричном представлении 32-битного машинного кода x86 для вычисления n— го числа Фибоначчи:

8B542408 83FA0077 06B80000 0000C383 FA027706 B8010000 00C353BB 01000000 B9010000 008D0419 83FA0376 078BD989 C14AEBF1 5BC3 

Язык ассемблера

Языки второго поколения обеспечивают один уровень абстракции поверх машинного кода. В первые дни кодирование на компьютеры, как ТХ-0 и для PDP-1, Первым делом МТИ. Хакеры написали монтажников.[1]ассемблера имеет мало семантика и формальная спецификация. Будучи только сопоставление понятных человеку символов. В том числе и символьные адреса, до

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

Большинство ассемблеров предоставляют макросы для генерации общих последовательностей инструкций.

Пример: Тот же калькулятор чисел Фибоначчи. Что и выше. Но на ассемблере x86-64 с использованием синтаксиса AT&T:

_fib: movl $1, %eax .fib_loop: cmpl $1, %edi jbe .fib_done movl %eax, %ecx addl %ebx, %eax movl %ecx, %ebx subl $1, %edi jmp .fib_loop .fib_done: ret 

В этом примере кода аппаратные функции процессора x86-64 (его

регистры) называются и управляются непосредственно. Функция загружает свои входные данные из %edi в соответствии с системой V ABI и выполняет свои вычисления . Манипулируя значениями в регистрах EAX, EBXи ECX, пока не завершит работу и не вернется. Обратите внимание. Что в этом языке ассемблера нет понятия возврата значения. Результат, сохраненный в регистре EAX, RET команда просто двигает код обработки расположении код записывается в стек (как правило. В инструкции сразу после того, как тот. Что называется эта функция). И это до автора для вызывающего кода. Чтобы знать. Что эта функция сохраняет результат в

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

Сравните это с той же функцией в C:

unsigned fib(unsigned n) { if (!n) return 0; else if (n  2) return 1; else { unsigned a, c; for (a = c = 1; ; --n) { c += a; if (n  2) return c; a = c - a; } } } 

Этот код очень похож по структуре на пример языка ассемблера. Но есть существенные различия с точки зрения абстракции:

  • Входные данные (параметр n) — это абстракция. Которая не указывает никакого места хранения на аппаратном обеспечении. На практике компилятор C следует одному из многих возможных соглашений о вызове, чтобы определить место хранения входных данных.
  • Версия языка ассемблера загружает входной параметр из стека в регистр и на каждой итерации цикла уменьшает значение в регистре. Никогда не изменяя значение в ячейке памяти стека.

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

  • Локальные переменные a. B и c являются абстракциями. Которые не задают никакого конкретного места хранения на оборудовании. Компилятор C решает. Как на самом деле хранить их для целевой архитектуры.

  • Функция return определяет возвращаемое значение. Но не определяет, как оно возвращается. Компилятор C для любой конкретной архитектуры реализует стандартный механизм возврата значения. Компиляторы для архитектуры x86 обычно (но не всегда) используют регистр EAX для возврата значения. Как в примере языка ассемблера (автор примера языка ассемблера решил скопировать соглашение C. Но язык ассемблера этого не требует).

Эти абстракции делают код C компилируемым без изменений на любой архитектуре. Для которой был написан компилятор C. Код ассемблера x86 специфичен для архитектуры x86.

Низкоуровневое программирование на языках высокого уровня

В конце 1960-х годов языки высокого уровня, такие как PL/S, BLISS, BCPL, extended ALGOL (для больших систем Берроуза) и C, включали некоторую степень доступа к функциям программирования низкого уровня. Одним из методов для этого является встроенная сборка, в которой ассемблерный код встроен в язык высокого уровня. Поддерживающий эту функцию. Некоторые из этих языков также позволяют директивам оптимизации компилятора. Зависящим от архитектуры, изменять способ использования компилятором целевой архитектуры процессора.