8 pontos por GN⁺ 2023-07-24 | 1 comentários | Compartilhar no WhatsApp
  • Esta é a introdução de um projeto pessoal de estudo para entender, até o fim, o que realmente acontece quando um programa é executado em um computador
  • A pergunta central leva a questões como se um programa roda diretamente na CPU, como as chamadas de sistema funcionam e como vários programas são executados ao mesmo tempo
  • Fora das aulas universitárias, era difícil encontrar materiais abrangentes sobre sistemas, então foi necessário filtrar por conta própria materiais de qualidade variável e informações conflitantes
  • Depois de algumas semanas de pesquisa e de criar quase 40 páginas de anotações, passei a entender melhor o processo que vai da inicialização do computador até a execução de programas
  • Mesmo para leitores que acham que já sabem o assunto, pode haver algo novo a aprender; se estiver sem tempo, a recomendação é começar pelo capítulo 3

Ponto de partida para entender a execução de programas

  • O ponto de partida deste texto é exatamente o que acontece quando um programa é executado em um computador
  • Eu já tinha algum conhecimento de baixo nível, mas tinha dificuldade para conectar as várias peças em um único fluxo
  • A pergunta se desdobra em três direções
    • Se um programa realmente é executado diretamente na CPU
    • O que é uma chamada de sistema e como ela funciona na prática
    • Como vários programas são executados ao mesmo tempo

Processo de pesquisa e ordem de leitura

  • Como não há muitos materiais abrangentes sobre sistemas, foi necessário pesquisar diversas fontes diretamente, e a qualidade e o conteúdo variavam de um material para outro
  • Após algumas semanas de pesquisa e quase 40 páginas de anotações, passei a entender melhor o funcionamento do computador, desde o processo de inicialização até a execução de programas
  • O objetivo é criar eu mesmo um texto explicativo sólido, do tipo que eu queria ter encontrado
  • Para leitores que sentem que já conhecem o conteúdo ou estão sem tempo, a recomendação é ler primeiro o capítulo 3
  • O próximo texto continua em Capítulo 1: The “Basics”

1 comentários

 
GN⁺ 2023-07-24
Opiniões do Hacker News
  • Fui eu que fiz isto. Obrigado por lerem e, no estilo Hacker News, apontarem várias correções. Putting the "You" in CPU ainda está em pleno andamento e, originalmente, eu pretendia polir mais, adicionar conteúdo e postar no HN por volta da semana que vem
    Tenho 17 anos e, há um ano, larguei o ensino médio e comecei a trabalhar em tempo integral no Hack Club(https://hackclub.com/). Programo desde que me entendo por gente e, há cerca de 6 anos, comecei a estudar em casa para me concentrar mais em programação e outros interesses
    Aprendi tudo por conta própria, então nunca fiz uma disciplina universitária de sistemas, e não gostava da minha própria resposta para “o que acontece quando alguma coisa é executada?”. Então dediquei muito tempo para aprender o mais profundamente possível e, nesse processo, percebi que sistemas operacionais e hardware são muito divertidos de estudar, mas os materiais online sobre esse assunto muitas vezes são péssimos
    Ao pesquisar, eu frequentemente acabava chegando a PDFs de slides de aulas de 2014 ou a respostas do StackOverflow que, na prática, estavam erradas ou simplificadas demais. Então escrevi Putting the "You" in CPU na esperança de que fosse um material melhor para quem quer estudar essa área por conta própria. Ainda não está perfeito, porque preciso escrever mais alguns parágrafos sobre SMP, mas acho que é muito melhor do que a maioria dos materiais que vi. Também foi a primeira vez que fiz ilustrações e diagramas; eles foram melhorando nos capítulos finais, e tenho bastante orgulho de algumas imagens da parte final
    O material inteiro está open source no GitHub: https://github.com/hackclub/putting-the-you-in-cpu

    • Hoje em dia também penso com frequência que “esses materiais online são péssimos demais”. Especialmente quando procuro coisas como diagramas de blocos detalhados do kernel Linux, como tabelas de páginas funcionam, comparações de ABI de chamada de função por plataforma, detalhes de protocolos de rede de baixo nível ou números de PID reservados no Linux
      Às vezes encontro respostas em threads do Reddit, mas normalmente vêm com ruído, e também fico em dúvida se o Reddit é um lugar confiável para armazenar conhecimento. O StackOverflow é especialmente pouco útil, porque é voltado a perguntas para resolver tarefas específicas, então as respostas inevitavelmente são curtas
      Para conhecimento geral, agora eu pulo a busca quebrada do Google e vou direto à Wikipedia, mas a Wikipedia também tem limites quanto à profundidade com que pode cobrir temas técnicos. Acho que talvez precisemos de uma wiki que reúna de forma abrangente os detalhes da estrutura interna dos computadores. Há muito material espalhado, mas seria ótimo tê-lo em um só lugar
    • Eu também tinha perguntas parecidas nessa idade, e encontrei muitas respostas interessantes no código-fonte do Linux. Claro que, naquela época, ele era muito mais simples. Em especial, a comunidade osdev e a documentação de arquitetura de sistemas da Intel foram grandes pontos de partida; pelo que me lembro, as partes 3a ou 3b tinham bastante conteúdo valioso
      Essa documentação ensina muito mais sobre como o mundo poderia ser do que sobre como ele é hoje. Ao fazer OSDEV, você aprende muitas coisas interessantes, como a forma como processadores de 32 bits faziam endereçamento de 36 bits com PAE, ou como processadores de 64 bits faziam endereçamento de 52 bits para extrair mais dinheiro
      Você também entende por que um computador, para sair do modo de 16 bits e chegar ao long mode, precisa de acrobacias de assembly em nível de sistema, e por que o modo de 8 bits ainda existe. Se você gostou de aprender sobre carregamento de binários, ainda há muita coisa para se surpreender. Por exemplo, mesmo no modo de 64 bits ainda existe segmentação de memória, mas no long mode ela é forçada para um mapeamento plano
    • Excelente trabalho. É um bom material para começar em desenvolvimento de sistemas operacionais; tem muitos detalhes, mas é fácil de digerir. Seria bom ajustar um pouco o fim da nota #2 do capítulo 2. Ela deveria refletir que o paradigma assíncrono de algumas linguagens de programação é multitarefa cooperativa. Dentro de um processo, usar multitarefa cooperativa costuma ser considerado seguro, mas se isso é desejável é outra questão. Pelo que sei, Erlang mantém preempção em nome da robustez
  • Gosto desse tipo de material. Ultimamente tenho entrado no mundo RISC-V, e parece claro que pessoas que querem impulsionar software livre e open source precisam desse tipo de conhecimento para fazer o software rodar bem nos vários computadores de placa única que estão surgindo

    • Como observação, a codificação de instruções RISC-V é uma das coisas mais esquisitas que dá para explorar, talvez com exceção de Thumb-2. Houve bons motivos para ela ser feita assim, mas, se você pretende ler dumps hexadecimais, o RISC-V não vai facilitar sua vida. Mesmo que, da maioria dos outros pontos de vista, ele seja simples
    • Se a pessoa estudou em nível de graduação ou acima, imagino que programadores em geral já saibam esse tipo de coisa. Pelo menos se não tiverem pulado as disciplinas de arquitetura de computadores e sistemas operacionais
      No meu caso, no processo de aprender como computadores funcionam, era comum definir uma arquitetura de conjunto de instruções, implementar um simulador e um assembler, e escrever assembly diretamente. Quem era mais dedicado chegava a implementar em FPGA ou escrever um backend para LLVM
      Em sistemas operacionais é a mesma coisa. Implementar um kernel para aprender, ou ao menos modificar um kernel existente, é um caminho bastante comum
  • Se você quer saber mais sobre como computadores funcionam em um nível bem fundamental, recomendo muito o livro Code, de Charles Petzold. Ele começa pelos primeiros princípios e sobe até um nível bastante alto

    • Se você prefere aprender construindo por conta própria, Nandgame https://nandgame.com também é bom
  • Uma das coisas que este material faz bem é apontar as expectativas e os resultados reais de aprendizado durante o processo de reunir informações. Algo como “eu esperava que fosse x por causa de y, mas na verdade era a por causa de b!”
    Desfazer mal-entendidos muitas vezes é uma etapa essencial para formar novo conhecimento, e ajuda quando o autor reconhece que, ao processar as informações, o leitor precisa superar os próprios equívocos. É muito mais amigável ao leitor do que uma postura do tipo “esta é a resposta certa, e se você pensou diferente é burro”

  • No capítulo 6, a explicação do valor de retorno de fork() está errada. Ao contrário do que está escrito, no pai deve ser retornado o PID do filho, e no filho deve ser retornado 0. Criei um pull request para essa parte

    • Muito obrigado mesmo, eu errei. Corrigi o exemplo de código e fiz o merge
  • Na explicação de que add eax, 512 é traduzido para 05 00 02 00 00, diz-se que o primeiro byte, 05, é o opcode que soma um número de 16 bits ao registrador EAX, e que os bytes restantes são 512 (0x200) em little-endian
    Mas essa explicação cobre apenas os 3 primeiros bytes de uma instrução de 5 bytes. Fico curioso sobre o que são os 00 restantes. Talvez a intenção fosse escrever que o opcode soma um número de 32 bits ao EAX?

    • Não é “he”, é “she”, e sim, está certo. Acho que naquela parte deveria ter sido escrito 32 bits. Boa observação
    • Se estamos falando de x86, está correto. O registrador eax tem largura de 32 bits, e os 16 bits inferiores do mesmo registrador são chamados de ax
  • É um bom texto. Como desenvolvedor web autodidata, sinto que materiais assim são muito valiosos. Ajudam a sair de várias abstrações e entender de forma mais concreta como as coisas realmente funcionam

  • Parece um bom material, entra no ponto principal bem rápido e é até um pouco divertido. É menos parecido com um “tutorial de mônadas” do que eu esperava. Pretendo ler mais

    • Fico feliz que tenha gostado. Se tiver feedback depois, seria ótimo saber
  • É um bom material. Mas ainda fico com uma dúvida sobre o kernel. Em [0], é explicado que o shebang é tratado pelo kernel e, como ele é obtido de buf sem carregar o arquivo inteiro, sempre é truncado no tamanho de buf
    Há 4 anos, alguém teria se irritado com o kernel truncando caminhos com mais de 128 caracteres e dobrou o tamanho do buffer, dobrando também o ponto de truncamento. Assim, hoje no Linux, se a linha de shebang passa de 256 caracteres, tudo depois dos 256 desaparece completamente
    Não entendo por que o caminho de arquivo de alguém seria truncado silenciosamente. O kernel, de todos os lugares, não deveria fazer isso. Um caminho de arquivo de 256 bytes pode não fazer sentido, mas uma string de caminho+argumentos de 256 bytes quase certamente vai existir em algum momento. Quebrar scripts silenciosamente é errado
    [0]: https://cpu.land/how-to-run-a-program

    • Como autora, concordo fortemente. Não deveria ser necessário entender a lógica de execução do kernel para compreender os limites do shebang. Isso é um projeto tolo, e acho que talvez eu devesse ter enfatizado mais esse ponto
      Alguns anos atrás, alguém tentou aplicar um patch no kernel para que shebangs longos demais ao menos falhassem de forma explícita, em vez de silenciosamente. Mas isso causou problemas no NixOS. Como é típico do Nix, ele já usava shebangs excessivamente longos com muitos caminhos longos do nix-store. Antes, eram truncados silenciosamente, mas ainda funcionavam; de repente, todos esses scripts passaram a falhar, quebrando a compatibilidade retroativa. Por isso o patch teve de ser revertido, e desde então mudanças parecidas não apareceram
      Para mais detalhes, veja https://lwn.net/Articles/779997/
    • Não se deve colocar argumentos complexos no shebang. O shebang existe para indicar a localização do interpretador do arquivo atual
      O próprio shebang nem consegue lidar corretamente com mais de um argumento. Se você escrever #!/bin/program -args somescript, isso será separado como ['/bin/program','-args somescript'], o que quase sempre não é o resultado desejado. Isso é específico do Linux, e shebangs também não são universalmente portáveis em todos os Unix
      Se quiser fazer algo complexo, é melhor usar #!/bin/sh e, no arquivo atual, fazer exec com os argumentos necessários
      Um ponto que faltou no link é que dá para usar env para encontrar o interpretador no caminho atual. Por exemplo, #!/usr/bin/env python3 encontra o python3 usado pelo usuário sem precisar saber sua localização
      Isso pode ser útil para scripts que serão executados em ambientes virtuais, que funcionam sobrescrevendo o PATH para o interpretador Python. No entanto, não se deve fazer isso em programas instalados. Como um programa instalado não deve permitir que o usuário controle qual interpretador será usado, deve-se usar um caminho absoluto
      O env tem a opção -S, que permite analisar uma linha de comando a partir de uma única string para lidar com essa limitação. Ainda assim, para quem passou anos evitando malabarismos em linhas de shebang, isso parece, no máximo, uma prática suspeita
    • O mais surpreendente é que o buffer não tenha sido definido para ser igual ao tamanho de bloco do disco. Normalmente é 512 bytes, mas nem sempre