🧠FFVAcademy
📁

File descriptors e I/O: o que todo processo compartilha

13 min de leitura·+65 XP

"Everything is a file" é o princípio unificador do Unix. File descriptors são handles inteiros que representam arquivos, sockets, pipes, dispositivos e até o próprio processo — todos acessados pelas mesmas syscalls read/write.

File descriptors: inteiros que representam tudo

# Todo processo começa com 3 fds abertos:
# 0 = stdin  (leitura)
# 1 = stdout (escrita)
# 2 = stderr (escrita)

import os

# Abrir arquivo retorna o próximo fd disponível:
fd = os.open("/etc/hostname", os.O_RDONLY)
print(fd)   # 3 (próximo após stderr)

# Ler usando o fd diretamente:
dados = os.read(fd, 256)
print(dados)

# Redirecionar stdout para arquivo (fd 1 aponta para o arquivo):
fd_arquivo = os.open("/tmp/saida.txt", os.O_WRONLY | os.O_CREAT, 0o644)
os.dup2(fd_arquivo, 1)      # fd 1 agora aponta para o arquivo
os.close(fd_arquivo)        # fechar o fd original (fd 1 ainda aponta)
print("Isso vai para o arquivo!")
os.dup2(2, 1)               # restaurar stdout apontando para stderr

# Verificar fds abertos do processo atual:
import os
pid = os.getpid()
# ls -la /proc/{pid}/fd
fd_dir = f"/proc/{pid}/fd"
for fd_name in os.listdir(fd_dir):
    try:
        alvo = os.readlink(f"{fd_dir}/{fd_name}")
        print(f"fd {fd_name} → {alvo}")
    except:
        pass
# fd 0 → /dev/pts/0   (terminal)
# fd 1 → /dev/pts/0   (terminal)
# fd 2 → /dev/pts/0   (terminal)
# fd 3 → /etc/hostname (arquivo aberto)

# Limite de fds por processo:
import resource
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
print(f"Max fds: {soft} (soft) / {hard} (hard)")   # tipicamente 1024/4096
# ulimit -n 65536  # aumentar para servidores com muitas conexões

Pipes: comunicação entre processos

import os, subprocess

# Pipe anônimo — comunicação pai-filho
r_fd, w_fd = os.pipe()   # retorna par (leitura, escrita)

pid = os.fork()
if pid == 0:
    # Filho: fecha fd de leitura, escreve no pipe
    os.close(r_fd)
    os.write(w_fd, b"mensagem do filho
")
    os.close(w_fd)
    os._exit(0)
else:
    # Pai: fecha fd de escrita, lê do pipe
    os.close(w_fd)
    dados = os.read(r_fd, 1024)
    os.close(r_fd)
    os.waitpid(pid, 0)
    print(f"Pai recebeu: {dados.decode()}")

# subprocess.Popen — shell pipes via Python:
proc = subprocess.Popen(
    ["ls", "-la"],
    stdout=subprocess.PIPE,    # cria pipe, conecta stdout do ls
    stderr=subprocess.PIPE
)
stdout, stderr = proc.communicate()
print(stdout.decode())

# Pipe nomeado (FIFO) — persiste no filesystem
os.mkfifo("/tmp/meu_pipe")
# Processo A: open("/tmp/meu_pipe", "w") — bloqueia até B abrir
# Processo B: open("/tmp/meu_pipe", "r") — ambos desbloqueiam

tudo é arquivo: exemplos práticos

O que pareceO que é no LinuxExemplo
Arquivo regularArquivo em filesystem/etc/hosts, main.py
DiretórioArquivo especial com entradas/home/user/
TerminalCharacter device/dev/tty, /dev/pts/0
DiscoBlock device/dev/sda, /dev/nvme0n1
Socket de redeFile descriptorTCP socket, Unix socket
PipeFile descriptorls | grep → pipe anônimo
Timertimerfd — file descriptor!epoll pode monitorar timers
Sinalsignalfd — file descriptor!Receber SIGTERM via read()
# /dev: dispositivos como arquivos
with open("/dev/urandom", "rb") as f:
    random_bytes = f.read(16)    # lê 16 bytes de ruído do hardware

# /dev/null: descarta tudo que escrever
import subprocess
subprocess.run(["ls"], stdout=open("/dev/null", "w"))  # silencia saída

# /proc: informações do kernel como arquivos
with open("/proc/cpuinfo") as f:
    cpu_info = f.read()    # kernel gera dinamicamente

with open("/proc/loadavg") as f:
    load = f.read()        # "0.45 0.32 0.28 1/234 12345"

# /sys: controle de hardware como arquivos
with open("/sys/class/thermal/thermal_zone0/temp") as f:
    temp_mc = int(f.read().strip())  # temperatura em milligraus
    print(f"CPU: {temp_mc / 1000:.1f}°C")
Modelo mental: file descriptor é um inteiro indexando a fd table do processo. Todos os I/Os (arquivos, rede, pipes) usam as mesmas syscalls read/write. dup2() implementa redirecionamento. Pipes são buffers do kernel com back-pressure natural. Limite de fds por processo deve ser aumentado para servidores com muitas conexões (ulimit -n).
💡
Próximo: I/O bloqueante vs não-bloqueante — o fundamento de event loops como asyncio e por que epoll escala para milhares de conexões.
🧩

Quiz rápido

3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo