Um sistema operacional de brinquedo, 32 bits finalmente implementa uma concha interativa simples 
Lista de tarefas:
Existem dois discos rígidos no total, o próprio sistema é instalado no disco principal e o modo de inicialização do MBR é adotado e o processo de MBR-> Boot Loader-> Kernnel é usado.
MBR está localizado em 1 setor começando LBA 0号扇区
Boot Loader está localizado em 4 setores começando com LBA 2号扇区do disco
Kernel está localizado em 200 setores começando com LBA 9号扇区
O sistema de arquivos é implementado no disco escravo. Isso pode não ser muito razoável. Se o sistema de arquivos for baseado na lógica do sistema comercial, ele deve ser implementado agora e o sistema operacional será instalado na partição correspondente.
Pagagem de memória, uma página é 4KB
O gerenciamento de memória adota o gerenciamento de bitmap e se distingue pelo tamanho ao alocar a memória. Se for maior que 1024 bytes, será alocado diretamente pela página.
Se for menor que 1024 bytes, com base na alocação de arena por página, use o blockchain ocioso na arena para alocação e controle.
Por conveniência, embora o mecanismo de paginação esteja ativado, a função de comutação das páginas e discos de memória não é implementada.

PCB tem 1 tamanho de página
A essência central do agendamento de threads é alternar a PCB controlando a interrupção do Pointer ESP by Clock. A prioridade é refletida no comprimento da fatia do tempo de execução de cada encadeamento.
A implementação do processo é baseada em threads e a seleção do TSS é baseada no Linux, usando o TSS único para fazer backup de pilhas de nível 0 e ponteiros de pilha de nível 0. A maior diferença de um encadeamento é que o processo possui um endereço de tabela de páginas no PCB, que é a maior diferença entre um processo e um thread. O processo realmente tem seu próprio espaço de memória virtual independente.
Não há algoritmo eficiente na programação, basta usar o agendamento de loop da fila
Implementação do tópico ocioso
A implementação do encadeamento ocioso é muito simples. Quando você obtém a programação, você o bloqueia e o entrega da CPU. Quando o agendador executar o despacho novamente, se nenhum thread ou processo pronto for encontrado na fila pronta, o encadeamento ocioso será despertado. Neste momento, o encadeamento ocioso suspende a CPU através hlt . Quando a fatia de tempo for usada e a CPU não encontrar um processo ou thread pronto, continue substituindo o encadeamento ocioso pela CPU. Neste momento, o IDLE continuará bloqueando -se e começará a repetir o processo de agendamento acima.
// 空载任务
static void idle ( void * arg ) {
while ( 1 ) {
thread_block ( TASK_BLOCKED );
asm volatile ( "sti; hlt" : : : "memory" );
}
}Fork de processo
O garfo do processo é primeiro copiar o PCB do processo atual e, em seguida, crie uma nova tabela de páginas através do bitmap de pool virtual do processo atual. A correspondência do endereço virtual é exatamente o mesmo que no processo original. Finalmente, um site de interrupção é forjado e o processo filho é adicionado à fila de agendamento e aguarde a execução do agendamento. No local de interrupção forjado, o EAX no PCB do processo infantil é modificado para 0, o que significa que o valor de retorno do garfo está 0 no novo processo, enquanto o EAX no PCB do processo pai permanece inalterado, representando o PID do processo infantil. O processo pai retorna até o final da chamada do sistema, enquanto o processo filho retorna diretamente através da função de saída de interrupção.
Exec
A implementação do EXEC primeiro carrega o arquivo ELF do disco para a memória, depois altera o nome do processo no PCB do processo atual e coloca os parâmetros exigidos pelo processo a ser executado no registro acordado e modifica o EIP para o ponto de entrada da ELF, perdoa o site de interrupção e executa o novo processo imediatamente chamando diretamente a função intercruz.
Entre eles, o ponto de entrada da ELF é implementado pela implementação de um próprio CRT extremamente simples, que fornece uma entrada de _Start e empurra os registros de parâmetros acordados para a pilha de três níveis e chama a função principal do comando externo para realizar a passagem dos parâmetros.
[bits 32]
extern main
extern exit
; 这是一个简易版的CRT
; 如果链接时候ld不指定-e main的话,那ld默认会使用_start来充当入口
; 这里的_start的简陋实现,充当了exec调用的进程从伪造的中断中返回时的入口地址
; 通过这个_start, 压入了在execv中存放用户进程参数的两个寄存器。然后call 用户进程main来实现了向用户进程传递参数
section .text
global _start
_start:
;下面这两个要和 execv 中 load 之后指定的寄存器一致
push ebx ;压入 argv
push ecx ;压入 argc
call main
; 压入main的返回值
push eax
call exit ; 不再返回,直接调度别的进程了,这个进程直接被回收了Processo espera
Após o garfo e a execução de um comando local, para não aparecer, o processo pai precisa terminar no processo de espera local.
A implementação aqui é inserir a chamada do sistema Sys_wait, atravessar toda a fila do processo, encontrar o processo cujo processo pai seja seu próprio estado suspenso e, em seguida, obtenha o valor de retorno em sua PCB, recicle as tabelas de diretório de PCB e página e remova -a da fila de agendamento. Se nenhum processo infantil pendente for encontrado após o Traversal, bloqueie -se e aguarde o processo da criança acordar.
Sair do processo
Durante a execução, os comandos externos são realmente embrulhados por um CRT simples criado por si mesmo. O principal do comando externo de chamada CRT simples obterá seu valor de retorno no final, passará para sair e depois chamará a saída
A implementação aqui faz principalmente três coisas:
Todo o processo de carregar comandos externos e executá -los
Primeiro, o comando externo precisa fornecer uma função int main(int argc, char **argv) . Ao vincular, você precisa trazer um start.o CRT simples caseiro e, finalmente, escrever os comandos externos compilados no sistema de arquivos.
Quando um comando externo deve ser executado, o processo atual força um processo, executando no novo processo, o processo atual executa aguarda, passa um endereço para aceitar o valor de retorno do processo filho e, em seguida, bloqueia e aguarda o retorno do novo processo. The new process loads external commands from the file system to memory in exevc, and changes the relevant content in pcb to the information of external commands, and modifies the eip in the pcb interrupt stack to the CRT's _start entry (at this time the new process has completely replaced itself with the external command process to be executed), and finally uses the interrupt exit function intr_exit to fake the interrupt exit and enter the CRT and then enter the main of the external comando. Quando a execução principal do comando externo termina e retorna, o CRT chama de saída pelo valor principal de retorno, coloque o valor principal de retorno em sys_exit na posição correspondente do processo PCB, recicle todos os recursos, exceto a tabela de diretório de PCB e página e acorde o processo pai e bloqueie o processo filho. After the parent process is awakened, the system call sys_wait gets the return value from the pcb of the pcb of the pcb of the pcb of the pcb of the pcb of the pcb of the pcb of the pcb of the pcb of the pcb of the pcb, cleans up the page directory table of the pcb of the child process, removes the child process from the dispatch queue and the global queue, and returns the child process pid. Nesse ponto, o processo infantil foi executado e é completamente reciclado.
A implementação do sistema de arquivos imita o inode de sistemas do tipo Unix
A partição limita o número de inodos 4096. A CPU opera o disco rígido de acordo com o tamanho do bloco (cluster) e um bloco é definido como um setor, com 512 bytes.
O Inode suporta 12 blocos diretos e 1 tabela indireta de primeiro nível. Um bloco é de 512 bytes em um setor; portanto, um único arquivo suporta até 140 * 512 bytes.
estrutura de inode

Layout do sistema de arquivos

A correspondência entre o descritor de arquivo e o inode

A implementação do pipeline depende da estrutura do arquivo no sistema de arquivos. Sua essência é substituir o inode que a estrutura do arquivo deve corresponder originalmente a um espaço de buffer de anel no espaço do kernel.
// 因为管道也是当作文件来对待,因此file结构体在针对真实文件和管道是有不同的意义
struct file {
// 文件操作的偏移指针, 当是管道是表示管道打开的次数
uint32_t fd_pos ;
// 文件的操作标志,当是管道是一个固定值0xFFFF
uint32_t fd_flag ;
// 对应的inode指针,当是管道时指向管道的环形缓冲区
struct inode * fd_inode ;
}; Como o espaço do kernel é compartilhado, a comunicação entre diferentes processos pode ser alcançada por meio de pipelines de leitura e gravação. A leitura e a gravação do pipeline são encapsuladas em sys_write e sys_read ; portanto, não há diferença entre operar o pipeline e operar arquivos ordinários.
A essência da redirecionamento é alterar o endereço da tabela de descritores globais correspondentes na tabela Descritor de arquivo PCB e, em seguida, a operação de leitura e gravação do descritor de arquivo correspondente é apontado para o novo arquivo.
A implementação do caractere do pipeline | No shell é alcançado redirecionando a entrada padrão e a saída padrão para o pipeline.
int32_t sys_read ( int32_t fd , void * buf , uint32_t count ) {
if ( fd == stdin_no ) {
if ( is_pipe ( fd )) {
// 从已经重定向好管道中读
ret = pipe_read ( fd , buf , count );
} else {
// 从键盘获取输入
}
} else if ( is_pipe ( fd )) {
// 读管道
ret = pipe_read ( fd , buf , count );
} else {
// 读取普通文件
}
return ret ;
}
int32_t sys_write ( int32_t fd , const void * buf , uint32_t count ) {
if ( fd == stdout_no ) {
if ( is_pipe ( fd )) {
// 向已经重定向好管道中写入
return pipe_write ( fd , buf , count );
} else {
// 向控制台输出内容
}
} else if ( is_pipe ( fd )) {
// 写管道
return pipe_write ( fd , buf , count );
} else {
// 向普通文件写入
}
}