🧠FFVAcademy
🚧

Syscalls: a fronteira entre user-space e kernel

14 min de leitura·+70 XP

Toda vez que seu programa abre um arquivo, faz uma requisição de rede ou aloca memória, ele pede ao kernel para fazer isso em seu nome. Syscalls são o único ponto de entrada para o kernel — entender esse mecanismo explica latência, segurança e por que buffering importa.

Ring protection e o modelo de proteção da CPU

# Syscalls comuns que Python faz internamente:
# read()      — ler arquivo/socket
# write()     — escrever em arquivo/socket/stdout
# open()      — abrir arquivo
# close()     — fechar file descriptor
# mmap()      — mapear memória
# brk()/mmap()— alocar memória heap (malloc usa esses)
# socket()    — criar socket de rede
# connect()   — conectar a servidor TCP
# accept()    — aceitar conexão TCP
# epoll_*()   — multiplexação de I/O (usado pelo asyncio)
# clone()     — criar processo/thread
# execve()    — executar programa
# exit()      — terminar processo

# Python não expõe syscalls diretamente (use ctypes/cffi ou a lib os)
import os

# open() Python → sys.call open() → retorna file descriptor (int)
fd = os.open("/etc/hostname", os.O_RDONLY)
dados = os.read(fd, 256)
os.close(fd)
print(dados.decode())

# Verificar syscall number no Linux x86-64:
# /usr/include/asm/unistd_64.h ou ausyscall --list
# read   = 0
# write  = 1
# open   = 2
# close  = 3
# mmap   = 9
# exit   = 60

strace: observando syscalls em tempo real

# Usar strace para observar um programa Python:
# strace -f python meu_script.py 2>&1 | head -50

# Output de exemplo (strace de python -c "open('/tmp/x', 'w')"):
# execve("/usr/bin/python3", ["python3", "-c", "open(...)"], envp) = 0
# ... inicialização do interpretador ...
# openat(AT_FDCWD, "/tmp/x", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
# fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
# ioctl(3, TCGETS, 0x...) = -1 ENOTTY (not a tty) ← detecta se é terminal
# close(3) = 0

# Comandos úteis do strace:
# strace -p PID                        # attach a processo rodando
# strace -c python script.py           # sumário de contagem por syscall
# strace -e trace=file python script.py # filtra syscalls relacionadas a arquivo
# strace -e trace=network python script.py # syscalls de rede
# strace -T python script.py           # mostra tempo de cada syscall

# Exemplo de output de strace -c:
# % time   seconds  usecs/call     calls    errors syscall
# ------ --------- ----------- --------- --------- --------
#  40.12    0.001234         123        10           read
#  25.45    0.000783          78        10           write
#  10.23    0.000314          31        10           mmap
# ...

# perf (sem overhead de strace):
# perf stat python script.py    # contadores de hardware (cache misses, etc.)
# perf top                      # profiling em tempo real de qualquer processo

Buffering: reduzindo syscalls

ModoQuando flushSyscallsUso
UnbufferedA cada write()Muitasstderr, I/O crítico
Line-bufferedA cada \nModeradasstdout em terminal
Fully-bufferedBuffer cheio (~8KB)Poucasstdout em arquivo/pipe
O_DIRECTA cada write() sem cache kernelMuitas mas sem double-copyBancos de dados
import os, sys

# Ver e controlar buffering em Python
print(sys.stdout.buffer.raw.name)      # '<stdout>'
# stdout em terminal: line-buffered
# stdout em pipe/arquivo: fully-buffered

# Forçar escrita imediata
print("mensagem urgente", flush=True)  # flush=True na chamada
sys.stdout.flush()                      # flush manual

# O_DIRECT: escrever sem cache do kernel (bancos de dados usam isso)
# O banco gerencia seu próprio cache (buffer pool) e não quer dupla cópia
import ctypes

# Syscall vread/write com O_DIRECT bypassa page cache
# Requer alinhamento de 512 bytes no buffer e offset
# Usado por PostgreSQL para wal_sync_method = open_sync

# Medir overhead de syscalls com perf:
# perf stat -e syscalls:sys_enter_read,syscalls:sys_enter_write python script.py
Takeaways: cada syscall custa ~100-300ns de overhead de troca de contexto. Minimize syscalls com buffering (padrão em Python). Use strace -c para identificar quais syscalls dominam o tempo. eBPF/bpftrace são alternativas de produção com overhead mínimo. seccomp limita syscalls disponíveis para containers — base do Docker sandboxing.
💡
Próximo: File descriptors e I/O — por que "tudo é arquivo" no Linux e como processos compartilham file descriptors.
🧩

Quiz rápido

3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo