#28_CMPL_DBG_TOS_TP
ELF64 — Executable and Linkable Format 64-bit (выполняемый и связываемый формат для 64 битной системы).
ELF64 — Executable and Linkable Format 64-bit (выполняемый и связываемый формат для 64 битной системы).
#29_CMPL_DBG_TP
DWARF (Debug With Arbitrary Record Format) — отладка с использованием произвольного формата записей.
DWARF (Debug With Arbitrary Record Format) — отладка с использованием произвольного формата записей.
#31_ASM_CMPL_GCC_TP
Последние версии компилятора и линкера GCC по умолчанию генерируют выполняемый код, не зависящий от положения в памяти, или перемещаемый код (position independent executable – PIE).
Это делается для защиты от хакеров, исследующих, как память используется программой, что в итоге позволяет им воздействовать на выполнение программы.
Такая программа слишком сложна для анализа (усложнение делается преднамеренно из соображений обеспечения безопасности).
Ключ компилятора -no-pie — отключает эту защиту и позволяет получить не перемещаемый код.
Последние версии компилятора и линкера GCC по умолчанию генерируют выполняемый код, не зависящий от положения в памяти, или перемещаемый код (position independent executable – PIE).
Это делается для защиты от хакеров, исследующих, как память используется программой, что в итоге позволяет им воздействовать на выполнение программы.
Такая программа слишком сложна для анализа (усложнение делается преднамеренно из соображений обеспечения безопасности).
Ключ компилятора -no-pie — отключает эту защиту и позволяет получить не перемещаемый код.
#32_BASH
Команда ld (GNU линкер в Linux) — используется для компоновки объектных файлов в исполняемые файлы или библиотеки.
Она является частью набора инструментов GNU Binutils и играет ключевую роль в процессе сборки программ на языке C/C++ и других ЯП, поддерживающих создание объектных файлов.
Основная функция команды ld — заключается в том, чтобы связать вместе различные объектные файлы (.o), библиотеки (.a или *.so) и другие компоненты, необходимые для создания конечного исполняемого файла или библиотеки.
Во время этого процесса компоновщик решает ссылки между различными частями программы, такими как функции и переменные, размещает секции данных и кода в памяти и создает итоговую программу.
Основные опции команды ld:
Пример использования.
Предположим, у нас есть два объектных файла main.o и funcs.o, а также библиотека libmath.a. Мы можем создать исполняемый файл следующим образом:
Здесь мы создаем исполняемый файл myprogram, используя объектные файлы main.o и funcs.o, а также подключаем библиотеку libmath.a, находящуюся в каталоге /path/to/libs.
Команда ld — инструмент для управления процессом компиляции и сборки программ в Linux.
Она предоставляет разработчикам гибкость и контроль над тем, как создаются исполняемые файлы и библиотеки, позволяя оптимизировать процесс разработки и настройки программного обеспечения под конкретные нужды.
Команда ld (GNU линкер в Linux) — используется для компоновки объектных файлов в исполняемые файлы или библиотеки.
Она является частью набора инструментов GNU Binutils и играет ключевую роль в процессе сборки программ на языке C/C++ и других ЯП, поддерживающих создание объектных файлов.
Основная функция команды ld — заключается в том, чтобы связать вместе различные объектные файлы (.o), библиотеки (.a или *.so) и другие компоненты, необходимые для создания конечного исполняемого файла или библиотеки.
Во время этого процесса компоновщик решает ссылки между различными частями программы, такими как функции и переменные, размещает секции данных и кода в памяти и создает итоговую программу.
Основные опции команды ld:
-o <output> — определяет имя выходного файла.
Например, -o myprogram;
-L <path> — указывает путь к каталогу, содержащему библиотеки.
Например, -L/usr/local/lib;
-l <library> — подключает библиотеку.
Например, -lm подключит математическую библиотеку libm;
-shared — создает разделяемую библиотеку (.so);
-static — использует статические версии библиотек вместо динамических;
-T <linker_script> — задает файл сценария линковщика, определяющий, как должны быть размещены секции программы в памяти.
Пример использования.
Предположим, у нас есть два объектных файла main.o и funcs.o, а также библиотека libmath.a. Мы можем создать исполняемый файл следующим образом:
$ ld -o myprogram main.o funcs.o -L/path/to/libs -lmath
Здесь мы создаем исполняемый файл myprogram, используя объектные файлы main.o и funcs.o, а также подключаем библиотеку libmath.a, находящуюся в каталоге /path/to/libs.
Команда ld — инструмент для управления процессом компиляции и сборки программ в Linux.
Она предоставляет разработчикам гибкость и контроль над тем, как создаются исполняемые файлы и библиотеки, позволяя оптимизировать процесс разработки и настройки программного обеспечения под конкретные нужды.
#33_ASM
ОСНОВНЫЕ ЧАСТИ ПРОГРАММЫ НА АССЕМБЛЕРЕ (NASM):
section .data
section .bss
section .txt
section .DATA — в разделе section .data объявляются и определяются инициализируемые данные в следующем формате:
<variable name> <type> <value>
Пример:
msg db "Hello, world!",10
msg — имя переменной,
db — тип переменной,
"Hello, world!" — строка сохраняемая в памяти,
10 — ascii код символа новой строки.
Раздел section .data также может содержать константы, т. е. значения, которые невозможно изменить в программе.
Константы определяются в следующем формате:
<constant name> equ <value>
Пример:
pi equ 3.1415926
ОСНОВНЫЕ ЧАСТИ ПРОГРАММЫ НА АССЕМБЛЕРЕ (NASM):
section .data
section .bss
section .txt
section .DATA — в разделе section .data объявляются и определяются инициализируемые данные в следующем формате:
<variable name> <type> <value>
Пример:
msg db "Hello, world!",10
msg — имя переменной,
db — тип переменной,
"Hello, world!" — строка сохраняемая в памяти,
10 — ascii код символа новой строки.
Раздел section .data также может содержать константы, т. е. значения, которые невозможно изменить в программе.
Константы определяются в следующем формате:
<constant name> equ <value>
Пример:
pi equ 3.1415926
#34_ASM
section .BSS
Аббревиатура bss означает Block Started by Symbol (блок, начинающийся с символа) и ведет свое происхождение с 1950-х годов, когда этот блок был компонентом языка ассемблера, разработанного для IBM 704.
В этот раздел помещаются не инициализированные переменные.
Для таких неинициализированных переменных выделяемое пространство памяти объявляется в следующем формате:
<variable name> <type> <number>
Например, следующая инструкция объявляет пространство памяти для массива из 20 двойных слов:
dArray resd 20
section .BSS
Аббревиатура bss означает Block Started by Symbol (блок, начинающийся с символа) и ведет свое происхождение с 1950-х годов, когда этот блок был компонентом языка ассемблера, разработанного для IBM 704.
В этот раздел помещаются не инициализированные переменные.
Для таких неинициализированных переменных выделяемое пространство памяти объявляется в следующем формате:
<variable name> <type> <number>
Например, следующая инструкция объявляет пространство памяти для массива из 20 двойных слов:
dArray resd 20
#35_ASM
section .TXT
Все действия происходят в разделе section .txt.
Этот раздел содержит код программы и начинается со следующих инструкций:
global main
main:
Часть main — называется меткой (label). Если метка расположена в строке, где после нее нет других символов, то после слова main должно быть записано двоеточие, иначе ассемблер выведет предупреждающее сообщение.
А предупреждающие сообщения не следует игнорировать.
Если за меткой следуют другие инструкции (в той же строке), то двоеточие не обязательно, но все же лучше выработать полезную привычку завершать все метки символом двоеточия.
К тому же это повышает удобство чтения исходного кода.
section .TXT
Все действия происходят в разделе section .txt.
Этот раздел содержит код программы и начинается со следующих инструкций:
global main
main:
Часть main — называется меткой (label). Если метка расположена в строке, где после нее нет других символов, то после слова main должно быть записано двоеточие, иначе ассемблер выведет предупреждающее сообщение.
А предупреждающие сообщения не следует игнорировать.
Если за меткой следуют другие инструкции (в той же строке), то двоеточие не обязательно, но все же лучше выработать полезную привычку завершать все метки символом двоеточия.
К тому же это повышает удобство чтения исходного кода.
#36_BASH_TOS
СПИСОК ПЕРЕМЕННЫХ СРЕДЫ в LINUX.
У каждого процесса имеется список переменных среды, являющийся набором переменных среды, который содержится в памяти пользовательского пространства процесса.
Каждый элемент этого списка состоит из имени и связанного с ним значения.
При создании нового процесса с помощью
fork()
он наследует копию среды своего родителя.
Таким образом, среда предоставляет родительскому процессу механизм для обмена информацией с дочерним процессом.
Когда процесс заменяет программу, запуская новую программу с помощью
exec(),
последняя либо наследует среду, используемую старой программой, либо получает новую среду, указанную как часть вызова exec().
Переменные среды создаются в большинстве оболочек командой export (или командой setenv в оболочке C shell):
$ export MYVAR='Hello world'
Программы на языке Cи могут получать доступ к среде, используя внешнюю переменную (char **environ) и различные библиотечные функции, позволяющие процессу извлекать и изменять значения в его среде.
СПИСОК ПЕРЕМЕННЫХ СРЕДЫ в LINUX.
У каждого процесса имеется список переменных среды, являющийся набором переменных среды, который содержится в памяти пользовательского пространства процесса.
Каждый элемент этого списка состоит из имени и связанного с ним значения.
При создании нового процесса с помощью
fork()
он наследует копию среды своего родителя.
Таким образом, среда предоставляет родительскому процессу механизм для обмена информацией с дочерним процессом.
Когда процесс заменяет программу, запуская новую программу с помощью
exec(),
последняя либо наследует среду, используемую старой программой, либо получает новую среду, указанную как часть вызова exec().
Переменные среды создаются в большинстве оболочек командой export (или командой setenv в оболочке C shell):
$ export MYVAR='Hello world'
Программы на языке Cи могут получать доступ к среде, используя внешнюю переменную (char **environ) и различные библиотечные функции, позволяющие процессу извлекать и изменять значения в его среде.
#37_TOS
ОГРАНИЧЕНИЯ РЕСУРСОВ в LINUX.
Каждый процесс потребляет ресурсы, например открытые файлы, память и время центрального процессора. Используя системный вызов
setrlimit();
процесс может установить верхний предел своего потребления различных ресурсов.
Каждый такой предел имеет два связанных с ним значения:
— мягкое ограничение, ограничивающее тот объем ресурса, который процесс может задействовать;
— жесткое ограничение, представляющее собой верхний предел значения, которое может быть отрегулировано мягким ограничением.
Непривилегированный процесс может изменить свое мягкое ограничение для конкретного ресурса на любое значение в диапазоне от нуля и до соответствующего жесткого ограничения, но свое жесткое ограничение он может только понизить.
Ограничения ресурсов оболочки могут быть отрегулированы с использованием команды
$ ulimit
Эти настройки ограничений наследуются дочерними процессами, создаваемыми оболочкой для выполнения команд.
ОГРАНИЧЕНИЯ РЕСУРСОВ в LINUX.
Каждый процесс потребляет ресурсы, например открытые файлы, память и время центрального процессора. Используя системный вызов
setrlimit();
процесс может установить верхний предел своего потребления различных ресурсов.
Каждый такой предел имеет два связанных с ним значения:
— мягкое ограничение, ограничивающее тот объем ресурса, который процесс может задействовать;
— жесткое ограничение, представляющее собой верхний предел значения, которое может быть отрегулировано мягким ограничением.
Непривилегированный процесс может изменить свое мягкое ограничение для конкретного ресурса на любое значение в диапазоне от нуля и до соответствующего жесткого ограничения, но свое жесткое ограничение он может только понизить.
Ограничения ресурсов оболочки могут быть отрегулированы с использованием команды
$ ulimit
Эти настройки ограничений наследуются дочерними процессами, создаваемыми оболочкой для выполнения команд.
#38_TOS
ОТОБРАЖЕНИЕ В ПАМЯТИ в LINUX.
Системный вызов
mmap();
создает в виртуальном адресном пространстве вызывающего процесса новое отображение в памяти.
Отображения делятся на две категории:
файловое отображение — отображает область файла на виртуальную память вызывающего процесса.
После отображения содержимое файла может быть доступно с помощью операций над байтами в соответствующей области памяти. Страницы отображения автоматически загружаются из файла по мере надобности.
анонимное отображение — не имеет соответствующего файла. Вместо этого страницы отображения получают начальное значение 0.
Отображения в памяти служат для различных целей, включая инициализацию текстового сегмента процесса из соответствующего сегмента выполняемого файла, выделения новой (заполненной нулями) памяти, файлового ввода-вывода (ввода-вывода с отображением в памяти), обмена данными между процессами (через общее отображение в памяти).
ОТОБРАЖЕНИЕ В ПАМЯТИ в LINUX.
Системный вызов
mmap();
создает в виртуальном адресном пространстве вызывающего процесса новое отображение в памяти.
Отображения делятся на две категории:
файловое отображение — отображает область файла на виртуальную память вызывающего процесса.
После отображения содержимое файла может быть доступно с помощью операций над байтами в соответствующей области памяти. Страницы отображения автоматически загружаются из файла по мере надобности.
анонимное отображение — не имеет соответствующего файла. Вместо этого страницы отображения получают начальное значение 0.
Отображения в памяти служат для различных целей, включая инициализацию текстового сегмента процесса из соответствующего сегмента выполняемого файла, выделения новой (заполненной нулями) памяти, файлового ввода-вывода (ввода-вывода с отображением в памяти), обмена данными между процессами (через общее отображение в памяти).
#39_TOS
МЕЖПРОЦЕССНОЕ ВЗАИМОДЕЙСТВИЕ в LINUX.
В Linux, как и во всех современных реализациях UNIX, предоставляется обширный набор механизмов для межпроцессного взаимодействия (Interprocess Communication, IPC), включая следующие:
СИГНАЛЫ, которые используются в качестве признака возникновения события;
КОНВЕЙЕРЫ (известные пользователям оболочек в виде оператора |) и FIFO-буферы, которые могут применяться для передачи данных между процессами;
СОКЕТЫ, которые могут использоваться для передачи данных от одного процесса к другому (данные при этом находятся на одном и том же базовом компьютере либо на различных хостах, связанных по сети);
ФАЙЛОВАЯ БЛОКИРОВКА, позволяющая процессу блокировать области файла с целью предотвращения их чтения или обновления содержимого файла другими процессами;
ОЧЕРЕДИ СООБЩЕНИЙ, которые используются для обмена сообщениями (пакетами данных) между процессами;
СЕМАФОРЫ, которые применяются для синхронизации действий процессов;
СОВМЕСТНО ИСПОЛЬЗУЕМАЯ ПАМЯТЬ, позволяющая двум и более процессам совместно использовать часть памяти. Когда один процесс изменяет содержимое совместно используемой области памяти, изменения тут же могут быть видимы всем остальным процессам.
МЕЖПРОЦЕССНОЕ ВЗАИМОДЕЙСТВИЕ в LINUX.
В Linux, как и во всех современных реализациях UNIX, предоставляется обширный набор механизмов для межпроцессного взаимодействия (Interprocess Communication, IPC), включая следующие:
СИГНАЛЫ, которые используются в качестве признака возникновения события;
КОНВЕЙЕРЫ (известные пользователям оболочек в виде оператора |) и FIFO-буферы, которые могут применяться для передачи данных между процессами;
СОКЕТЫ, которые могут использоваться для передачи данных от одного процесса к другому (данные при этом находятся на одном и том же базовом компьютере либо на различных хостах, связанных по сети);
ФАЙЛОВАЯ БЛОКИРОВКА, позволяющая процессу блокировать области файла с целью предотвращения их чтения или обновления содержимого файла другими процессами;
ОЧЕРЕДИ СООБЩЕНИЙ, которые используются для обмена сообщениями (пакетами данных) между процессами;
СЕМАФОРЫ, которые применяются для синхронизации действий процессов;
СОВМЕСТНО ИСПОЛЬЗУЕМАЯ ПАМЯТЬ, позволяющая двум и более процессам совместно использовать часть памяти. Когда один процесс изменяет содержимое совместно используемой области памяти, изменения тут же могут быть видимы всем остальным процессам.
#40_TOS
СИГНАЛЫ зачастую описываются как «программные прерывания».
Поступление сигнала информирует процесс о том, что случилось какое-то событие или возникли исключительные условия.
Каждый тип сигнала идентифицируется с помощью целочисленного значения, определяемого в символьном имени, имеющем форму
SIGxxxx.
Сигналы отправляются процессу ядром, другим процессом (с соответствующими разрешениями) или самим процессом.
Например, ядро может отправить сигнал процессу, когда произойдет что-нибудь из следующего перечня:
— пользователь набрал на клавиатуре команду прерывания (обычно это Ctrl+C);
— завершился один из дочерних процессов данного процесса;
— истекло время таймера (будильника), установленного процессом;
— процесс попытался получить доступ к неверному адресу в памяти.
В оболочке сигнал процессу можно отправить с помощью команды
$ kill.
Внутри программ ту же возможность может предоставить системный вызов
kill( ).
Когда процесс получает сигнал, он, в зависимости от сигнала, выполняет одно из
следующих действий:
— игнорирует сигнал;
— прекращает свою работу по сигналу;
— приостанавливается, чтобы впоследствии возобновить свое выполнение с получением сигнала специального назначения.
СИГНАЛЫ зачастую описываются как «программные прерывания».
Поступление сигнала информирует процесс о том, что случилось какое-то событие или возникли исключительные условия.
Каждый тип сигнала идентифицируется с помощью целочисленного значения, определяемого в символьном имени, имеющем форму
SIGxxxx.
Сигналы отправляются процессу ядром, другим процессом (с соответствующими разрешениями) или самим процессом.
Например, ядро может отправить сигнал процессу, когда произойдет что-нибудь из следующего перечня:
— пользователь набрал на клавиатуре команду прерывания (обычно это Ctrl+C);
— завершился один из дочерних процессов данного процесса;
— истекло время таймера (будильника), установленного процессом;
— процесс попытался получить доступ к неверному адресу в памяти.
В оболочке сигнал процессу можно отправить с помощью команды
$ kill.
Внутри программ ту же возможность может предоставить системный вызов
kill( ).
Когда процесс получает сигнал, он, в зависимости от сигнала, выполняет одно из
следующих действий:
— игнорирует сигнал;
— прекращает свою работу по сигналу;
— приостанавливается, чтобы впоследствии возобновить свое выполнение с получением сигнала специального назначения.
#41_TOS_TP
ПОТОКИ — в современных реализациях UNIX у каждого процесса может быть несколько потоков выполнения.
Потоки можно представить себе в качестве набора процессов, совместно использующих одну и ту же виртуальную память, а также ряд других атрибутов.
Каждый поток выполняет один и тот же программный код и совместно с другими потоками использует одну и ту же область данных и кучу. Но каждый поток имеет свой СОБСТВЕННЫЙ СТЕК, содержащий локальные переменные и информацию о связанности вызовов функций.
Потоки могут осуществлять взаимный обмен данными через совместно используемые глобальные переменные.
API для работы с потоками предоставляет условные переменные и мьютексы, являющиеся примитивами, позволяющими потокам процесса обмениваться данными и синхронизировать свои действия, в частности их использование общих переменных.
Потоки могут также обмениваться друг с другом данными с применением IPC и механизмов синхронизации.
Основным преимуществом использования потоков является упрощение обмена данными (через глобальные переменные) между сотрудничающими потоками. Кроме того, некоторые алгоритмы более естественно преобразуются в многопоточные реализации,
чем в варианты использования нескольких процессов.
Помимо этого, многопоточные приложения могут легко воспользоваться преимуществами параллельной обработки на многопроцессорном оборудовании.
ПОТОКИ — в современных реализациях UNIX у каждого процесса может быть несколько потоков выполнения.
Потоки можно представить себе в качестве набора процессов, совместно использующих одну и ту же виртуальную память, а также ряд других атрибутов.
Каждый поток выполняет один и тот же программный код и совместно с другими потоками использует одну и ту же область данных и кучу. Но каждый поток имеет свой СОБСТВЕННЫЙ СТЕК, содержащий локальные переменные и информацию о связанности вызовов функций.
Потоки могут осуществлять взаимный обмен данными через совместно используемые глобальные переменные.
API для работы с потоками предоставляет условные переменные и мьютексы, являющиеся примитивами, позволяющими потокам процесса обмениваться данными и синхронизировать свои действия, в частности их использование общих переменных.
Потоки могут также обмениваться друг с другом данными с применением IPC и механизмов синхронизации.
Основным преимуществом использования потоков является упрощение обмена данными (через глобальные переменные) между сотрудничающими потоками. Кроме того, некоторые алгоритмы более естественно преобразуются в многопоточные реализации,
чем в варианты использования нескольких процессов.
Помимо этого, многопоточные приложения могут легко воспользоваться преимуществами параллельной обработки на многопроцессорном оборудовании.
#42_TOS_TP
РЕГИСТРЫ (registers) — это области (локации), используемые процессором для хранения данных, инструкций или адресов памяти.
Количество регистров невелико, но процессор может считывать и записывать их чрезвычайно быстро.
Можно считать регистры некоторой разновидностью блокнота для процессора, где он хранит временную информацию.
ПРАВИЛО — если важна скорость, то процессор может получать доступ к регистрам намного быстрее, чем доступ к оперативной памяти.
РЕГИСТРЫ (registers) — это области (локации), используемые процессором для хранения данных, инструкций или адресов памяти.
Количество регистров невелико, но процессор может считывать и записывать их чрезвычайно быстро.
Можно считать регистры некоторой разновидностью блокнота для процессора, где он хранит временную информацию.
ПРАВИЛО — если важна скорость, то процессор может получать доступ к регистрам намного быстрее, чем доступ к оперативной памяти.
#43_ASM_TOS_TP
РЕГИСТРЫ ОБЩЕГО НАЗНАЧЕНИЯ.
Существует 16 регистров общего назначения, и каждый из них может использоваться как 64-битовый, 32-битовый, 16-битовый или 8-битовый регистр.
В таблице ниже перечислены имена всех этих регистров, обозначающие соответствующие размеры.
Четыре регистра rax, rbx, rcx, rdx могут иметь два типа 8-битовых регистров — нижние 8 бит в нижней половине 16-битового регистра и верхние 8 бит в верхней половине 16-битового регистра.
Несмотря на то, что регистры rbp и rsp называются регистрами общего назначения, с ними нужно работать с особой осторожностью, так как они используются процессором во время выполнения программы.
РЕГИСТРЫ ОБЩЕГО НАЗНАЧЕНИЯ.
Существует 16 регистров общего назначения, и каждый из них может использоваться как 64-битовый, 32-битовый, 16-битовый или 8-битовый регистр.
В таблице ниже перечислены имена всех этих регистров, обозначающие соответствующие размеры.
Четыре регистра rax, rbx, rcx, rdx могут иметь два типа 8-битовых регистров — нижние 8 бит в нижней половине 16-битового регистра и верхние 8 бит в верхней половине 16-битового регистра.
Несмотря на то, что регистры rbp и rsp называются регистрами общего назначения, с ними нужно работать с особой осторожностью, так как они используются процессором во время выполнения программы.
#44_ASM_TOS_TP
РЕГИСТР СЧЕТЧИКА КОМАНД - rip.
Процессор постоянно отслеживает следующую выполняемую инструкцию, сохраняя ее адрес в регистре rip.
Значение в регистре rip можно изменить на любое на свой страх и риск (но лучше этого не делать).
Более безопасный способ изменения значения rip — использование инструкций переходов (jump).
РЕГИСТР СЧЕТЧИКА КОМАНД - rip.
Процессор постоянно отслеживает следующую выполняемую инструкцию, сохраняя ее адрес в регистре rip.
Значение в регистре rip можно изменить на любое на свой страх и риск (но лучше этого не делать).
Более безопасный способ изменения значения rip — использование инструкций переходов (jump).
#45_ASM_TOS_TP
РЕГИСТР ФЛАГОВ - rflags.
После выполнения очередной инструкции программа может проверить, установлен ли конкретный флаг (например, ZF=1), чтобы затем действовать соответственно.
Существует еще один регистр флагов с именем MXCSR, который используется в одной инструкции со многими потоками данных (SIMD).
В таблице ниже приведены возможные значения регистра rflags.
РЕГИСТР ФЛАГОВ - rflags.
После выполнения очередной инструкции программа может проверить, установлен ли конкретный флаг (например, ZF=1), чтобы затем действовать соответственно.
Существует еще один регистр флагов с именем MXCSR, который используется в одной инструкции со многими потоками данных (SIMD).
В таблице ниже приведены возможные значения регистра rflags.
#46_ASM_TOS_TP
РЕГИСТРЫ xmm и ymm — используются для операций с числами с плавающей точкой и в инструкциях со многими потоками данных (SIMD).
РЕГИСТРЫ xmm и ymm — используются для операций с числами с плавающей точкой и в инструкциях со многими потоками данных (SIMD).