Manipulador de interrupção

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 :

  1. Escreva um manipulador de interrupções (Operação 8259A Ativar interrupções).
  2. 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).
  3. 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.

  1. Interrupção externa: As interrupções externas à CPU são chamadas de interrupções externas.
  2. 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

Insira a descrição da imagem aqui

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

  1. A função init_all é usada para inicializar todos os dispositivos e estruturas de dados.
  2. init_all chama idt_init para inicializar conteúdo relacionado a interrupções.
  3. 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.
  4. Depois que idt_init for concluído, o IDT será carregado.

Insira a descrição da imagem aqui
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 kernelo 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

Insira a descrição da imagem aqui

Vamos ver como fica o descritor de interrupção após a instalação oito! Ordem:info idt

Insira a descrição da imagem aqui

  • 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]

Insira a descrição da imagem aqui

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)

Insira a descrição da imagem aqui

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

Insira a descrição da imagem aqui
Referências

Acho que você gosta

Origin blog.csdn.net/weixin_42888638/article/details/128610791
Recomendado
Clasificación