Arquivos Executáveis no Linux: memória, heap, pilha e erros clássicos
Aula 41 · GNU/Linux para Servidores
Descrição
Explore a estrutura de memória dos arquivos executáveis no Linux: como programas saem do armazenamento e são carregados na memória principal, seguindo a arquitetura de Von Neumann. Entenda a diferença entre executáveis e arquivos de dados, e como o Sistema Operacional organiza a memória em segmentos de código, dados estáticos, heap e pilha (stack). Aprenda como a heap cresce ao alocar variáveis e como a stack empilha chamadas de funções — e o que acontece quando essas áreas colidem. Conheça dois erros clássicos da computação: Stack Overflow (estouro de pilha) e Out of Memory (sem memória para alocação). Referências aos livros de Tanenbaum (Sistemas Operacionais Modernos) e Stallings (Arquitetura e Organização de Computadores).
Transcrição do áudio
O coleguinha é a segunda vez que eu gravo esse vídeo aqui, cara. Mirimesh, eu vou clicar ali no OBS para ver se ele não falhou, tá? Vamos lá. Então, nós temos, naturalmente, já expliquei a questão que nós temos tipos de arquivos do Linux, especiais de bloco, de caracter, assim, e os regulares. E aí, expliquei para vocês que os regulares, dois tipos. Os que são repositórios de dados, como um TXT, um .doc, um .xlsx, que é do Excel, um .pdf, não é? São arquivos que são repositórios de dados. Programas consomem esses repositórios de dados, esses arquivos, e programas criam esses arquivos de dados que eles podem consumir. Assim, afinal, você escreve um .txt, joga num pendrive, vai para sua casa e abre o .txt na sua casa. Repare, você está usando o .txt para a comunicação entre dois processos. Legal? E nós temos os arquivos executáveis, que são aqueles que são feitos e projetados para serem processados que têm instrução e dados. Lembra de Von Neumann, cara, nesse momento, né? Você tem lá uma memória, você tem as instruções e os dados para a instrução. Legal? Lembre-se de Von Neumann. Beleza. Vamos lá. Nesse ponto agora, então, vai nos interessar, então, um tipo, e é o arquivo executável. Esse arquivo executável é um binário preparado e projetado. Preste atenção, o script não é binário, o script não é um executável, tá? Executáveis são preparados e projetados para a máquina compreender e, então, executar as instruções. Mas o script é interpretado por um shell, ou ele é interpretado por algum programa. Beleza? Vamos lá. Então, nós temos aqui, naturalmente, o programa ele é executado, né? Então, ele sai da memória secundária de armazenamento e vai para a memória principal. Só que, naturalmente, quando isso ocorre, é montado toda uma estrutura para que ocorre o processamento. Não é isso. E aí o CPU, então, ele faz o processamento. Legal? Bom, olha só. Já um arquivo de dados, ele é apenas uma coleção de dados, né? Todo um mapa. Legal? Bom, tudo está na RAM. E tudo tem que ir para a memória principal. Tudo vai para a memória principal. Isso foi definido na nossa arquitetura lá na década de 40, 50, do século passado, que estamos mantendo isso até hoje. E teria que, como eu posso dizer, romper um conceito muito consolidado para que isso não ocorresse. Por isso que você tem que copiar da memória secundária para a memória principal e executar a partir da memória principal. Lembre-se do top quo hierarquia de memórias lá do livro do Stallings de AOC, a arquitetura e organização de computadores. Dá uma olhada lá naquele capítulo. Legal? Galera, eu conheço todos esses livros. Eu conhecimento já alcança esses livros básicos, todinhos, e eu consigo relacionar eles e gerar mais conhecimento a partir de conhecimento. Isso é uma dádiva que o ser humano recebeu. Legal? Então, leia livros também. Maximize sua capacidade. Então, estando na memória principal, então o CPU pode, naturalmente, fazer as operações sobre o programa. E esses arquivos, então, naturalmente, executáveis, eu posso chamar ele de qualquer lugar do meu sistema de arquivos. Então, vamos aqui imaginar aqui que eu estou aqui e aí eu tenho um programa aqui onde eu estou, então, ponto barra meu programa. Certo? Só que eu tenho que estar no diretório. Legal? Alguns reparem que eu não digito, né? O SER, SERBIN, CP para executar o comando CP. Eu já faço CP direto, né? Porque o Linux, assim como todo sistema operacional, ele tem os diretórios onde ficam os programas, que são programas do sistema, do usuário. Ele fica, geralmente, no BIN, SBIN, o SERBIN, o SRSBIN, localBIN, barraHOME, barraUsoário, barra, ponto barra local, ponto... Não, não, pera aí. Deixa eu mostrar para vocês o do usuário, né? O do usuário fica aqui, ó. CD.localBIN, tá? PWD. É, o outro local também que você encontra programas, tá? Aqui são os programas desse usuário específico. Aqui que você coloca. Beleza? Bom, e essa estrutura que é colocada na memória, assim, tá? É um texto ou código ou programa, depende do nome, depende do livro que na base, tá? Sempre vai estar ali na base. E daqui começa o endereço da primeira instrução e da última instrução que vai ser executado, tá? Tá aqui. Essa parte azul clara aqui. Beleza? Aqui você vai encontrar variáveis globais e dados estáticos. Aqui tudo que é estático e global fica aqui. Bom, logo em seguida você tem duas áreas. Em muitos livros, a pilha é descrita como em cima e a rípa embaixo. Alguns livros a rípe é descrita em cima e a pilha embaixo. Cara, não importa. Não importa. Não importa. Por quê? O que importa é o que eu vou te explicar agora. Sempre entre a rípe e a pilha tem um espaço vago aqui, tá? Tem um grande espaço vago aqui. Beleza? O que que é a rípe? A rípe é uma área de memória que adicionamos variáveis. Então, se aqui no meu programa eu clima variável nova, ela é colocada aqui dentro dessa área. Se aqui dentro do meu programa eu coloco dados numa variável e eu preencho dados nessa rípe. Beleza? Tá. E a pilha, a stack. Sempre que no meu programa eu chamo uma função ou eu chamo um método, eu empilho aqui, tá? Então, eu chamo uma função, eu empilho. Tenta a função, chamo outra função, eu empilho. Eu empilho, eu empilho. E, naturalmente, eu vai crescendo assim e a rípe vem crescendo assim. E elas vão se encontrar o momento. E isso acontece. Mais uma coisa que eu não te falei ainda é que você nunca consegue fazer duas coisas ao mesmo tempo, por exemplo. É criar duas variáveis, criar uma variável e uma pilha ao mesmo tempo, criar uma pilha e preencher uma variável ao mesmo tempo. Cria duas pilhas ao mesmo tempo. Sempre é uma operação por rodada, tipo um RPG. Certo? Legal. Então essa área vai se enxendo e a área vazia vai acabando. Certo? Se você ler o livro do TaniBolt, Sistema de Operacionais Modernos, no capítulo de memória, ele fala sobre paginação de memória. E ele fala sobre paginação virtual. Essa área vazia, ela pode ganhar mais espaço. Desde que não cresça as coisas aqui, formam muito rápido. No tipo, você leu um dump de um banco de dados e joga uma variável. O deu. Entendeu? A RIP vai crescer e vai colidir com a pilha, naturalmente. Ou que você não faça uma recursividade maluca, de tal forma que vai crescendo, crescendo, crescendo, até encostalar sem nenhum ponto de parada, né? Uma recursividade, sem ponto de parada. Então preste atenção. Então se tudo ter tempo ao tempo, o próprio Sistema Operacional vai conseguir aumentar e expandir essa área de memória aqui. Vai expandir. Aos poucos. Tem que ler o capítulo de memória do livro do TaniBolt. Beleza? Vamos supor então que não existe espaço nenhum. E eu falei para vocês que ele não executa duas coisas ao mesmo tempo. Tipo, uma RIP e uma pilha ao mesmo tempo. Ou é um ou é o outro. Então não tem espaço nenhum. E se eu chamo uma nova função aqui dentro do código, vou tentar empilhar algo, uma função nova, não ter espaço. Então vai dar o erro clássico da computação chamada Stack Overflow. Stack Overflow. Vamos dar uma olhada se eu estou realmente gravando. Está gravando aí, né, cara? Cara, complicado, hein? T. Eu não tenho espaço nenhum, mas eu vou criar uma nova variável. Vai dar um Out of Memory. São dois erros clássicos da computação. A computação possui quatro erros clássicos da computação. Então quem? Stack Overflow e Out of Memory são dois erros clássicos da computação. Todo cara que estudou compiladores, todo cara que pegou esse livro aqui, foi lá, leu esse livro aqui. É, copia isso aqui, vai lá, beleza. A RUMBRAUSER. Cara, todo cara que leu esse livro aqui, ele sabe que esse livro aqui. Referência mundial no assunto. Esse aqui é o tânimo do tânimo dos compiladores. É o data do data dos compiladores, tá? Tranquilo? É o Stalin dos compiladores. Dos compiladores, tá? Legal. Então nós temos os binários, nós temos os estes ezês. E aí nós já vimos o código fonte com o MEN. Então para ser executável, ele tem que estar em linguagem de máquina praticamente. Está lá, ser compilado, bonitinho. A linguagem que é máquina, compreenda. E também nós vimos que ele pode ser chamado como um programa porque ele tem um ponto de entrada MEN. Bom, nós vimos também essa questão que nós temos DLLs, que são bibliotecas que não possuem o ponto de entrada MEN. Eles não podem ser executados, mas eles podem ser carregados por um executável. Beleza? Vimos até então essa parte de programação que já fizemos alguns programas aqui nesse material. Legal? Então vou ficar por aqui, próximo vídeo, compiladores. Espero que tenha gravado essa vez.