Quem sou eu

Minha foto
Salvador, BA, Brazil
Analista de sistemas, expert em telecom, formado em Eng. Elétrica e nerd assumido

quarta-feira, 27 de outubro de 2010

Sobre Pesquisas de Intenção de Voto

Por alguma destas artes do destino eu tive que me envolver profissionalmente com o estudo da estatística, indo um pouquinho além daquilo que normalmente é ensinado sobre este assunto - tipicamente em um único semestre - nos cursos de engenharia (elétrica, no meu caso). Então acho que posso "dar pitaco" sobre a confiabilidade - ou falta dela - dos resultados das pesquisas de intenção de voto publicados até agora.

Acredito piamente que é perfeitamente possível, dentro de um limite de confiabilidade diretamente associado com a metodologia empregada na pesquisa, aferir a opinião do eleitorado. Mas também acredito piamente que o comportamento normal dos processos (industriais, econômicos, sociais, ou o que seja) é a variação, e não a falta dela.

O que você diria se eu lhe contasse a seguinte história: quatro pedreiros foram escolhidos ao acaso e a cada um deles foi dada a tarefa de construir um muro com as mesmas dimensões (altura, largura e espessura). Ao final da tarefa, ao comparar os resultados obtidos, descobrimos que todos os quatro construíram muros cujas dimensões, além das quantidades de material utilizadas (cimento, areia, tijolos, água, etc.) e do tempo de execução da tarefa, todos apresentam uma variação de apenas 1% entre o maior e o menor valor de cada item. Não sei a opinião de vocês, mas eu acharia muito estranha essa coincidência toda.

Pois eu vejo situação semelhante ocorrendo nos resultados ds pesquisas de intenção de voto publicados até agora. Vamos usar, a título de exemplo, as pesquisas relativas a intenções de voto no 2° turno da eleição para Presidente da República. Apesar de utilizarem metodologias diferentes para seleção da amostra da população de eleitores a entrevistar, bem como usarem formas diferentes de realizar as entrevistas, todos apresentam resultados muito próximos uns dos outros.

Se isso não fosse suficiente, quando os resultados são apresentados na forma de série temporal das pesquisas realizadas por cada instituto, observa-se que a variação entre pesquisas consecutivas quase sempre encontra-se dentro da "margem de erro" (que nunca é devidamente explicada ao eleitorado leigo). Mas este fato trivial - a variabilidade de cada processo - é apresentada grandiosamente na imprensa como configurando "tendências" de comportamento do eleitorado. Ridículo!

E, adicionando a injúria ao insulto à inteligência de quem tenha pelo menos dois neurônios funcionando de forma coerente, esta variabilidade limitada dos resultados ao longo do tempo teria que ser interpretada como se o eleitorado já estivesse com sua opinião perfeitamente definida, e que os fatos da campanha não tem causado nenhum efeito perceptível. Não acho isso muito razoável.

Resumindo, estes números são, para mim, prova de que existe algo de podre no reino da Dinamarca.

sexta-feira, 22 de outubro de 2010

Sistemas de Computação 5 - O Ciclo Fetch-Execute

Já vimos o aspecto das instruções de máquina e seu conteúdo, mas ainda precisamos entender o processo pelo qual a CPU consegue efetivamente executar a sequência de instruções de máquina que forma o programa. Primeiro: como as instruções de máquina do programa organizam-se na memória?

Elas ficam armazenadas de forma sequencial, e sua execução também ocorre de forma sequencial, a menos que a própria instrução em execução provoque um desvio. Neste caso a sequência linear de execução das instruções é retomada a partir do endereço de memória para onde ocorreu o desvio.

Vamos, por enquanto, deixar pendente a questão de como as instruções de máquina foram colocadas na memória exatamente daquela maneira. Para a execução das instruções de máquina a CPU utiliza dois registradores de controle:
  • Program counter (PC): armazena o endereço na memória da próxima instrução a executar;
  • Instruction register (IR): armazena o código da instrução em execução.
Estes registradores são utilizados em uma sequência de ações da CPU, conhecido como ciclo fetch-execute
  1. Instruction fetch: usando o endereço contido no PC, a unidade de controle comanda a leitura da próxima instrução da memória para o IR;
  2. Instruction decode: a unidade de controle identifica o OpCode  e as referências aos operandos da instrução no IR. Após isso a unidade de controle incrementa o valor do PC com o número de bytes da instrução que está carregada no IR;
  3. Operand fetch: os conteúdos dos operandos da instrução no IR são copiados de seus locais de origem para os registradores de entrada da ALU;
  4. Execute: a unidade de controle aciona os circuitos da ALU necessários para a execução da instrução e o resultado é armazenado em um registrador de saída da ALU. Eventuais ocorrências durante a execução da instrução (ex.: overflow aritmético) são registrados em alguns bits, denominados condition code) de um dos registadores de controle;
  5. Result store: o conteúdo do registrador de saída da ALU é copiado para sua localização defnitiva;
  6. volta ao passo 1.

Isto é o essencial por enquanto. Mais tarde vamos ver que, por causa do mecanismo de interrupções, o ciclo fetch-execute das máquinas modernas é um pouquinho mais complicado. Apenas para manter o hábito, abaixo vai uma figura que representa o ciclo fetch-execute (tal como o conhecemos até agora) na forma de um fluxograma.


A partir desta descrição podemos entender os vários tipos de instrução de máquina geralmente reconhecidos pela unidade de controle da CPU (embora cada fabricante de processador faça isto de modo ligeiramente diferente, e dê diferentes nomes para coisas que fazem essencialmente a mesma coisa):
  • Instruções aritméticas: executam operações aritméticas sobre operandos que armazenam números inteiros e reais (eventualmente também sobre vetores, matrizes ou tensores de números inteiros ou reais);
  • Instruções lógicas: executam operações lógicas (AND, OR, XOR, NOT, etc.) sobre operandos que, geralmente, são tratados como se armazenassem apenas um string de bits;
  • Instruções de comparação: comparam o conteúdo dos operandos como sendo strings de bits ou como valores numéricos e, em geral, expressam o resultado da comparação (operandos iguais, 1° operando maior ou 2° operando maior) nos bits do condition code;
  • Instruções de desvio: provocam a quebra da execução sequencial das instruções na memória, forçando que ela seja retomada a partir do endereço de memória dado como operando da instrução (conhecido como jump address ou branch address). Existem dois tipos de instruções de desvio: desvio incondicional, onde o desvio para o jump address fornecido sempre ocorre, e desvio incondicional, onde o desvio para o jump address fornecido pode ocorrer ou não, a depender de mais um operando da instrução, que controla as condições de ocorrência ou não do desvio (geralmente este operando é um string de bits que, juntamente com os bits do condition code, sofrem uma operação de XOR cujo padrão de bits resultante define se ocorrerá ou não o desvio);
  • Instruções de movimentação: provocam a cópia de conteúdos da memória para os registradores, dos registradores para a memória e (quase sempre) de um local na memória para outro.
  • Instruções de controle: permitem ao programador efetuar operações sobre os registradores de controle, afetando assim o ocmportamento da CPU (ex.: instruções para permitir ou mascarar temporariamente que a CPU reconheça determinados tipos de interrupções).

As instruções de controle, bem como o acesso a determinadas áreas da memória, tem um grande potencial para provocar "travamento" da CPU caso não sejam uusadas da forma apropriada. Por isso geralmente essas instruções (e a manipulação das áreas restritas da memória) são consideradas instruções privilegiadas, e a CPU só admite a sua execução se o programa for habilitado para isso.

Aqui nós temos um aparente paradoxo: colocar a CPU em modo de aceitar a execução de instruções privilegiadas só pode ser feito pela execução de instruções privilegiadas. Como pode ser isso? Peço que vocês tenham um pouco de fé, por enquanto. Quando chegarmos aos tópicos sobre sistemas operacionais isto vai ficar mais claro.

No próximo artigo vamos falar de algumas coisinhas para melhorar a performance do ciclo fetch-execute básico.

quarta-feira, 20 de outubro de 2010

Sistemas de Numeração 1 - Sistemas de Numeração Posicionais

Um sistema de numeração posicional é aquele onde o valor atribuído a cada símbolo usado na representação dos números depende da posição que ele ocupa na representação. O exemplo trivial é o sistema de numeração que estamos acostumados a utilizar no dia a dia: o sistema decimal (porque a base do sistema é 10) ou indo-arábico (por causa da origem dos símbolos - algarismos - utilizados). Considere a representação decimal 56782. O algarismo 2 vale exatamente o seu valor intrínseco, porque está posicionado na ordem de grndeza das unidades. Já o algarismo 8 tem valor posicional 80, por ocupar a ordem de grandeza das dezenas. E assim por diante com os demais algarismos.

O interessante é que esta idéia pode ser generalizada. Para entender isto primeiro precisamos provar um teorema, que poderíamos chamar de teorema geral da enumeração (TGE). Vou apresentar aqui apenas a versão da demonstração para números naturais (porque é só o que interessa para o caso dos sistemas de computação). Mas é fácil estender a prova para os números reais (sugiro que vocês tentem).


A prova que vou apresentar depende da definição da operação de divisão no conjunto dos naturais.


Para efeito de generalidade vamos supor que x seja muito maior que b. Então podemos fazer a seguinte série de divisões:


Substituindo a última expressão obtida para o quociente na penúltima, e assim por diante até voltar à primeira expressão obtida para o quociente temos:


A última expressão encontrada satisfaz às características do polinômio desejado, porque todos os coeficientes que correspondem a restos das divisões efetuadas tem valores naturais no intervalo [0,b), e o último quociente, por construção, também é um número natural no intervalo [0,b), o que prova a existência do polinômio desejado.

Para a prova da unicidade deste polinômio suponhamos que exista um outro polinômio tal que:


Os dois polinômios serão diferentes se houver pelo menos um dos coeficientes de mesma ordem de grandeza em b com valores diferentes. Então, subtraindo um polinômio do outro temos


Mas isto só é possível se todos os coeficientes dos dois polinômios forem iguais. Se quiser alongar ainda mais a prova, suponha também que o novo polinômio possa ter grau superior ao que já foi encontrado (porque menor não pode ser). Não mudaria nada o resultado. Ao subtrair um do outro fica evidente que os eventuais coeficientes dos termos de ordem superior a n também teriam que ser zero, o que prova a unicidade do polinômio encontrado.

Muito bem. E como fazemos uso deste teorema para construir todos os sistemas de numeração posicionais? Da seguinte forma: primeiro escolhemos o valor b para a base do nosso sistema; depois escolhemos um conjunto com b símbolos diferentes (não importa quais, pode ser quadradinho, estrelinha, etc.) para representar, respectivamente, as quantidades 0, 1, ... , b - 1 e são chamados de algarismos; a partir daí qualquer número natural é representado pela sequência de algarismos cujos valores correspondem aos coeficientes do polinômio de potências de b que é igual ao número a representar, com o algarismo mais à esquerda correspondendo ao coeficiente do termo de mais alta ordem do polinômio, e o algarismo mais à direita ao termo independente do polinômio.

Como o TGE garante que para qulquer valor da base escolhida existe um e somente um polinômio que satisfaz as suas regras (e no qual baseia-se o sistema de numeração daquela base), a consequência lógica é que todos os sistemaas de numeração são equivalentes. E isto nos traz duas perguntas (que responderei depois, em outros artigos):
  • Dada a representação de um número em um sistema de numeração qualquer, como obter a sua representação equivalente em um outro sistema?
  • Como generalizar os procedimentos da aritmética para que eles sejam válidos para qualquer sistema de numeração?

Os sistemas de numeração que nos interessam diretamente, além do decimal, claro, são o sistema binário (base 2) e o sistema hexadecimal (base 16). Por uma questão de familiaridade, sempre que possível usamos como algarismos os mesmos do sistema indo-arábico, com os mesmos valores intrínsecos. Então os algarismos do sistema binário são 0 e 1; e os algarismos do sistema hexadecimal são 0, 1, 2, 3, 4, 5, 6, 7, 8. 9, A (representando o valor decimal 10), B (representando o valor decimal 11), C (representando o valor decimal 12), D (representando o valor decimal 13), E (representando o valor decimal 14) e F (representando o valor decimal 15),

No próximo artigo desta série vou responder à primeira das perguntas que deixei pendentes.

terça-feira, 19 de outubro de 2010

Sistemas de Computação 4 - Instruções de Máquina

O nível mais básico que um processador pode ser programado é através do conjunto das suas instruções de máquina (machine instructions set), também conhecidas como linguagem de máquina. Todos os fabricantes de processadores publicam a documentação necessária para compreender os detalhes da sua implementaão da arquitetura von Neumann e o funcionamento de cada uma das instruções de máquina reconhecidas.

Por exemplo: quem estiver interessado nos princípios de funcionamento e na linguagem de máquina das famílias de processadors Intel de 32 e 64 bits devem ler o manual Intel(R) 64 and IA-32 Architectures Software Developer's Manual (volumes 1, 2A, 2B, 3A e 3B) e os manuais Intel(R) 64 and IA-32 Architectures Optimization Reference Manual e Intel(R) 64 and IA-32 Architectures Software Developer's Manual Documentation Changes, só para começar.

Pra não dizerem que sou parcial com este ou aquele fabricante (e também em homenagem às minhas raízes profissionais), quem quiser fazer o mesmo com as máquinas IBM z/Architecture (sucessora das venerandas arquiteturas ESA/390, ESA/370, /370-XA, /370 e /360) tem que começar pela leitura do manual z/Architecture Principles of Operation.

Não quero prender meus textos à descrição da tecnologia de qualquer fabricante em particular. Em vez disso vou procurar descrever aspectos que são válidos para todos eles, embora a terminologia específica usada por cada fabricante possa variar. Vamos nos focar nas funcionalidades, e deixar a nomenclatura pra lá, ok?

Então, vamos lá... Cada instrução de máquina é um conjunto de bits que tem, geralmente, o tamanho de uma palavra de memória (fullword). Também é posível que o instruction set aceite instruções de tamanhos diferentes. Nestes casos os tamanhos alternativos mais comuns são de meia palavra (halfword) e uma palavra dupla de memória (doubleword).

A instrução de máquina é dividida logicamente em duas áreas: os bits mais signiicativos da instrução (geralmente 8 ou 16 bits em processadores com palavra de memória de 32 ou 64 bits) são denominados Operation Code (ou, simplesmente OpCode) e identificam a natureza da instrução, Os demais bits são denominados área de operandos, e identificam os operandos que devem ser utiliados na execução da instrução.

Falando em bits mais significativos e menos significativos, aqui cabe uma advertência para quem não tem familiaridade com o assunto. A memória normalmente é "vista" pela CPU como uma sequência liear de bytes (usualmente com 8 bits cada). Os bytes da memória são endereçados (em binário, e com um número de bits geralmente igual a uma fullword) a partir de zero e crescendo sequencialmente até a capacidade total instalada na máquina (em tempo: estou falando da memória física, real, aqui. Memória virtual é um assunto para mais tarde). Mas existem maneiras diversas de colocar os bytes de informação na memória: os dois principais sâo:
  • Big endian: os dados na memória são armazenados de forma que o byte mais significativo dos dados ocupa o endereço de memória mais baixo, e o byte menos significativo dos dados ocupa o endereço de memória mais alto;
  • Little endian: os dados na memória são armazenados de forma que o byte mais significativo dos dados ocupa o endereço de memória mais alto, e o byte menos significativo dos dados ocupa o endereço de memória mais baixo.
Por exemplo, suponha que temos que armazenar na memória, a partir do endereço de memória x0 (para os não iniciados, este "x" antes do número indica que ele está expresso no sistema de numeração hexadecimal) a sequência de quatro bytes que representam o número decimal 1234 (em formato ASCII, um byte por caracter - se você não está familiarizado com a sigla ASCII calma... Veremos isto mais tarde, quando falarmos dos modos de codificação do valor dos operandos). O byte mais significativo dos dados é o caracter 1, e o byte menos significativo é o caracter 4. Conforme a endianess da máquina o armazenamento na memória seria conforme mostrado abaixo.


Curiosamente já houveram máquinas (como o DEC PDP-11) que tinham endianess mista. Enfim... Nestes textos sempre vou adotar a convenção de representar dados na memória no formato big endian. Então, seguindo esta convenção, a figura abaixo mostra a representação esquemática de uma instrução de máquina.

A propósito, se alguém ainda tem dificuldades com números expressos no sistema binário ou hexadecimal, em breve devo intercalar nos meus posts uma outra série de artigos, dedicados aos sistemas de numeração, aos procedimentos de conversão de representação e aos procedimentos da aritmética básica.

Mas, vamos voltar ao assunto... OpCode e área de operandos. O diagrama abaixo representa isto.


Conforme o valor contido no OpCode a CPU (mais especificamente a unidade de controle) identifica a localização e os valores dos operandos da instrução. Genericamente existem quatro tipos de operandos que podem ser referenciados em uma instrução de máquina:
  • Operandos implícitos: são aqueles que, embora não explicitados na área de operandos da instrução, serão utilizados no seu processamento.
  • Operandos imediatos: são aqueles cujo valor faz parte da instrução;
  • Operandos indiretos: são aqueles onde a referência contida na instrução aponta para o local  onde o valor do oprando está armazenado.
Para efeito de exemplo. vamos supor que o valor do operando de uma instrução seja a sequência de quatro caracteres ASCII ABCD. Se este fosse um operando implícito não haveria nenhuma referência na área de operandos da instrução, nem sobre o valor nem sobre a localização do operando, entretanto ele seria usado durante a execução da instrução. As figuras abaixo mostram como poderiam ser os casos deste operando ser imediato ou indireto.


As referências a operandos indiretos podem ser feitas três formas principais:
  • Referência absoluta: a referência ao operando identifica o registrador onde o valor do operando está armazenado ou o endereço absoluto na memória do byte mais significativo do valor do operando;
  • Referência base-deslocamento: a referência ao operando é composta de duas partes. A primeira identifica um registrador ou um endereço absoluto na memória onde está armazenado o endereço base (absoluto) do operando. A segunda parte pode ser um operando imediato, ou a identificação de um registrador ou um endereço absoluto na memória onde está armazenado o deslocamento do operando em relação ao endereço base. O endereço absoluto na memória do byte mais significativo do operando é obtido pela soma do valor do endereço base com o valor do deslocamento;
  • Referência base-índice-deslocamento: semelhante à referência base-deslocamento, apenas com a introdução de um elemento a mais - o índice - referenciado de forma semelhante à base. O endereço absoluto do byte mais significativo do operando é obtido pela soma dos valores base, índice e deslocamento.
Observe que a figura anterior de exemplo de operando indireto representa o caso onde o endereço absoluto do byte mais significativo do operando é dado como um operando iediato na instrução de máquina. As figuras abaixo exemplificam as outras formas de referência a operandos indiretos.



Agora que já vimos como são as instruções de máquina, no próximo artigo desta série vamos falar do método usado pela CPU para executar as instruções de máquina do programa; Porém acho que é chegada a hora, pelo bem dos menos experientes nesta área, de começar a publicar a série sobre sistemas de numeração.

    sexta-feira, 15 de outubro de 2010

    Sistemas de Computação 3 - Arquitetura von Neumann

    A grande novidade da assim chamada arquitetura von Neumann foi a formalizar o conceito que um computador é constituído essencialmente de três elementos:
    • A unidade central de processamento, ou CPU (central processing unit), responsável pela execução das instruções do programa;
    • A memória, responsável pelo armazenamento tanto das instruções do programa (que traduzem o algoritmo de solução do problema) quanto dos dados (que servem de operandos para as instruções do programa);
    • Os dispositivos de entrada e saída, ou dispositivos de I/O (input/output), responsáveis por traduzir os dados de algum meio externo (ex.: cartões perfurados e teclados) para o formato interno de armazenamento na memória, ou do formato interno da memória para um formato externo de armazenamento por longo período (ex.: fitas ou discos magnéticos) ou para um formato externo legível para seres humanos (ex.: telas de vídeo e impressoras).
    A CPU, por sua vez, também tem uma estrutura interna:
    • Unidade de controle, ou control unit, responsável pela interpretação das instruções do programa e pelo sequenciamento temporal das atividades necessárias para a sua execução;
    • Unidade aritmética e lógica, ou ALU (arithmetical and logical unit), onde estão os circuitos eletrônicos para execução das operações unitárias aritméticas (somas, subtrações, etc.) e lógicas (comparações, AND, OR, etc.) sobre os operandos das instruções;
    • Registradores, que são pequenas áreas de memória interna à CPU, que são usados para armazenamento temporário de operandos e resultados de instruções (registradores de uso geral ou general-purpose registers), e também para armazenar dados sobre o estado interno da CPU e sobre o programa em execução (registradores de controle ou control registers).
    A figura abaixo sintetiza todo o que já falamos sobre a estrutura da arquitetura von Neumann.



    Desde o tempo das válvulas eletrônicas o esquema básico de construção e funcionamento dos computadores eletrônicos digitais é esse. Daí para a frente as melhorias foram principalmente nos elementos de construção física da máquina (ex.: a transição da válvula para o transistor, e do transistor discreto para circuitos integrados), a algumas coisinhas adicionais ao modelo básico.

    Inicialmente toda transferência de dados entre CPU e memória e entre memória e dispositivos de I/O ocorria sobre um mesmo meio de comunicação: o barramento da máquina. O barramento normalmente é composto por dois conjuntos de vias de comunicação independentes. Um deles é usado para mensagens de controle (origem, destino e sentido da transferência de dados), e o outro para a transferência dos dados propriamente ditos. Cada via (controle e dados) é formada por n caminhos individuais, cada um com capacidade de transmitir um bit. e o conjunto dos caminhos na via transfere n bits (de controle ou de dados) em paralelo.

    O número n para a via de dados é o tamanho da palavra de memória da máquina (o número de bits que podem ser transferidos entre memória e CPU em uma única operação). Coloquialmente nos referimos ao tamanho da palavra de memóriaquando falamos do "número de bits" do processador (8, 16, 32, 64 bits...). A figura abaixo representa esquematicamente um barramento com via de controle de 4 bits e via de dados de 8 bits.


    Com o desenvolvimento da tecnologia dos processadores a diferença entre o tempo de acesso à memória pela CPU e o tempo de acesso à memória pelos dispositivos de I/O tornou-se um fator de limitação do desempenho da máquina. Então foi introduzido um novo barramento, apenas para a comunicação entre dispositivos de I/O e a memória. Finalmente, para permitir que o processador possa aproveitar o tempo ocioso durante a execução de operações de I/O (vamos entender melhor isso mais tarde), introduziram-se as controladoras de dispositivos, que tem uma "inteligência" local suficiente para efetuar toda a operação de transferência de dados entre a memória e os dispositivos de I/O controlados por ela sem depender do processador. Isto é conhecido como DMA (direct memory access).

    À medida que os processadores ficavam ainda mais velozes, até mesmo a diferença de desempenho entre o chip do processador e os chips da memória tornou-se um fator de limitação da performance. Construir toda a memória com chips do mesmo nível de desempenho do processador seria muito caro. Mas, usando o princípio da localidade referencial, pode-se construir uma hierarquia de memória, onde o nível mais baixo (e mais lento) é a memória principal, o nível mais alto (e mais veloz) são os registradores da CPU, e níveis intermediarios, chamados de memórias cache, para compatibilizar as diferenças de desempenho entre os extremos. As máquinas modernas tipicamente usam uma memória cache em três níveis: o cache L1 (e, geralmente, também o cache L2) são incorporados no próprio chip do processador, e o cache L3 normalmente é um chip separado.

    Então podemos agora desenhar um diagrama geral para uma máquina moderna baseada na arquitetura von Neumann.



    No próximo post dessa série vamos analisar a estrutura das instruções de máquina e seus operandos

    quinta-feira, 14 de outubro de 2010

    Sistemas de Computação 2 - Evolução dos Computadores

    Falar na evolução dos computadores até o seu estágio atual pode render horas de conversa, porque existem milhares de pequenos detalhes que, embora interessantes, servem principalmente para "encher linguiça". Aqui eu vou procurar me concentrar apenas nos eventos mais significativos para o surgimento dos modernos computadores eletrônicos digitais.

    Em 1854 George Boole publicou seu principal trabalho: An Investigation of the Laws of Thought on Which are Founded the Mathematical Theories of Logic and Probabilities (mais conhecido apenas como The Laws of Thought). Posteriormente, em 1847, ele publicou um panfleto intitulado Mathematical analysis of Logic. Estes dois trabalhos lançaram as bases para o desenvolvimento da disciplina da lógica algébrica, também conhecida como álgebra de Boole. Embora inovador, o trabalho de Boole só teria utilização prática no século seguinte.

    Em 1936 Alan Turing publicou o artigo On Computable Numbers, with an Application to the Eintscheidungsproblem. Usando uma abordagem simples e intuitiva (hoje conhecida como máquina de Turing) ele estendeu a validade do teorema da indecidibilidade de Gödel para o campo dos procedimentos automatizáveis (algoritmos) e fundou, na prática, a ciência da computação. Na mesma época, e com o mesmo objetivo, Alonzo Church desenvolveu o cálculo lambda. Nem Turing nem Church tinham conhecimento do trabalho um do outro, mas, pela maior simplicidade de aplicação, o trabalho de Turing obteve um reconhecimento muito maior.

    Em 1937, após completar o mestrado no MIT, Claude Shannon publicou um artigo baseado na sua tese intitulado A Symbolic Analysys of Relays and Switching Circuits. Neste texto a álgebra de Boole e a aritmética binária foram colocados como fundamentais para o projeto de circuitos lógicos. Uma teoria semelhante à de Shannon foi elaborada independentemente, desde 1935, por Victor Shestakov na Universidade de Moscou, porém seu trabalho só foi publicado em 1941.

    Também em 1941 Konrad Zuse terminou a construção do Z3, a primeira máquina que, embora ainda eletromecânica (usava relés como elementos de comutação), satisfazia aos requisitos para ser considerada uma máquina de Turing universal, o que equivale, na prática, ao que chamamos hoje de computador de propósitos gerais. O Z3 foi destruído em 1946 durante o bombardeio de Berlim, e o governo alemão não liberou financiamento para que Zuse evoluísse o projeto do Z3 para utilizar válvulas eletrônicas, por considerar que outros projetos eram mais prioritários para o esforço de guerra.

    A honra de ser o primeiro computador eletrônico (usando válvulas eletrônicas como elementos de comutação) equivalente a uma máquina de Turing universal coube ao ENIAC. A construção desta máquina, financiada pelo exército dos EUA, foi executada entre 1943 e 1946 na Universidade Estadual da Pensilvânia. Os projetistas principais desta máquina foram John Mauchly e J. Presper Eckert.

    Ainda antes que o ENIAC estivesse operacional, em 1944, Mauchly e Eckert propuseram a construção de uma nova máquina, chamada EDVAC, que deveria suceder o ENIAC, incorporando uma série de novas características operacionais. O matemático húngaro (naturalizado americano) John von Neumann foi contratado como consultor para este projeto e, em 1945, publicou um relatório parcial descrevendo as características básicas de funcionamento do EDVAC. Este relatório, denominado First Draft of a Report on the EDVAC (ou, simplesmente, First Draft), lançou as bases para a arquitetura de construção de todos os computadores comercialmente disponíveis até hoje, e ficou conhecida daí em diante (para desgosto de Mauchly e Eckert) como arquitetura von Neumann.

    Mas isso já é assunto para o próximo texto.

    quarta-feira, 13 de outubro de 2010

    Sistemas de Computação 1 - Visitando a Cozinha

    Tem muito tempo que eu penso sobre isto. Acho que a idéia começou quando eu ainda dava aula da cadeira Introdução aos Sistemas de Computação no curso de Bacharelado em Informática da UCSal. A questão básica é que ficar apenas ensinando conversão de sistemas de numeração e como funciona o código ASCII (embora necessário) é muito pouco para um curso universitário, e não existem textos simples para falar do básico sobre arquitetura de computadores e sistemas operacionais. Afinal, o conjunto computador + sistema operacional é o mínimo para que se tenha um sistema de computação. Então vou usar este blog como meu meio de expressão para tentar explicar de forma simples, porém com rigor, os conceitos fundamentais destes dois tópicos.

    Assim como os restaurantes que convidam os clientes a visitarem a cozinha do estabelecimento não esperam que eles tornem-se chefs de cuisine por causa disso, eu também não espero que os leitores destes textos venham a virar cientistas da computação. Porém, aqueles que se identificarem com o assunto poderão usar esta série de textos como um ponto inicial para aprofundamento.

    Fim da introdução. Hora de ir em frente com o assunto.