College Online
0%

Conceitos de Arquivos

Módulo 5 · Aula 1 ~15 min de leitura Nível: Intermediário

O que é um Arquivo?

Um arquivo é a abstração do SO para dados persistentes. Enquanto processos e memória são efêmeros (desaparecem quando o computador desliga), arquivos persistem. O SO esconde a complexidade do hardware de armazenamento (setores, trilhas, blocos) por trás de uma interface simples: nome, conteúdo, metadados.

No Unix, a filosofia é levada ao extremo: "tudo é arquivo". Dispositivos, pipes, sockets, e até informações do kernel (/proc) são expostos como arquivos.

Atributos de um Arquivo

Shell — Atributos de arquivo
# stat mostra todos os atributos (metadados)
$ stat /etc/hostname
  File: /etc/hostname
  Size: 12          Blocks: 8          IO Block: 4096   regular file
Device: 8,1         Inode: 262146      Links: 1
Access: (0644/-rw-r--r--)  Uid: (0/root)   Gid: (0/root)
Access: 2026-05-08 10:00:00   # último acesso (atime)
Modify: 2026-01-15 08:30:00   # última modificação do conteúdo (mtime)
Change: 2026-01-15 08:30:00   # última modificação de metadados (ctime)
 Birth: 2025-06-01 12:00:00   # criação (btime, nem todos FS suportam)

# Atributos principais:
# - Nome: hostname
# - Tipo: regular file (pode ser directory, symlink, device, etc.)
# - Tamanho: 12 bytes
# - Inode: 262146 (identificador único no filesystem)
# - Permissões: 0644 (rw-r--r--)
# - Dono: root:root (UID:GID)
# - Timestamps: atime, mtime, ctime

Operações sobre Arquivos

C — Operações básicas com system calls
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    // CREATE + OPEN: retorna file descriptor (int)
    int fd = open("teste.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    // O_CREAT: cria se não existe
    // O_WRONLY: somente escrita
    // O_TRUNC: trunca se já existe
    // 0644: permissões rw-r--r--

    // WRITE: escreve bytes
    char *msg = "Hello, OS!\n";
    write(fd, msg, strlen(msg));

    // SEEK: move o ponteiro de leitura/escrita
    lseek(fd, 0, SEEK_SET);  // volta ao início

    // CLOSE: libera o file descriptor
    close(fd);

    // OPEN para leitura
    fd = open("teste.txt", O_RDONLY);

    // READ: lê bytes
    char buf[256];
    ssize_t n = read(fd, buf, sizeof(buf) - 1);
    buf[n] = '\0';
    printf("Lido: %s", buf);

    close(fd);

    // DELETE
    unlink("teste.txt");

    return 0;
}

Métodos de Acesso

Métodos de Acesso a Arquivos
Acesso Sequencial:
  Leitura/escrita avança automaticamente.
  read() lê os próximos N bytes, ponteiro avança.
  Ideal para: logs, streams, processamento de texto.
  |--->|--->|--->|--->|--->|  (sempre para frente)

Acesso Direto (Random):
  Pode ler/escrever qualquer posição com lseek().
  Ideal para: bancos de dados, arquivos indexados.
  |    |    |    |    |    |
       ^         ^    ^       (qualquer posição)
       lseek(5)  lseek(15)

Acesso Indexado:
  Um índice separado aponta para posições no arquivo.
  O SO lê o índice primeiro, depois vai direto ao dado.
  Ideal para: grandes bancos de dados, arquivos ISAM.

  Índice:           Arquivo:
  [chave:pos]       [              dados              ]
  ["ana": 0]   -->  [dados_ana|dados_bob|dados_carlos]
  ["bob": 100] -->       ^
  ["carlos":200]         |
                    lseek(100)
i
File descriptors No Unix, todo arquivo aberto é representado por um file descriptor (um inteiro). Processos nascem com 3 FDs: 0 (stdin), 1 (stdout), 2 (stderr). O open() retorna o próximo FD livre. Como "tudo é arquivo", sockets de rede, pipes, e devices também são FDs.

Tipos de Arquivo no Unix

Shell — Tipos de arquivo
$ ls -la /dev/ /tmp/ /etc/ | head -20

# Tipo indicado pelo primeiro caractere de ls -l:
# -  arquivo regular       (texto, binário, imagem)
# d  diretório             (contém referências a outros arquivos)
# l  symbolic link         (ponteiro para outro arquivo)
# c  character device      (/dev/tty, /dev/null)
# b  block device          (/dev/sda, /dev/nvme0n1)
# p  named pipe (FIFO)     (comunicação entre processos)
# s  socket                (comunicação de rede local)

# Exemplos práticos:
$ file /bin/ls             # ELF 64-bit executable
$ file /etc/hostname       # ASCII text
$ file /dev/null           # character special
$ file /dev/sda            # block special

No harness.os

O Scale 1 do harness.os é literalmente um sistema de arquivos. A estrutura de diretórios de um projeto é o harness mais básico:

Diagrama — Scale 1 como Filesystem
Scale 1 Harness = Filesystem Design
====================================

projeto/
+-- CLAUDE.md              = /etc/init.d (config de boot)
|                             Lido no início de cada sessão.
|
+-- .claude/
|   +-- settings.json      = /etc/sysctl.conf (config do kernel)
|
+-- rules/                 = /etc/rules.d/ (regras de comportamento)
|   +-- commit-hygiene.md  = regra específica
|   +-- testing.md         = regra específica
|
+-- knowledge/             = /usr/share/doc/ (dados de referência)
|   +-- architecture.md    = documentação carregável
|   +-- api-reference.md   = documentação carregável
|
+-- workflows/             = /usr/bin/ (scripts executáveis)
    +-- deploy.md          = processo passo-a-passo
    +-- qa-gate.md         = processo passo-a-passo

Operações de arquivo no harness:
  open()  = start_session() lê CLAUDE.md
  read()  = get_knowledge() lê um arquivo de knowledge
  write() = log_learning() cria/atualiza um arquivo
  stat()  = list_knowledge() lista metadados
  unlink()= (raro) remover knowledge obsoleto

Método de acesso:
  Scale 1 = acesso sequencial (agente lê CLAUDE.md inteiro)
  Scale 2 = acesso indexado (MCP query por concern/domain)
Python — Harness como filesystem
import os

class HarnessFS:
    """Scale 1 harness como operações de filesystem."""

    def __init__(self, project_root):
        self.root = project_root

    def open_config(self):
        """open() + read() do CLAUDE.md"""
        path = os.path.join(self.root, "CLAUDE.md")
        with open(path) as f:
            return f.read()

    def list_knowledge(self):
        """readdir() do diretório knowledge/"""
        kdir = os.path.join(self.root, "knowledge")
        return os.listdir(kdir) if os.path.exists(kdir) else []

    def read_knowledge(self, name):
        """open() + read() de um knowledge chunk"""
        path = os.path.join(self.root, "knowledge", name)
        with open(path) as f:
            return f.read()

    def write_learning(self, name, content):
        """open() + write() para um novo learning"""
        path = os.path.join(self.root, "knowledge", name)
        with open(path, "w") as f:
            f.write(content)

A transição de Scale 1 (arquivos) para Scale 2 (MCP + banco) é equivalente à transição de acesso sequencial para acesso indexado. No Scale 1, o agente lê o CLAUDE.md inteiro (sequencial). No Scale 2, consulta por concern ou domain (indexado, muito mais eficiente).

Homework

  1. Escreva um programa em C que cria um arquivo, escreve 100 bytes, faz lseek() para a posição 50, lê 10 bytes, e imprime. Observe a diferença entre acesso sequencial e random.
  2. Liste todos os tipos de arquivo em /dev/ da sua máquina usando ls -la /dev/ | cut -c1 | sort | uniq -c.
  3. No harness.os, desenhe a estrutura de diretórios ideal para um novo projeto usando Scale 1 (apenas arquivos, sem banco). Que arquivos você criaria em rules/, knowledge/, e workflows/?

Resumo

Verifique seu entendimento

Quais são os três file descriptors que todo processo Unix herda automaticamente ao nascer?

  • 0 (stdin), 1 (stdout), 2 (stderr)
  • 0 (read), 1 (write), 2 (execute)
  • 0 (root), 1 (home), 2 (tmp)
  • 0 (code), 1 (data), 2 (stack)