📁
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õesPipes: 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 desbloqueiamtudo é arquivo: exemplos práticos
| O que parece | O que é no Linux | Exemplo |
|---|---|---|
| Arquivo regular | Arquivo em filesystem | /etc/hosts, main.py |
| Diretório | Arquivo especial com entradas | /home/user/ |
| Terminal | Character device | /dev/tty, /dev/pts/0 |
| Disco | Block device | /dev/sda, /dev/nvme0n1 |
| Socket de rede | File descriptor | TCP socket, Unix socket |
| Pipe | File descriptor | ls | grep → pipe anônimo |
| Timer | timerfd — file descriptor! | epoll pode monitorar timers |
| Sinal | signalfd — 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