Tag Archives: kernel

Sistema Operacional miniOS-0.04: Linker

Estou continuando a serie de posts sobre como fazer um sistema operacional, fazendo o meu próprio sistema.

Quando essa parte do tutorial estiver publicada no meu blog, você já poderão conferir no repositório os fontes dos arquivos.

Agora vamos falar um pouco sobre linker e porque precisamos nos preocupar com isso quando estamos codificando um sistema operacional.

Linker

Linker

Credito Wikipédia

Um linker é um programa que vai fazer a ligação entre os diversos módulos do programa. Esses módulos podem ser arquivos objeto gerado por códigos fonte, biblioteca externas ou qualquer coisa parecida. Ele vai transformar todos os símbolos do seu programa(como variáveis e funções) em endereços de memoria física, que pode ser tanto para um arquivo no computador quanto para a memoria.

Com toda a certeza, você deve saber que o computador não usa o nome da sua função que você declara em “C” para chamá-la/executá-la. Nem o nome das variáveis, que são acessadas através de endereços de memoria também. Isso também é uma afirmação valida para quando você programa em ASM, os rótulos que você usa não são usados diretamente. Quem traduz previamente esses nomes para endereços físicos e é responsável por ligar todos esses elementos é o programa conhecido como linker.

Para construir nosso SO, precisamos mudar um pouco o comportamento desse linker, através de um linker script(que posso encurtar para chamar apenas de linker, para facilitar).

Porque precisamos de um linker?

Precisamos que o kernel siga alguns padrões para que ele possa ser reconhecido e iniciado corretamente pelos gerenciadores de boot(tal como o LILO e GRUB). Para que isso aconteça, precisamos seguir o padrão “Multi Boot”, que vamos falar um pouco mais a frente o como vamos fazer isso.

Mais para o futuro, vamos precisar que construtores e destrutores sejam executados antes do inicio da função “main()” e depois que ela terminar. Para isso, através do linker, vamos precisar saber onde eles estão guardados na memoria.

Precisamos disso porque não existe maneira de garantir isso em ASM ou em C apenas, se existisse como, poderíamos usar o linker script padrão do sistema mesmo.

O que é o Multi Boot(mboot)?

Inicializar o sistema do computador é um trabalho complicado. Existem diversos hardwares com varias formas que ficaram ainda mais complexas com o passar dos anos por causa de modos de compatibilidade e outras coisas. Para que você não se preocupe com a inicialização do computador, foi criado os gerenciadores de boot. Para que o gerenciador de inicialização entenda que seu programa é um kernel iniciável de forma mais fácil, foi criado o padrão multi boot. Existem outras formas, mas vamos usar o mboot apenas.

Para que o gerenciador de boot saiba que seu kernel usa o padrão, e para que ele receba algumas informações necessárias, no começo do arquivo do kernel(ele procura nos primeiros 8K bytes) você deve colocar uma estrutura de dados que contem um numero magico(para que ele possa identificar o começo da estrutura entre os dados) e mais alguns dados. Tudo isso mais a frente vamos codificar diretamente em ASM, mas por enquanto, precisamos apenas garantir que o linker coloque a estrutura no começo.

O multi boot tem mais de uma versão, que se diferencia pelas informações que tem na estrutura binaria no começo do kernel. Como não vamos precisar das funcionalidades da segunda versão, vamos implementar apenas a primeira mesmo.

Para entender melhor em detalhes o formato binário do mboot, você pode consultar o manual da GNU.

Mas, por enquanto não vamos implementar nada além do linker script, só depois, então não se preocupe por estar um pouco abstrato demais.

Construindo nosso linker script

memory

Esquema da memoria do nosso kernel

O Nosso script vai ser algo parecido com isso:

ENTRY(start)

SECTIONS
{
	. = 1M ;
	.text : ALIGN(4096)
	{
		code = .; _code = .; __code = .;
		KEEP(*(.mboot*))
		*(.header*) ;
		*(.text*) ;

		*(.gnu.linkonce.t*) ;
	}
	code_end = .; _code_end = .; __code_end = .;

	.rodata : ALIGN(4096)
	{
		rodata = .; _rodata = .; __rodata = .;

		start_ctors = .;
		*(SORT(.ctors*))
		end_ctors = .;

		start_dtors = .;
		*(SORT(.dtors*))
		end_dtors = .;

		*(.rodata*)
		*(.gnu.linkonce.r*)
	}
	rodata_end = .; _rodata_end = .; __rodata_end = .;

	.data : ALIGN(4096)
	{
		data = .; _data = .; __data = .;

		*(.data)
		*(.gnu.linkonce.d*)
	}
	data_end = .; _data_end = .; __data_end = .;

	.bss : ALIGN(4096)
	{
		bss = .; _bss = .; __bss = .;
		sbss = .;
		*(COMMON)
		*(.bss)
		*(.gnu.linkonce.b*)
	}
	bss_end = .; _bss_end = .; __bss_end = .;

	end = .; _end = .; __end = .;
}

Vamos agora a uma explicação mais detalhada. Vamos focar em pontos importantes mais a frente.

ENTRY(start)

Primeiramente, precisamos dizer que a função que vamos iniciar chamando é a “start“. Porque não “main()“, como o tradicional? Porque vamos precisar executar alguns códigos antes de chamar a função “main()”, que vamos descobrir melhor em passos futuros.

	. = 1M ;

Depois, vamos colocar os dados do kernel apenas depois da posição de memoria 0x00100000, mas porque? O gerenciador de boot usa a mesma memoria que o kernel, então essa memoria pode estar sendo usada para outras finalidades ou para passar dados para o kernel que acabou de iniciar. Além do que essa memoria pode estar sendo mapeada para outras coisas.

Essa posição inicial é relativa quando o programa estiver na memoria, seguindo os padrões de um arquivo executável que o gerenciador de boot vai preparar para executar. Ou seja, o arquivo real do kernel não vai ter 1M sem nada no começo(vai existir uns poucos dados do formato do binário, na verdade), apenas vai existir esse 1M de diferença quando ele estiver sendo executado na memoria.

	.text : ALIGN(4096)
	{
	....
	.rodata : ALIGN(4096)
	{
	....
	.data : ALIGN(4096)
	{
	....
	.bss : ALIGN(4096)
	{
	....

Vamos passar por todas essas secções criadas de uma vez. Elas são padrão em programas, porque basicamente precisamos das seguintes seções em um programa:

  • .text O código executável do programa fica nessa área de memoria
  • .rodata Como é de se esperar, as variáveis constantes e globais ficam nessa área de memoria
  • .data Área das variáveis globais que são inicializadas em tempo de compilação
  • .bss Variáveis globais não inicializadas ficam aqui

Outras seções, como common, sbss, gnu.linkonce.* e outras são apenas variações dessas áreas, mas são usadas pelo compilador e estão ai apenas para facilitar e prevenir erros no futuro.

Também vale a pena ressaltar que essas secções são alinhadas com paginas de memoria do tamanho de 4K, para ser mais fácil fazer paginação no futuro e ser mais otimizado.

		KEEP(*(.mboot*))

Lembra que precisamos garantir que a estrutura que identifica o kernel como mboot no começo do arquivo do kernel que vai ser gerado? É aqui que garantimos isso. Todas as seções que comecem com .mboot vão ser colocadas nesse local, no começo. O KEEP garante que ele vai ser colocado ali, independente se o linker trocar de lugar essa secção(por causa de otimizações ou por não ser referenciado em nenhum outro lugar).

		start_ctors = .;
		*(SORT(.ctors*))
		end_ctors = .;

		start_dtors = .;
		*(SORT(.dtors*))
		end_dtors = .;

Mais um pedaço de código que vamos usar mais a frente. Vamos ter a indicação de onde começa e termina os construtores e destrutores de objetos globais, justamente para executá-los no momento certo.

	end = .; _end = .; __end = .;

Futuramente, para saber qual memoria esta disponível para ser usada, vamos precisar saber onde a memoria usada pelo kernel termina. Essa marcação vai ajudar nisso. Usamos vários nomes para essa variável por comodidade.

	code_end = .; _code_end = .; __code_end = .;
....
	rodata_end = .; _rodata_end = .; __rodata_end = .;
....
	data_end = .; _data_end = .; __data_end = .;
....
	bss_end = .; _bss_end = .; __bss_end = .;

Adicionei essas marcações apenas para facilitar encontrar o final das secções. Você poderia até fazer sem elas, mas faz mais sentido fazer com as marcações. E fica mais intuitivo quando for implementar o multi boot no ASM.

Palavras finais

O que temos que focar aqui é a estrutura de como o kernel vai ser usado montado na memoria(e também no arquivo) e o que precisamos para ser compatível com o multi boot.

Aprender a linguagem de script do linker é algo que não é necessário, apenas precisamos saber por alto ela para que possamos garantir algumas coisas.

Algumas coisas aqui só vão ser utilizados mais para a frente, e a questão do boot nos próximos posts.

Caso alguém note algo que eu escrevi errado, algum conceito que esteja errado e/ou alguma dica, pode mandar mensagem para minha pessoa de forma amigável, tanto por comentário quanto por qualquer outra forma.

PS:. BUG FIX

Certo, enquanto produzia a parte a frente do post, notei que alguns bugs(o que é estranho, porque eu não erro :p) existiam no código antigo. Como já dei “push” e voltar agora seria muito complicado, caso notem alguma coisa diferente no repositório do que esta no post, pegue a ideia do que esta no post e use a versão correta. A versão correta pode ser ou do código que o post esta mostrando, ou a versão que vai aparecer.

De qualquer forma, eu já acertei esse post. Mas fica uma lista do que era:

  • Erro no CMake.
    • Faltou incluir um código para compilar o kernel em outra pasta sem ser a do projeto.
    • Pequenos ajustes para que o CMake funcionasse sem a opção de uma biblioteca C, que eu vou adicionar no futuro.
  • O Linker faltou algumas marcações

Encare essa nota de rodapé como um log de alterações do futuro. Não precisa se preocupar com essa secção, e se encontrar algum erro no continuo espaço tempo, use o do futuro :p.

Advertisements

Sistema Operacional miniOS-0.03: Build automatizado

Estou continuando a serie de posts sobre como fazer um sistema operacional, fazendo o meu próprio sistema. (Quase como se não tivesse passado muito tempo….)

Nesse post, espero falar um pouco mais sobre o gerenciamento do build, ou seja, muita coisa sobre o CMake. Não espere que esse seja um tutorial sobre CMake, então não vou entrar em conceitos básicos do gerenciados de build. Também vou adiantar conceitos que não vão ser usados agora.

Planejo falar um pouco sobre como vamos usar o compilador mais para o final.

Quando essa parte do tutorial estiver publicada no meu blog, você já poderão conferir no repositório os fontes dos arquivos.

Porque?

Originalmente, as primeiras impressões que eu tive com o build de um sistema operacional foi usando nada(apenas o compilador na forma pura) ou no máximo com o uso do Makefile.

O Makefile é muito cru e sinceramente não fornece capacidades tão boas para ser portável, para realizar tarefas mais complexas ou mais simples de forma fácil. Mas, apesar disso, alguns materiais mais antigos só tinham o Makefile a disposição, e por isso ele era amplamente usado.

Quando pensei em melhorar o processo de desenvolvimento do SO, automaticamente pensei em usar um gerenciador de build mais pratico. Como eu queria melhorar minha pericia com o CMake, vamos usar ele mesmo.

O que seria Build?

Quando falo em build(sou mais acostumado a usar essa palavra, não me passa na cabeça uma melhor em português agora), eu na verdade estou me referenciando ao processo de construir o programa.

Construir o seu programa pode ter varias etapas, como por exemplo transformar arquivos fonte em arquivos que vão ser usados em produção, gerar informações que seu programa vai precisar, configurar o programa final que você vai gerar, e por ai vai. Tudo isso com finalidade de facilitar processos repetitivos.

No caso do nosso sistema operacional, temos que escolher as opções que vamos usar com o compilador atual, escolher que biblioteca C vamos usar e qual a arquitetura que vamos gerar o kernel. Claro, além de compilar todo o kernel.

Quem é acostumado com o kernel do linux, vai lembrar do seu tradicional “make menuconfig”. Seria mais ou menos essa parte do processo.

Como fazemos?

No arquivo /CMakeLists.txt do código fonte podemos escolher essas opções através de variáveis. Para alterar essas variáveis eu recomendo usar o comando:

ccmake .

Se você passar por esse arquivo, notara que ele incluí diversos outros arquivos e usa algumas funções. Vamos mais a frente falar sobre esses arquivos e sobre o que essas funções fazem.

/minios/kernel/arch/CMakeLists.txt

O primeiro arquivos importante. Não vou passar linha por linha dele aqui, mas você pode previamente ver ele no link acima.

Basicamente, temos uma única função aqui, LOAD_ISA_PROFILE, que aceita dois argumentos, a ISA e a plataforma. Como existem códigos que podem variar dependendo desses dois parâmetros, essa função serve para passar os arquivos e configurações desses parâmetros. Caso não exista um arquivo nas pastas que sobrescrevam o padrão(veja como isso é feito no código fonte), essa função passa a construir o kernel com todos os arquivos *.c, *.cpp e *.s que se encontram dentro das pastas padrão para essa plataforma e a ISA.

Certo, mas o que é essa tal de ISA? Instruction set architecture(ISA) é o modelo abstrato de um computador. Como vamos ter programar em ASM em alguns pontos, esse tipo de código normalmente varia de acordo com a plataforma e/ou a ISA que você esta usando. Por isso, se existisse mais de uma implementação do OS para outras ISAs, aqui que separaríamos o código que vamos utilizar.

Porque separamos a ISA da plataforma? Sinceramente, não sei se isso é tão importante. Creio que seria importante caso fosse feito otimizações ou algo assim, por isso separei.

/minios/kernel/compiler/CMakeLists.txt

No link acima, podemos ver o arquivos que define a função LOAD_COMPILER_PROFILE, que aceita como parâmetro o compilador que vamos usar para compilar o nosso kernel.

Essa é uma parte interessante, porque precisamos disso? Porque, todo compilador, apesar de que você não saiba disso(ou talvez saiba, ou imagine isso), insere código a mais nos seus programas. Que códigos são esses? Códigos para que seu programa funcione em determinados sistemas operacionais, para que você utilize bibliotecas que o sistema fornece e até mesmo inserindo funções extras que não dependem de bibliotecas externas.

Porque saber disso é importante? Porque, no sistema operacional você não tem como contar com essas comodidades, então você precisa desabilitar. O que significa isso? Quer dizer que, a partir de agora, todas as bibliotecas que você usar devem ser suas e não ligadas dinamicamente, além do que você não tem uma inicialização do seu programa, e mais para a frente nos vamos fazer essa inicialização, que chamamos de boot do sistema operacional.

Como vamos utilizar o GCC, para que a função do cmake que eu fiz funcione, temos que criar o arquivo:

/minios/kernel/compiler/gcc/flags.cmake

Dentro desse arquivo, vamos dizer para o gcc usar as sequintes flags para compilar nosso kernel:

set(COMPILER_C_FLAGS "-Wall -Wno-main -nostdlib -nostdinc -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-stack-protector -ffreestanding")
set(COMPILER_CXX_FLAGS "-ffreestanding -O2 -Wall -Wextra -nostdlib -nostdinc -fno-builtin -fno-stack-protector -Wno-main -nostartfiles -fno-exceptions -fno-rtti")

Uma lista grande de flags, que basicamente quer dizer: Não precisa construir a função “main()” do programa, deixa que vamos resolver. Não utilize bibliotecas do sistema e nem a biblioteca básica. Não use funções internas do GCC e retire as funções incompatíveis de C++.

Sobre C++: Inicialmente, construtores e destrutores não vão estar 100% disponíveis, além de RTTI e exceções. Construtores e destrutores vamos dar um jeito no futuro(eu rezo para isso…), mas o resto das coisas é mais complicado, porque requer que implementamos partes da biblioteca padrão do C++, isso da tanto trabalho que é melhor programar sem…

Palavras finais

Vejam o código, porque aqui é apenas uma passada rápida. Sinto que eu deveria documentar mais algumas coisas, mais por enquanto esses posts ficam como fonte de documentação.

Qualquer pessoa pode usar o que quiser nesse ponto, ou até usar o GCC puro sem gerenciador de build, mas como eu já fiz isso e sei o que vai precisar, eu coloquei essa etapa antes. Sendo bem sincero, essa estrutura foi pensada mais no processo final quando eu programei o SO da primeira vez.

Como importante, o que se tem que tirar daqui é o fato de não poder contar com nada que o compilador e SO nativo oferecem para produzir um kernel. Nada de bibliotecas e/ou inicialização. Se quiserem saber mais também, o manual do GCC é uma boa para procurar sobre essas flags de compilação.

Se notarem algo de estranho, me falem. Possivelmente posso ir corrigindo esses posts com o tempo também…


Sistema Operacional miniOS-0.02: Configuração do Ambiente

Ola terráqueos. Voltei.

Estou continuando a serie de posts sobre como fazer um sistema operacional, fazendo o meu proprio sistema.

Agora vou tratar da configuração do ambiente, ou em outras palavras, deixar as ferramentas que não são de desenvolvimento ajustadas para facilitar. Basicamente vamos falar do diretório /tools do nosso projeto.

Quando essa parte do tutorial estiver publicada no meu blog, você já poderão conferir no repositório os fontes dos arquivos.


/tools/boot_qemu.sh

Primeiramente, vamos falar sobre esse arquivo, que vai ser o script que vai ajudar a inicializar o nosso kernel.

Para testar o kernel, eu optei por usar o qemu, que é um emulador de maquinas bem completo e com suporte a virtualização, ou seja, podemos também usar o kernel um uma maquina virtualizada com pouco esforço.

Não vou explicar o script, acho que isso não é necessário, mas a função básica é verificar se o qemu de 32 bits esta disponível no sistema, se não estiver, usar o comando padrão. Depois, se a variável de ambiente estiver sem nenhum valor, ele procurar a imagem do kernel com o nome padrão(“miniOS”) e inicializa o sistema. Como vamos precisar por questões de debug saber o conteúdo dos registradores, esse script vai esperar um tempo para que a maquina levante o sistema e vai mostrar o conteúdo dos registradores.

O objetivo vai se rodar esse comando depois de construir o “executável” do sistema, dentro do diretório central do código fonte.

Originalmente, no tutorial do Bran e em outros, se usa um “emulador” antigo mas que funcionava bem legal, que era o bochs. O bochs é funcional e foi muito usado durante muito tempo, mas ele sempre foi mais difícil de usar, além de ter alguns “bugs” que não me pareciam muito fáceis de lidar… Por isso resolvi usar uma ferramenta mais fácil e moderna, por isso o qemu ;).

Para quem quer usar uma interface, eu recomendo o aqemu. Ele tem a opção de colocar um kernel para dar boot, e fica mais pratico. Não vou abordar isso, tanto porque acho o script mais fácil ^^.

Como sempre, o código agora já esta no repositório.


Sistema Operacional miniOS-0.01: Estrutura de diretórios

Voltei! Dessa vez, para começar e por enquanto, um post mais fácil, pelo menos. Vamos falar sobre a estrutura de diretórios do projeto de um sistema operacional, e o que temos que ter e onde, além de algumas dificuldades que já começou a aparecer.

Certo, vamos mostrar a estrutura de diretórios no geral, e depois eu vou explicar o porque de cada pasta é importante e para que serve, além de levantar umas ideias gerais sobre o futuro do que vai conter em cada uma das pastas.


/src/
/src/compiler/CC
/src/kernel/boot/
/src/kernel/kpp/
/src/kernel/fs/
/src/kernel/arch/
/src/kernel/arch/ISA/
/src/kernel/arch/ISA/PLATFORM
/src/libc/
/tools/

/tools

Esse diretório é o que temos um carinho especial. Aqui vamos colocar as ferramentas que vão nos ajudar a desenvolver nosso sistema operacional, vão facilitar o desenvolvimento, na verdade.

Também vamos colocar alguns programas não relacionados com o kernel em si, pelo menos porque vão ser códigos tão pequenos que não precisam criar repositórios a parte para eles, sendo mas simples deixar ai mesmo.

/src

Aqui vai ficar os códigos relacionados ao kernel, diretamente ao que vai entrar no kernel. Provavelmente, tudo o que vai ser compilado e construido dentro do kernel vai ficar em alguma pasta por aqui.

Na verdade, os códigos não vão ficar aqui, mas em pastas separadas pelas suas funções, como explicado abaixo.

/src/compiler/

Aqui começamos a entrar em detalhes interessantes. Vamos começar com um pequeno conto, o de como um programa normal funciona.

Quando você inicia um programa, você já começa com a sua função main(), certo? Mas quem iniciou as suas variáveis globais? E quem configurou algumas funções? Quem criou os arquivos stdin, stdout e stderr?

Você pode argumentar que tudo isso é função das variáveis globais(por exemplo, stdin, stdout e por ai vai…), mas, isso ainda tem funções especificas que você tem que saber sobre o ambiente para funcionar. Esse tipo de função é de códigos específicos do compilador! No futuro, esses códigos que vamos precisar, vão estar nesse diretório. A medida que precisarmos, eu vou explicando o porque, para que e como vamos precisar disso, então relaxe por enquanto e aceite que precisamos.

Uma coisa a mais a se considerar é que existem vários compiladores, então para cada compilador eu vou precisar de algum código especifico. Essa parte não tem jeito, muita coisa pode ser reusada, mas infelizmente normalmente os codigos são diferentes um dos outros. Para manter separado, vamos criar um diretório também para cada compilador, como por exemplo:


/src/compiler/gcc

Para cada compilador, um diretório. O como vamos usar assim, mais para frente a gente fala(próximo post?).

/src/kernel/

Vamos colocar aqui os códigos especificamente feitos para o kernel. Aqui vai ficar a lógica do kernel e coisas que tem a ver com arquitetura.

/src/kernel/boot/

Precisamos de alguns códigos para inicializar o kernel. Aqui é que ficam esses códigos, aqui é a cola entre o gerenciador de boot e o seu kernel, digamos que a interface, a passagem.

Não confundir com o gerenciadores de boot, que inicializa e carrega o kernel. Isso não é trabalho de um kernel, e não vamos fazer um, então deixa essa parte quieto.

/src/kernel/kpp/

Não podemos contar com nada, ou com muito pouco pelo menos, no desenvolvimento de um kernel. Isso quer dizer que não podemos usar as bibliotecas padrões que normalmente usamos, e isso quer dizer que vamos precisar reimplementar algumas coisas.

Para todas essas coisas que iremos reimplementar de algoritmos básicos(como vector, lista, etc…), vamos colocar nesse diretório.

Sobre o nome do diretório, é as iniciais de kernel plus plus, meio sem sentido mais com sentido…

/src/kernel/fs/

Nada de novo aqui, apenas vai ser o diretório com códigos relacionados ao sistema de arquivo.

Mais no futuro a gente resolve o que vai colocar aqui, principalmente porque não sabemos ainda como vai ser a cara de como lidar com sistema de arquivos.

/src/kernel/arch/

Com certeza vamos precisar utilizar algum código que seja especifico para alguma plataforma, e nesse diretório é que vamos colocar esses códigos que vão depender de alguma arquitetura.

O objetivo é que todos os códigos que dependam de alguma plataforma fiquem aqui, basicamente como eu quero isolar o código em ASM também, gostaria que todos os códigos ficasse aqui.

Mas não é sempre que podemos isolar o código ASM do código em C/C++. Existem alguns códigos de inicialização e alguns outros códigos que precisam ser escritos em ASM para serem mais portáveis ou funcionarem como você quer.

/src/kernel/arch/ISA

Dentro dos códigos mais específicos e em ASM da arquitetura, ainda temos códigos que dependem das informações especificas da ISA da maquina. Essas declarações e códigos vão ficar aqui.

Aqui vai ficar uma parte um pouco chata do trabalho já que também vamos lidar possivelmente com C, que é construir alguns tipos da ISA. pode ser que usemos algumas soluções mais com cara de C++, mas vai ser difícil…

Também temos que ter uma forma de escolher a ISA em que nosso sistema estará compilando. Isso vai fazer parte do sistema de build.

Uma coisa importante a se ressaltar é que o nome “ISA” no nome da pasta vai ser substituído pelo nome da ISA que você esta implementando, porque você pode ter varias implementações. Por exemplo, se você estiver implementando a ISA da arquitetura x86, a pasta seria:


/src/kernel/arch/x86

Isso tudo porque depois vamos usar isso para identificar e configurar a plataforma para onde vamos gerar o nosso kernel.

/src/kernel/arch/ISA/PLATFORM

Aqui ficaram códigos ainda mais específicos das plataformas, como na ISA, só que menos genéricos ainda e mais dependentes do hardware.

É meio difícil de explicar, e fica meio nebulosa a fronteira entre os códigos da ISA e os da plataforma, mas acho que é legal criar essa divisão para aproveitar códigos usados entre plataformas. Talvez seja trabalho demais para pouca coisa também, mas nesse momento não estou ligando para isso.

Assim como no caso da ISA, se estivermos por exemplo implementado a plataforma i386 da ISA x86, faríamos:


/src/kernel/arch/x86/i386

/src/libc/

Uma das piores coisas de se contruir um sistema operacional é que você não tem acesso a praticamente nenhuma ferramente pronta. Isso quer dizer que você mal pode contar com o seu compilador, imagine as bibliotecas basicas de C ou C++.

Para facilitar esse trabalho, de reimplementar todas as bibliotecas, você pode portar algumas bibliotecas. Como a biblioteca basica de C é muito importante, é aqui que teremos os ports de algumas bibliotecas para o nosso sistema.

Porque uma pasta só para isso? Porque o ideal é que possamos trocar a implementação de forma mais fácil, então aqui dentro terremos varias opções que poderão ser escolhidas pelo sistema de build do nosso kernel.

Fim!

Bom, basicamente seria isso ai, essa vai ser a estrutura de diretórios que já adianta alguma coisa para saber o que vamos fazer.

Entrei em vários pontos aqui, mas não falei como eles serão resolvidos, algumas partes porque eu realmente vou explicar melhor isso no futuro, outras porque ainda quero descobrir a melhor forma de resolver.

Mas o mais importante é, se alguem ler isso aqui e alguma duvida surgir, perguntem aqui nos comentários, que eu vou aprimorando cada vez mais o post e também vou tentando responder as duvidas. Tentem não ser tão ansiosos e se concentrar no post de agora!


Sistema Operacional miniOS-0.00: Começando a fazer um do zero, o vai ser preciso?

Construindo um Sistema Operacional

Olá amiguinhos! Recentemente, eu comecei e terminei de construir um sistema operacional. OK, eu me orgulho disso, achei ele muito bem feito, mas ainda tem muitos pontos que eu vi que eu podia melhorar.

Então, agora, eu começo uma serie de posts em que eu pretendo refazer esse sistema, dessa vez muito mais organizado, comentando aqui no blog com posts explicando cada passo, comentando o código de forma realmente legal e inserindo alguns elementos a mais também, tudo isso com a opinião de vocês meus leitores.

Eu pretendo evoluir as ideias que eu já usei e/ou usar algumas ideias melhores que eu tinha pensado na época, mas como eu tinha tempo a cumprir eu não fiz. Além que também eu vou tentar refatorar muito do código que eu já tinha usado.

O plano de release

Como eu já fiz uma vez e tenho alguma noção, eu sei mais ou menos o caminho em ordem do que fazer, e em cada post eu vou tentar abordar cada um desses tópicos. Pelo menos esse é o plano…

Lista do que vou desenvolver em provável ordem:

  1. Estrutura de diretórios
  2. Configuração do Ambiente
  3. Build automatizado
  4. Linker
  5. Código ASM de inicialização
  6. Código “C” de inicialização
  7. Monitor
  8. kprintf
  9. Teste?
  10. Construtores globais
  11. Destrutores globais
  12. Biblioteca padrão “C
  13. Gerencia de memoria no final do kernel
  14. Algumas estruturas de dados
  15. Global Descriptor Table (GDT)
  16. Interrupt Descriptor Table (IDT)
  17. Interrupt Service Routines (ISR)
  18. Interrupt ReQuest (IRQ)
  19. Programmable Interval Timer (PIT)
  20. Teclado
  21. Interface para sistema de arquivo genérica
  22. Gerencia de memoria com paginação
  23. Multiprocesso
  24. Tratamento de Exceções?

Lembrando que provavelmente eu vou alterar esse lista com o tempo, porque eu posso mudar de ideia com relação a ordem ou posso quebrar em mais de um tema esses tópicos, coisas assim.

A parte que eu inicialmente acho mais difícil

Testes, esse é a parte realmente nova e que pode levar tempo. Requer que eu crie mais coisas também.

Testes influenciam o desenvolvimento depois, porque a arquitetura de testes influencia muito no desenvolvimento.

A complexidade é grande, e eu vou abordar melhor o porque no post sobre testes, em que eu devo resolver e propor alguma coisa.

O começo, algo feito

Como eu sempre gosto de deixar algo sempre pronto, com bônus inicial eu já criei o repositório para quem quiser ir acompanhando, no github.

Na verdade, é o repositório antigo que eu tinha feito, mas eu deletei(criei um branch, na verdade…) e recomecei do zero. Mas quem quiser já pode ir acompanhando, mas acho melhor que vocês falem comigo por aqui e vejam os posts aqui também ;).


%d bloggers like this: