Criptografando HD/HD externo/Pendrive

Já comentei em vários textos anteriores ferramentas que utilizo para o meu backup e chegou a vez da criptografia, processo que precisei refazer quando chegou meu novo HD externo. Na criptografia temos duas opções, a primeira é criptografar o arquivo, que depende do suporte do programa, ou que o mesmo seja descriptografado toda vez que for utilizado e criptografado novamente no final do processo. A segunda opção é criptografar o sistema de arquivos, que depende do suporte do sistema operacional, porém é transparente para os programas e os processos de criptografar e descriptografar é automático.

Minha opção foi criptografar o sistema de arquivos, e utilizar o recurso chamado dm-crypt, que no Debian existe um programa de configuração chamado cryptsetup, podendo ser instalado com um apt-get install cryptsetup.

Como este método de criptografia é baseado no sistema de arquivos, o particionamento ocorre da mesma forma, porém em vez de criar um sistema de arquivos Ext4, criei uma partição com o LUKS (Linux Unified Key Setup). Para explicar os comandos e quem tiver vontade poder executá-los também, em vez de criptografar uma partição, farei isso com um arquivo, já que em sistemas Unix tudo é arquivo, porém vale lembrar que todos os dados do arquivo ou partição serão perdidos.

Primeiro, para este exemplo, precisamos de um arquivo grande e existe uma forma de gerá-lo com o comando dd, exemplo: dd if=/dev/zero of=/tmp/arquivo bs=1M count=1024, se for fazer o procedimento numa partição, este comando não é necessário e troque sempre que vir o /tmp/arquivo pelo caminho da partição, exemeplo: /dev/sdb2. O parâmetro if=/dev/zero é um arquivo especial que possui zeros infinitos, então toda vez que for lido retornará “0”. O of=/tmp/arquivo é o caminho do arquivo que criarei, se for utilizado o caminho de um HD como /dev/sdb ou partição como /dev/sdb2 toda esta área será preenchida com zeros. Os parâmetros bs=1M e count=1024 representam o tamanho do bloco que será copiado e quantas vezes esse processo será repetido respectivamente, ou seja, serão copiados 1 MB 1024 vezes, gerando um arquivo de 1 GB em branco no HD.

Para formatarmos com o LUKS, podemos utilizar o comando cryptsetup luksFormat /tmp/arquivo e digitando a senha, lembrando que todos os dados do arquivo ou partição serão perdidos. Para conseguirmos utilizar a partição criptografada é preciso abri-la primeiro com o comando cryptsetup open /tmp/arquivo arquivo1, o primeiro é o caminho do arquivo o segundo é um nome para o mapeamento que será criado em /dev/mapper.

Próximo passo é a formatação do sistema de arquivos, porém em vez de formatar a partição diretamente, é preciso usar o mapeamento, no caso /dev/mapper/arquivo1. Um exemplo de formatação com Ext4 é mkfs.ext4 /dev/mapper/arquivo1. Agora podemos montar este mapeamento e gravar os arquivos normalmente que tudo salvo nesta partição será criptografada.

Na hora de remover a partição, basta fazer o procedimento na ordem inversa, primeiro desmonte o sistema de arquivo do /dev/mapper/arquivo1. Depois feche o mapeamento com o comando cryptsetup close /dev/mapper/arquivo1. Para abrir a partição novamente basta abrir o mapeamento e depois montar a partição.

Uma coisa legal do LUKS é que ele permite o uso de até 8 chaves diferentes, para adicionar uma chave nova basta executar o comando cryptsetup luksAddKey /tmp/arquivo, e agora qualquer uma das duas senhas poderão ser utilizadas para acessar a partição. Além de senhas também é possível utilizar um arquivo como chave o que pode ser um recurso interessante também. Caso deseje ver mais informações a respeito do LUKS ou chaves desta partição também é possível executar o comando cryptsetup luksDump /tmp/arquivo, um exemplo de saída é:

LUKS header information for /tmp/arquivo

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha1
Payload offset: 4096
MK bits:        256
MK digest:      81 e6 1f 37 b2 5c 8d 97 a4 a9 57 c1 2b 1b 89 49 35 0e 59 8f 
MK salt:        3b e4 4b bc 0d 09 b1 2a 5c 38 ea e2 a1 67 e6 5e 
                39 97 05 1d d5 73 cf 33 da 08 e1 bc 73 9f d5 95 
MK iterations:  146750
UUID:           2013f2e3-1d73-429a-818d-b4a182d8658e

Key Slot 0: ENABLED
        Iterations:             587155
        Salt:                   23 e7 e8 73 a0 a5 a9 58 a6 58 ba ec 81 41 49 d1 
                                75 a8 51 89 99 fb 8d a9 87 cc e7 23 eb bf 44 cb 
        Key material offset:    8
        AF stripes:             4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

Para quem formatou uma partição em vez do arquivo, o navegador de arquivos pode simplificar o processo de acesso a partição, no meu caso no Thunar consigo acessar meu HD externo da mesma forma como se o mesmo não estivesse criptografado clicando no ícone do mesmo, porém aparece uma janela solicitando a chave de acesso.

Para quem quiser mais informações, ou desejar criptografar partições do sistema, recomendo dar uma olhada no texto da wiki do Arch Linux, que apesar de estar na wiki de uma distribuição específica, os programas são os mesmos para todas e a lógica é a mesma, só confirmar se os parâmetros da wiki existem na versão instalada no seu sistema, que as vezes um dos dois podem estar uma versão mais antiga.

Anúncios

Remover arquivos duplicados

Há algum tempo, escrevi sobre o método que utilizo para fazer backup do meu hd externo. Porém quando comecei a utilizá-lo, tinha pouco mais de 500 GB de arquivos e muitos backups antigos, diretórios que quando iria formatava o computador, simplesmente olhava que faltava fazer backup de um diretório apenas, copiava-o inteiro, recuperar os arquivos mais importantes ou utilizados, enquanto os demais ficavam perdidos e ocupando espaço, mesmo já vindo de outro backup.

A questão é que eu tinha, pelo menos, uns seis backups, envolvendo formatação e cópias de pendrivers. Muitos desses arquivos estavam repetidos, significando que eu poderia deixar apenas uma cópia, apagando as demais e mesmo assim ter todos os meus arquivos.

O primeiro método que pensei foi gerar hash de todos os arquivos, e depois compará-los para encontrar duplicados, porém gerar hash de 500 GB é extremamente demorado e muitos desses arquivos não sobrem alterações, então não seria necessário recalcular toda execução. Olhando no sistema de arquivos, temos a informação de última alteração no arquivo, então bastava gerar um cache com informações de arquivo, hash e data da última alteração, toda vez que o for executado, poderia comparar primeiro a data de alteração e calcular o hash apenas se necessário.

Com o script pronto, a primeira execução foi demorada, uma vez que teria que calcular o hash dos 500 GB, levando algumas horas, encontrando 5 GB de arquivos que poderiam ser apagados sem que eu perdesse qualquer informação. Uma próxima execução foi muito mais rápida, não levando mais de dois minutos, desde que com modificações em arquivos pequenos.

Consegui logo apagar uns 2 ou 3 GB, porém a lista de arquivos duplicados, apesar de auxiliar no processo, não era algo muito prática, uma vez que teria que buscar o arquivo manualmente para apagá-lo. Com arquivos grandes o processo ia rápido, e liberava mais espaço, porém em arquivos de texto puro não dava um rendimento satisfatório. Como sou programador web, logo montei uma página para listar esses arquivos, com um checkbox para selecionar os arquivos que desejava apagar, deixando o processo todo muito mais prático. Hoje tenho menos de 5 MB de pequenos arquivos duplicados, que comparados aos 5 GB representa menos de 0,1%.

Reorganizei todo o código, aproveitando para montar uma interface web mais completa, e publiquei o código no github sobre licença MIT (https://github.com/eduardoklosowski/deduplicated), então quem quiser dar uma olhada, utilizar, ou até contribuir com o desenvolvimento do mesmo fique à vontade.

Uma explicação rápida para quem deseja utilizá-lo. Por ser escrito em Python, recomendo a instalação via pip com o comando pip install git+https://github.com/eduardoklosowski/deduplicated.git ou pip install git+https://github.com/eduardoklosowski/deduplicated.git#egg=deduplicated[web] caso deseje instalar as dependências da interface web. Com isto você terá o comando deduplicated, bastando utilizar os parâmetros update para atualizar o cache dos arquivos, duplicated para listar os arquivos duplicados ou check para atualizar o cache e exibir os arquivos duplicados, seguido de um ou mais diretórios que deseja verificar, exemplo deduplicated check /home/eduardo. Caso tenha instalado a interface web, basta executar o comando deduplicated-web e abrir o endereço http://127.0.0.1:5050/.

Também existe uma opção para verificar se um arquivo está no cache com o comando indir, exemplo deduplicated indir meuarquivo.txt /home/eduardo. A vantagem é que você não precisa ter os arquivos para fazer essa verificação, eu verificava se os arquivos do meu notebook estavam no hd externo desta forma, sem precisar estar com ele ligado.

Recentemente tive um problema com o meu hd externo, essa história está no hack ‘n’ cast (quando for publicado disponibilizo o link aqui). Como eu tinha o cache do meu hd externo, pude compará-lo com o meu backup, descobrindo o que não estava atualizado e se eu tinha perdido algo. Esse procedimento se resumiu a executar as funções de atualização do cache na manualmente para adaptar certas partes e listar determinados valores. Caso alguém deseje posso até montar o script como uma função extra.

Eu fiquei extremamente feliz ao conseguir economizar o espaço do meu hd externo, o que já valeu o programa. Quando tive problemas no hd externo, percebi que ter as coisas organizadas e automatizadas, podendo consultar alguns logs, torna tudo mais fácil e tranquilo, bastando efetuar o RMA e depois executar um rsync para resolver todo o problema, obviamente teve a parte de formatação e criptografia também.

Backup HD Externo

Hoje vou compartilhar como faço meu backup com vocês. Comprei um HD externo a algum tempo e todos meus dados que quero guardar, gravo no mesmo, porém se ocorresse alguma falha, perderia todas as minhas informações, então precisava de uma cópia de segurança. Como ainda tenho o meu primeiro computador, o qual não utilizo mais, porém ainda funciona perfeitamente, além de um HD razoável, decidi utilizar o mesmo como backup. Agora o desafio foi como fazer e manter uma cópia atualiza dos arquivos, uma vez que o computador de backup não está ligado todo o tempo, não depender de procedimentos manuais ou fazer a cópia integral dos arquivos toda vez que demoraria muito.

A solução foi extremamente simples, no GNU/Linux temos um programa chamado “rsync”, que é uma ferramenta para fazer cópia de arquivos, a parte interessante é justamente por fazer todo esse controle que eu preciso, a primeira vez que sincronizar, todos os dados serão copiados, nas próximas, apenas as diferenças (arquivos alterados, adicionados e excluídos) serão transmitidas. Outra vantagem é que ele funciona via rede, então posso utilizar o meu HD externo no meu notebook e utilizar o SSH para fazer o sincronismo.

Na prática o funcionamento foi assim, com os HDs devidamente funcionando e acessíveis pelo sistema operacional, rsync instalado no notebook e computador (apt-get install rsync no Debian ou Ubuntu), copiei para o HD externo meus arquivos e executei rsync -av --progress --delete --exclude lost+found /media/HDEXTERNO user@192.168.0.100:/media/HDVELHO/(reparem na barra no final do diretório de destino, ela é importante). Com esse comando mando o meu notebook fazer uma cópia de todo o conteúdo do HD externo (/media/HDEXTERNO) para o diretório /media/HDVELHO/HDEXTERNO no meu computador via SSH (usuário user no IP 192.168.0.100), deixando de fora o diretório “lost+found” que é do Ext4 e não desejo incluir no backup. Depois de fazer alterações no HD externo, posso executar esse mesmo comando novamente que será feito o sincronismo dos arquivos, sem mistérios. Porém vale lembrar que mudanças feitas no HD do computador serão sobrescritas, então utilizo o mesmo para visualizar arquivos, existem opções do rsync para copiar apenas arquivos que forem mais novos na origem e ignorar arquivos mais novos no destino, porém para o meu caso não foi necessário.

Para quem prefere uma interface gráfica, existe o Grsync. Extremamente simples de instalar (apt-get install grsync no Debian e Ubuntu). A princípio pode ser um pouco assustador a quantidade de opções, porém é só olhar com calma e escolher o que desejar ou utilizar a padrão mesmo. Depois de configurar as opções é possível salvar a configuração para reutilizá-la depois. Quando for executar será chamado o rsync em brackground, a sincronização ocorrerá como na linha de comando.

Hoje utilizo o Grsync para fazer backup de alguns diretórios para o HD externo e o rsync na linha de comando (através de um alias do Bash) para fazer o backup do HD para o computador. Se algum dia tiver problemas com o HD no computador, simplesmente substituo-o e executo o rsync, se tiver problema com o HD externo, compro outro, executo o rsync com os diretórios invertidos para copiar tudo para o HD externo e volto a utilizá-lo normalmente.

Também o possível utilizar esse mesmo esquema invertendo o diretório de gravação, você pode gravar seus arquivos em algum diretório e fazer o backup no HD externo, acredito que seria um caso até mais comum, porém preferi deixar esse espaço livre no notebook, já que tinha o computador disponível.

Como funciona o computador – Argumentos e comunicação de processos

Para um programa funcionar e trazer respostas diferentes é necessário que haja alguma forma de interagir com o mesmo, as vezes o usuário, outras o próprio sistema. Como os sistemas que utilizamos hoje seguem um padrão que é muito forte na linguagem C, irei usá-la como exemplo.

Todos os programas em C, quando chamados diretamente no sistema, iniciam pela função int main(int argc, char *argv[]), para palavra aqui tem um significado. O primeiro e mais óbvio é o main, que é o nome da função, utilizado pelo sistema para saber o ponto inicial de todo processo. Logo antes temos o retorno da função (int), para os processos também conhecido como “return code”, é utilizado para identificar se o programa finalizou com algum erro ou não. Toda vez que a função main chamar um return para finalizar o processo esse número é enviado para quem iniciou o processo, 0 quando tudo foi processado corretamente (este texto foi copiado sem nenhum tipo de revisão), ou diferente de 0 caso tenha ocorrido algum erro. Isso é feito dessa forma para que possa identificar a causo do erro, um programa que copia arquivos via rede poderia retornar 0 quando copiou corretamente, 1 quando não conseguiu encontrar o arquivo para copiar e 2 quando tiver alguma falha de rede por exemplo, facilitando assim que outros processos saibam o que ocorreu. No Bash ao finalizar a execução de um programa é possível ver o seu “return code” com o comando echo $?.

Para finalizar a assinatura da função main, temos dois argumentos. argc é um contador de quantos argumentos o programa recebeu na inicialização, normalmente isso é utilizando na linha de comando, por exemplo quando executados cp -R arquivo backup no GNU/Linux, passa 3 argumentos diferentes para o processo. Logo em seguida vem o argv, que é uma lista de onde o valor desses argumentos estão dentro da memória, no caso já descrito para que o programa acesso o -R, arquivo e backup podendo identificar o que o mesmo deverá executar. No Bash esses argumentos podem ser passados depois do nome do programa, separados por espaço.

Muitas vezes em alguma interface gráfica abrimos uma foto, ao dar dois cliques, seria como executar um programa na linha de comando, passando o arquivo como argumento, assim o programa sabe qual arquivo ele deve abrir, como gimp foto.jpg.

Os programas que funcionam na linha de comando também são conhecidos por mostrar textos na tela e as vezes pedir para o usuário digite algumas informações. Isso é possível graça a três fluxos de padrões, STDIN, STDOUT e STDERR (este texto foi copiado sem nenhum tipo de revisão), também conhecidos por entrada, saída e saída de erro. Um programa que manipule esses fluxos é capaz de ler o teclado e mostrar mensagens na tela por exemplo. Vale notar que temos dois fluxos de saída, isso permite salvar a saída do programa num arquivo, porém continuar mostrando erros na tela.

No Bash é possível alterar a origem ou destino desses fluxos na execução de códigos. Para que um programa leia um arquivo em vez do teclado, podemos utilizar o <, como em cat < arquivo.txt. Para salvar a saída de um comando num arquivo, podemos utilizar o >, como em find > arquivo.txt, porém caso tenha algum erro ainda será possível ver na tela. Para alterar a saída de erro para um arquivo devemos utilizar o 2>, como find 2> erros.txt. Também podemos juntar esses operadores, como find > arquivo.txt 2> erros.txt. Ainda há uma forma de unir a saída de erro com a saída, utilizando 2>&1, como find > arquivo.txt 2>&1.

Uma das características dos sistemas UNIX são os programas que executam apenas uma função bem definida, fazendo desvios nesses fluxos é possível combinar esses programas para efetuarem coisas maiores, simplesmente conectando a saída de um programa com a entrada de outro, utilizando o |, como cat arquivo.txt | sort | uniq.

Existe ainda uma outra forma de passar argumentos, assim como as linguagens de programação têm variáveis, existem variáveis no contexto do sistema operacional e os processos podem acessá-las, e em alguns casos até alterá-las. No Bash podemos utilizá-las através do export, como export CONFIG=/etc/meu.config e executar o programa posteriormente, ou antes do nome do programa, como CONFIG=/etc/meu.config apache2.

Um exemplo de uso dessas formas de comunicação com processos são os servidores HTTP configurados com CGI, que nada mais são que páginas geradas dinamicamente por um processo. Algumas variáveis referentes ao servidor e cliente são definidas como variáveis de ambiente, formulários e arquivos são enviados para a entrada padrão, o endereço da página acessada inicia um programa específico que processas essas informações e envia para a saída padrão a página HTML que o servidor HTTP responderá para o cliente.

—–

Finalizo aqui esta série, para quem acompanhou espero que tenham gostado.

Não sei se o texto ficou claro o suficiente em todas as partes. O objetivo era apresentar o básico sobre como funciona um computador e como essas partes interagem, podendo assim ter uma visão geral e possibilitar que quem tiver vontade de conhecer mais a fundo possa buscar mais informações. Acredito que esse objetivo tenha sido completado.

Caso surjam mais temas, posso fazer uma segunda série de posts, ou explicar melhor algum ponto, então podem sugerir assuntos.

Como funciona o computador – Processos

Depois que o computador está executando o sistema operacional, todos os programas que desejam ser executados devem virar um processo. Iniciar um processo significa para o sistema operacional, copiar o conteúdo do programa para a memória, reservando espaço para o mesmo fazer sua execução criando variáveis, criar um ambiente separado dos demais, chamado de contexto, tanto no sistema quando no processador. Após esses passos o processo pode ser iniciado.

Inicialmente os computadores tinham apenas um núcleo, o que significa que era possível fazer apenas uma operação simultaneamente. Hoje com a popularização dos processadores multinúcleos é possível a execução de processos paralelos (este texto foi copiado sem nenhum tipo de revisão), porém a quantidade de processos normalmente é maior que a de núcleos, sendo necessário que esta mesma técnica ainda seja aplicada.

O sistema operacional, através de alguma lógica fará a divisão do tempo do processador entre os processos ativos, e permitirá sua execução. Para um processo ter sua execução é necessário carregar os valores dos registradores que foram salvos na memória, permitir a execução e após o mesmo gravar o valor dos registradores novamente na memória.

Esse processo de salvar os valores dos registradores na memória, e carregar de outro processo é conhecido como troca de contexto (este texto foi copiado sem nenhum tipo de revisão). Sem esse mecanismo não seria possível iniciar a execução de um processo, interrompê-lo momentaneamente para a execução de outro e depois retornar sua execução do ponto interrompido. Isso que dá a característica de multitarefa e tempo compartilhado nos sistemas operacionais atuais.

Quanto mais processos simultâneos, menor o tempo que o processo terá para sua execução, além do tempo necessário para realizar mais trocas de contextos. Tudo isso pode ser responsável pela lentidão quando muitos processos estão tentando executar várias operações, em vez de executar os processos um após a finalização do outro.

Vale observar que se o mesmo programa for executado duas vezes, cada vez será um processo totalmente diferente. Apesar do código carregado da memória tem o mesmo conteúdo, seus contextos serão diferentes.

Como funciona o computador – Sistema operacional

O sistema operacional é a parte em software central do computador, praticamente tudo passa ou é gerenciado por ele. Porém temos que dividi-lo um pouco, existe uma parte responsável por gerenciar o computador chamado do kernel e demais partes que tornam o computador operacional.

Explicando melhor o kernel, ele que iniciará o sistema e fará a gerência dos recursos. Assim como o espaço do HD, a memória e processador não foram projetados (pelo menos não inicialmente) para auto dividir, necessitando que algo o faça, se não pois programas poderiam tentar utilizar o mesmo espaço de memória e terem resultados totalmente inesperados, além de um não permitir a execução do outro dentro do processador. Toda vez que um novo programa é aberto, será o kernel o responsável por alocar recursos para o mesmo, como espaço de memória e tempo de processamento, para só depois iniciar o processo.

Os processos executam várias funções básicas, e cabe ao kernel, através de chamadas de sistemas (este texto foi copiado sem nenhum tipo de revisão), responder a essas solicitações, oferecendo um conjunto de instruções para os programas, porém como cada kernel é diferente, um programa compilado para um kernel específico pode não funcionar para outro, principalmente quando eles não seguem o mesmo padrão como o Linux e o Window. Porém o Linus Torvalds utilizava programas do projeto GNU para validar a sua compatibilidade com o Unix.

Outra função do kernel, além de oferecer os recursos computacionais aos programas, é permitir a comunicação com os diferentes dispositivos através dos drivers.

Passando as outras partes que compõem os sistemas operacionais temos vários programas auxiliares, que normalmente são compostos por uma interface para o usuário, shell ou linha de comando para alguns e interface gráfica para outros, editor de texto e compilador. Essas ferramentas podem variar, como no caso da interface gráfica ser obrigatória no Windows e o compilador não estar incluído no pacote inicial, o que não é problema caso não vá se desenvolver nada, porém limita a possibilidade de utilização do computador aos programas disponibilizados por terceiros.

No caso do GNU/Linux é muito simples ver esses componentes, temos o Linux, que é apenas o kernel, as demais ferramentas vieram do projeto GNU, formando assim o nome GNU/Linux. Para linha de comando o padrão hoje é o Bash (este texto foi copiado sem nenhum tipo de revisão), mas existem outros como zsh e até o csh que tinha uma linha de comando parecendo mais com a linguagem C. Editor temos o Vim, Emacs, Nano e Mcedit, cada um com suas vantagens e facilidades. Compilador temos principalmente o GCC do projeto GNU, com essas ferramentas já é possível desenvolver todo os demais do sistema, incluindo uma interface gráfica se desejado. Porém desenvolver tudo não é prático e hoje os programas são distribuídos nos casos de software livre ou licenciados nos casos de software proprietário.

—–

Se ficou alguma dúvida ou deseja conhecer mais sobre sistema operacional, recomendo o episódio 3 do podcast Hack ‘n’ cast: Introdução ao GNU/Linux. Vale a pena conferir.

Como funciona o computador – Sistema de arquivos

Todas as informações que são armazenadas definitivamente num computador, ou seja, que não se perderam após o mesmo ser desligado, precisam estar armazenadas dentro de uma estrutura, para posteriormente ser possível recuperá-la. Tantos HD, HD externo, CD, DVD e pendrives usam essas estruturas chamadas de sistemas de arquivos.

Porém antes de falar sobre sistema de arquivos, existe uma técnica chamada de particionamento, que divide logicamente o espaço contínuo dessas unidades. O uso de partições possibilita mais de um sistema de arquivos no mesmo dispositivo, cada um em seu pedaço do espaço disponível. O funcionamento das partições estão relacionadas a forma de particionamento, como MBR, padrão mais antigo, ou GPT.

Todas as formas de particionamento gravam em certas regiões da memória do dispositivo informações sobre a divisão do espaço, seguindo a forma de uma tabela (este texto foi copiado sem nenhum tipo de revisão), também conhecida como tabela de particionamento. Essas tabelas são independentes do sistema operacional, tanto que é o principal responsável por permitir ter instalado num mesmo computador dois sistemas operacionais distintos.

Após ter reservado espaço através do particionamento, é necessário criar a estrutura para organizar os arquivos dentro deste espaço, essa estrutura de organização são chamadas de sistemas de arquivos. Normalmente dentro do sistema de arquivos teremos espaços para informações relativas ao arquivo, como nome, data de criação, data de modificação, permissões de acesso, diretório onde se encontra o arquivo, além do próprio conteúdo do arquivo.

Como existem vários sistemas de arquivos, sempre existirá um que se adéque melhor as situações, quando existem mais arquivos grandes ou pequenos, isso também pode ser parametrizado ao criar a estrutura dentro da partição. Existem também sistemas de arquivos para aplicações específicas, como identificar alterações nos arquivos desde um certo ponto, podendo voltar ao estado anterior ou armazenar arquivos para processamento em big data. Esses sistemas de arquivos também podem ser utilizados dentro do espaço de outros arquivos, ganhando as funcionalidades do sistema desejado ao custo de ter que acessar dois sistemas de arquivos para recuperar as informações.

A escolha do sistema de arquivos depende do sistema operacional (este texto foi copiado sem nenhum tipo de revisão), não são todos que são suportados por todos os sistemas, e podem ser perdidos alguns recursos como controle de permissão, caso o mesmo não seja plenamente suportado pelo sistema operacional.

Vale lembrar que o sistema de arquivos foi uma das primeiras coisas desenvolvidas para o Unix, ainda na fase onde Ken Thompson, Dennis Ritchie não tinham um computador para desenvolver o sistema e seu projeto foi feito no quadro com giz.

Como funciona o computador – Linguagem C

Tudo o que o processador faz é executar instruções determinadas que estão na memória, porém escrever essas instruções manualmente é extremamente lento para desenvolver um programa, além de ficar preso a arquitetura de computador a qual foi escrita. A principal alternativa é a linguagem de programação C.

As principais vantagens são: a existência de mecanismos de controle estruturados (este texto foi copiado sem nenhum tipo de revisão), permitindo a execução de blocos de comandos cobre certas condições ou até que uma condição se torne falsa sem se preocupar com o endereço das instruções. Apresenta tipos básicos de dados, como número inteiro, decimal, negativo e até caracteres, esses tipos são baseados em alguma codificação para serem representado em bits.

A relação entre a linguagem C e Assembly é bastante direta, uma vez que o GCC (compilador de C do projeto GNU) converte o código para Assembly durante o processo de compilação do programa, como se tudo fosse conjuntos de macros. Porém as instruções em C não têm uma relação muito direta com as instruções Assembly, uma vez que essa relação depende da estratégia adotada pelo compilador, e mesmo compilando um programa duas vezes no mesmo computador pode gerar resultados diferentes.

Existe uma palestra muito interessante sobre compilação determinística que fala justamente do problema dos compiladores gerarem resultados diferentes, e que organizações como a NSA podem tirar proveito para inserir falhas de segurança apenas nos programas compilados que não aparecem no código fonte. Essa palestra está em português e foi apresentada por Seth Schoen no FISL 15 (http://hemingway.softwarelivre.org/fisl15/high/40t/sala40t-high-201405071559.ogv).

Como as instruções em C são padronizadas, um mesmo código poderia ser compilado para diferentes arquiteturas, desde que não tenha instruções ou lógica específica da arquitetura. Para atingir esse objetivo basta mudar o compilador, no caso o GCC permite compilar o programa para uma arquitetura diferente do processador atual, porém tem que ser preparado para isto.

Diferente de outras linguagens, C assume que o programador sabe o que está fazendo e não faz muitas validações no código (este texto foi copiado sem nenhum tipo de revisão), desde que esteja dentro da sintaxe da linguagem é possível compilar e rodar posteriormente, porém erros podem ocorrer caso o programador não tenha tratado corretamente a lógica ou valores. Muitas falhas de segurança ocorrem justamente por esse fato, como a falha Heartbleed, que não valida corretamente o tamanho da mensagem enviada pelo cliente e acaba enviando mais informações do que deveria do servidor.

Mesmo sendo bastante antiga é uma das linguagens que mais aproxima o programador com as instruções do processador, por isso muitas vezes ganhando em desempenho e menor consumo de memória que o programa equivalente em outras linguagens. Além dos fatos já descritos, ela também é a linguagem a qual a base dos sistemas operacionais normalmente são escritos e influenciou muitas outras linguagens. Definitivamente não é a mais fácil ou simples de se aprender, porém possibilita um melhor conhecimento do computador e base para conhecer outras linguagens, ou até entender o que ocorre por trás das mesmas.

Como funciona o computador – Processador

O processador é a parte fundamental do computador, sua estrutura varia muito conforme a arquitetura, porém todas possuem elementos semelhantes, variando em número ou capacidade, porém preservando sua função quando presentes.

A parte principal é a unidade de processamento, responsável por cálculos matemáticos e operações de lógica booleana (este texto foi copiado sem nenhum tipo de revisão), todas essas operações são baseadas em sequências de bits. Além do processamento, existe uma unidade para controlar as operações de devem ser efetuadas, somar, subtrair, comparar e muitas outras definidas pela arquitetura, também são sequências de bits, cada valor desta sequência corresponde a um circuito eletrônico dentro do processador, responsável por gerar a sequência corresponde ao resultado da operação desejada.

Porém para o processador não se perder e manter valores e resultados existem os registradores, esses componentes armazenam sequências de bits dentro do processador. Todos os dados e operação que o processador precisa executar estão armazenados neles. Alguns registradores também tem funções especiais, como o registrador que guarda o endereço de memória da próxima operação de deverá ser executada e resultados de comparação, se dois valores são iguais, se o primeiro é menor que o segundo, se é maior, tudo na forma de bits.

Um exemplo, o processador tem a instrução de buscar da memória o valor guardado no endereço de memória 1 para o registrador A. Após acessar a memória para buscar o valor, aumenta em 1 o endereço da operação que será executado, busca a sequência de bits que representam essa nova instrução, que é para buscar o valor guardado no endereço de memória 2 para o registrador B. Após acessar a memória, aumenta novamente em 1 o endereço da operação, consulta qual a próxima operação (este texto foi copiado sem nenhum tipo de revisão), que é somar o valor do registrador B no registrador A. Após a soma, aumenta novamente o endereço da operação, e verifica que é para escrever o valor do registrador A no endereço de memória 3. Esse processo descreve uma soma dos valores dos endereços de memória 1 e 2, colocando o resultado no endereço de memória 3, seguindo as operações que estão previamente descritas em outros endereços de memória.

Existem formas de mudar esse fluxo de operações, como alterar o valor do endereço da operação, ou em apenas alguns casos especifico, como quando dois valores são iguais. Toda essa lógica é a base dos programas de computadores.

Uma forma de passar essas instruções para o processador é através do Assembly, que é uma representação legível por humanos desses valores, por exemplo “ADD A, B” para somar o valor do registrador B no A. Para o processador entender essas operações é necessário antes um processo de tradução desta representação para os valores que ele entenda, normalmente feito por outro programa. Assembly não é uma linguagem de programação, e sim uma linguagem de montagem justamente pelo fato de representar cada operação que o processador deverá executar.

Como os programas estão na memória, existem alguns códigos que tentam alterar o valor desses endereços de memória para forçar o computador executar algo para o qual o programa que ele estava executando não foi programado, como no caso de algum ataque. Alterar o valor dos dados também pode ser interessante para passar por uma validação de senha, por exemplo.

Como funciona o computador – Projeto do computador

Estou iniciando uma série de posts para descrever minha visão de como um computador funciona. Ainda não decidi até que ponto desejo andar com a série, ou objetivo final, porém pretendo iniciar do ponto descrito aqui e ir até pelo menos o básico do sistema operacional. Este primeiro post será mais teórico para criar uma base de conhecimento para as demais partes, o próximo terá exemplo de funcionamento, porém é de extrema importância entender esses componentes para os exemplos fazerem sentido.

——

Todo computador necessita de um projeto, que normalmente seguem o modelo proposto por John von Neumann. Deste modelo, a parte importante para este texto é entender que um computador é composto de CPU, memória, entrada/saída (I/O) e barramento para interligar e comunicar estas partes. Outro ponto que vale destacar é que os programas podem ser executados sem a necessidade de alteração de hardware, uma analogia oposta seria os videogames de cartucho, onde você precisaria alterar o circuito elétrico, trocar o cartucho, para mudar de jogo, considerando que houvesse processamento dentro do cartucho e não apenas armazenamento do programa.

Esses componentes dependem da arquitetura do computador. Seu computador de casa, ou notebook, provavelmente é da arquitetura Intel de 32 bits (este texto foi copiado sem nenhum tipo de revisão), também conhecido como i386 ou x86, nos casos de processadores 32 bits, ou AMD 64, também conhecidos por x86_64, nos casos de processadores 64bits. Esses nomes são apenas para a arquitetura e não do fabricante, processadores Intel de 64 bits são da arquitetura AMD 64. Seu celular provavelmente é ARM e seu access point MIPS.

Todos os computadores trabalham com bits (estou desconsiderando computadores quânticos, o qual não tenho muitos conhecimentos ainda), utilizando apenas dois estados elétricos para representar dados, estado com energia e sem energia, também representado por 1 e 0 respectivamente. Uma sequência desses bits formam as informações, que utiliza matemática binária e lógica booleana para representar esses dados.

A CPU é a parte responsável por todo o processamento, operações e cálculos, descreverei no próximo post seu funcionamento.

A memória é responsável por guardar pedaços de informações. Nos computadores atuais é representado pela memória RAM. Seus dados são importantes apenas para a execução atual do computador, caso o mesmo seja desligado (este texto foi copiado sem nenhum tipo de revisão), seu conteúdo pode ser perdido e na prática é considerado assim, apesar de hoje existir técnicas para recuperar seus dados, porém esse processo não pode ser garantido e não é utilizado normalmente pelo computador. Seu funcionamento é de gravar e ler dados em um determinado endereço de memória.

I/O são toda as operações de comunicação desse sistema com componentes externos, como teclado, mouse, HD e demais dispositivos. É a parte do sistema mais diversificada.

O barramento é o meio de comunicação desses componentes, além de oferecer conexão elétrica pode apresentar outros artifícios, como cache para a memória, guardando uma parte de suas informações e como possui tempo de resposta menor que a memória, possibilita uma resposta mais rápida para o processador. Vale a pena notar que sua velocidade depende das partes envolvidas, o processador é o componente mais rápido, seguido pela memória e I/O, caso o processador esteja executando uma operação de gravar ou ler a memória, será limitado pela velocidade da mesma, assim como se o processador por consultar o I/O, será ainda mais lento que a memória.