Diretório de artigos
Prefácio
O blog registra a operação do experimento no Capítulo 7 de "Operating System Truth Restore" ~
Ambiente experimental : ubuntu18.04+VMware, baixe e instale Bochs
Conteúdo da experiência :
- Escreva um manipulador de interrupções (Operação 8259A Ativar interrupções).
- Melhore o manipulador de interrupções (chame a versão em linguagem C da função do manipulador de interrupções na versão assembly do intrXXentry).
- Use o contador/temporizador 8253 para definir a frequência com que ocorrem as interrupções do relógio. (Acelere o sinal de interrupção).
conhecimento pré-requisito
Conceito de interrupção
O conceito de processamento de interrupção (interrupção) : Como a CPU aprende algo que aconteceu no computador, a CPU suspende o programa que está sendo executado e muda para o programa que trata o evento. Quando este programa é executado, a CPU continua a executar o programa agora mesmo. . Todo o processo é chamado de tratamento de interrupções, também conhecido como interrupções.
O sistema operacional do sistema operacional é orientado por interrupções. Sem interrupções, o sistema operacional não pode fazer quase nada!
Classificação de interrupção
As interrupções podem ser divididas nos 2 tipos a seguir.
- Interrupção externa: As interrupções externas à CPU são chamadas de interrupções externas.
- Interrupções internas: As interrupções de dentro da CPU são chamadas de interrupções internas.
A essência do mecanismo de interrupção : A essência do mecanismo de interrupção é chamar o manipulador de interrupção correspondente após a passagem de um sinal de interrupção. Portanto, a CPU resume vários tipos de interrupção de dispositivos externos e instruções internas em um método de gerenciamento, ou seja, atribuindo um número inteiro a cada sinal de interrupção, usando esse número inteiro como ID de interrupção (o número inteiro é o vetor de interrupção) e, em seguida, usando este O ID é usado como um índice na tabela do descritor de interrupção para localizar a entrada correspondente e, em seguida, o manipulador de interrupção correspondente.
interrupção externa
Interrupções externas referem-se a interrupções de fora da CPU, e a fonte de interrupção externa deve ser algum hardware. Portanto, as interrupções externas também são chamadas de interrupções de hardware.
As interrupções externas de hardware são notificadas à CPU através de duas linhas de sinal, nomeadamente INTR (INTeRrupt) e NMI (Non Maskable Interrupt). Como mostrado abaixo
Como as tarefas possuem prioridades, elas podem ser subdivididas em interrupções mascaráveis (interrupções mascaráveis) e interrupções não mascaráveis (NMI).
-
Conceito de interrupção mascarável
: A CPU pode ignorar a interrupção emitida por este dispositivo externo porque isso não derrubará o sistema.
[Nota] A CPU também pode ignorá-lo e dividir a interrupção na metade superior e na metade inferior para processamento separado. -
Conceito de interrupção não mascarável
: uma interrupção de emergência que pode causar travamento da CPU se for grave.
Fluxo de processamento de interrupção: Depois que a CPU recebe uma interrupção, ela precisa saber o que aconteceu antes de poder executar o método de processamento correspondente. Isso é conseguido verificando o número do vetor de interrupção e, em seguida, consultando a tabela de vetores de interrupção ou a tabela de descritores de interrupção (a tabela de vetores de interrupção é a matriz do manipulador de interrupções em modo real e foi substituída pela tabela de descritores de interrupção em modo protegido).
Interrompido dentro
As interrupções internas podem ser divididas em interrupções suaves e exceções.
- Interrupção suave: interrupção suave refere-se a uma interrupção iniciada ativamente pelo software. (ex. "int valor imediato de 8 bits")
- Exceções: As exceções são causadas por erros gerados na CPU durante a execução das instruções. (ex. "int3")
Operação experimental
experimente um
Iniciar processo de interrupção
- A função init_all é usada para inicializar todos os dispositivos e estruturas de dados.
- init_all chama idt_init para inicializar conteúdo relacionado a interrupções.
- A inicialização é concluída em duas partes, nomeadamente pic_init e idt_desc_init. Entre eles, pic_init (Controlador de interrupção programável) é usado para inicializar o controlador de interrupção programável 8259A, e ide_desc_init é usado para inicializar a tabela do descritor de interrupção IDT.
- Depois que idt_init for concluído, o IDT será carregado.
O Experimento 1 usa linguagem assembly para implementar um manipulador de interrupções, que é salvo no arquivo kernel.S.
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim kernel.S
Tendo concluído a escrita dos manipuladores de interrupção, agora precisamos instalá-los na tabela de descritores de interrupção. Ou seja, crie uma tabela de descritor de interrupção IDT e instale um manipulador de interrupção. Este programa é salvo no arquivo interrupt.c (o arquivo interrupt.c contém o conteúdo atual sobre interrupções).
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim interrupt.c
Depois de concluir a preparação dos dados relacionados à interrupção, você precisa configurar o agente de interrupção 8259A. Aqui, o assembly inline é usado para implementar a função de E/S da porta. io.h declara definições de funções para operações portuárias.
(base) user@ubuntu:/home/cooiboi/bochs/lib/kernel$ sudo vim io.h
Em seguida, você precisa definir o arquivo para o trabalho relacionado à inicialização, ou seja, init_all.
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim init.c
init_all é chamado pela função principal main em main.c e atualiza a função principal.
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim main.c
Conclua a criação dos arquivos restantes
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim global.h
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim init.h
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim interrupt.h
Confira kernel
o arquivo
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ ls
global.h init.h interrupt.h kernel.S
init.c interrupt.c kernel.bin main.c
Crie uma nova pasta de compilação para armazenar os arquivos de saída (todos os arquivos de destino e arquivos de kernel compilados)
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mkdir build
Depois de concluir o trabalho básico de criação de arquivo, entre agora nas etapas de compilação, vinculação e gravação em disco.
Trabalho de compilação
sudo nasm -f elf -o build/print.o lib/kernel/print.S
sudo nasm -f elf -o build/kernel.o kernel/kernel.S
(base) user@ubuntu:/home/cooiboi/bochs$ sudo nasm -f elf -o build/print.o lib/kernel/print.S
(base) user@ubuntu:/home/cooiboi/bochs$ sudo nasm -f elf -o build/kernel.o kernel/kernel.S
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c
(base) user@ubuntu:/home/cooiboi/bochs$ sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
(base) user@ubuntu:/home/cooiboi/bochs$ sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
(base) user@ubuntu:/home/cooiboi/bochs$ sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c
trabalho de ligação
Observe aqui que o diretório do novo kernel.bin está no diretório build.
sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o
(base) user@ubuntu:/home/cooiboi/bochs$ sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o
gravar no disco
sudo dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=200 seek=9 conv=notrunc
(base) user@ubuntu:/home/cooiboi/bochs$ sudo dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=200 seek=9 conv=notrunc
14+1 records in
14+1 records out
7236 bytes (7.2 kB, 7.1 KiB) copied, 0.00014881 s, 48.6 MB/s
Se houver um problema com o conteúdo do disco rígido, você pode excluí-lo e reescrevê-lo. A seguir está o conteúdo escrito antes (publicado para facilitar a implementação)
sudo dd if=/home/cooiboi/bochs/boot/mbr.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=1 conv=notrunc
sudo dd if=/home/cooiboi/bochs/boot/loader.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=3 seek=2 conv=notrunc
Iniciar Bochs
sudo bin/bochs -f boot/bochsrc.disk
(base) user@ubuntu:/home/cooiboi/bochs$ sudo bin/bochs -f boot/bochsrc.disk
Vamos ver como fica o descritor de interrupção após a instalação oito! Ordem:info idt
- A primeira coluna é o número de série do descritor da porta de interrupção, 0x20 no total.
Interrupt Gate target
É o endereço do manipulador de interrupção apontado no descritor de porta, expresso na forma de seletor: offset.
Suponha que você queira visualizar um descritor de porta de interrupção, você pode usar info idt 序号
o comando. [O descritor da porta de interrupção correspondente ao número do vetor de interrupção 0x20 é mostrado na figura abaixo]
Experimento 2
Melhore o manipulador de interrupção e chame a versão em linguagem C da função do manipulador de interrupção na versão assembly do intrXXentry.
Modifique os arquivos interrupt.c e kernel.S
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim interrupt.c
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim kernel.S
As etapas subsequentes são exatamente as mesmas do Experimento 1 ~
"int vector: 0x20" é constantemente impresso no gráfico em execução (apenas a interrupção do relógio está ativada e o número do vetor de interrupção do relógio é 0x20)
Experimento 3
Crie um novo diretório de dispositivos para armazenar códigos de dispositivos.
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mkdir device
Crie timer.c e timer.h.
(base) user@ubuntu:/home/cooiboi/bochs/device$ sudo vim timer.c
(base) user@ubuntu:/home/cooiboi/bochs/device$ sudo vim timer.h
Em seguida, adicione a função timer_init ao arquivo init.c
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim init.c
Depois de concluir o trabalho básico de criação de arquivo, entre agora nas etapas de compilação, vinculação e gravação em disco.
Os seguintes comandos são executados /home/cooiboi/bochs$
abaixo .
compilar
sudo nasm -f elf -o build/print.o lib/kernel/print.S
sudo nasm -f elf -o build/kernel.o kernel/kernel.S
sudo gcc -m32 -I lib/kernel -c -o build/timer.o device/timer.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c
trabalho de ligação
sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o build/timer.o
gravar no disco
sudo dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=200 seek=9 conv=notrunc
Iniciar Bochs
sudo bin/bochs -f boot/bochsrc.disk
Referências