Пишу, по большей части, про историю, свою жизнь и немного про программирование.

Выделение памяти (ассемблер под Линукс)

До переезда блога на новый движок я писал аналог команды tee на ассемблере. Тогда передо мной стояла проблема выделения памяти — в команду может быть передано несколько имён файлов, я не знаю сколько, их нужно будет открыть, а под открытые файлы нужно место.

Программу я закончил, но из-за переезда последний этап её написания я не зафиксировал. Исправляюсь.

Как я уже упоминал, есть два способа выделить память под Линуксом — sys_brk и sys_mmap. Второй способ используется, когда памяти надо выделить много, очевидно это не мой случай, поэтому я использовал вызов sys_brk. Этот вызов двигает границу так называемой «кучи» (Runtime heap на картинке) вверх (к старшим адресам) и в получившемся пространстве можно что-то хранить.

Код выглядит так:

mov rax, 12 ; sys_brk
xor rdi, rdi ; в rdi записываем ноль (получить текущий адрес)
syscall

cmp rax, 0
jl memory_error ; обработка выделения памяти

; в rax у нас указатель на текущую границу кучи
; новый адрес нужно записать в регистр rdi
mov rcx, size ; количество хидеров файлов, которые надо хранить
lea rdi, [rcx * 8 + rax] ; каждый хидер занимает восемь байт
mov rax, 12 ; sys_brk
syscall

То есть получаем старый адрес, прибавляем сколько надо, устанавливаем новый адрес. Все менеджеры памяти Линукса (включая известнейший вызов malloc с Си), используют один из этих двух вызовов или их комбинацию.

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