Harbin Institute of Technology CSAPP grande lição de casa - vida do programa

sistema de computador

grande trabalho

Programa Tópico Life-Hello's P2P
Professional Artificial Intelligence (2+x)
número de aluno 202111****
turma 21wl***
aluno Wen**    
Instrutor Wu Rui

Escola de Ciência e Tecnologia da Computação
maio 2023

Resumo
Como todo programador sabe, "Olá, mundo!" é a base do mundo da programação. Este artigo conta a história de uma vida inteira da palavra "Olá". Primeiro, escrevemos um arquivo de código-fonte chamado hello.c. Em seguida, ele é pré-processado para gerar um arquivo chamado hello.i. Em seguida, nós o compilamos em um arquivo de linguagem assembly chamado hello.s. Em seguida, convertemos o arquivo de montagem em um arquivo de objeto relocável hello.o. Em seguida, usamos o vinculador ld para combinar hello.o e o arquivo de destino do sistema para criar um arquivo de destino executável, chamado hello.
Quando executamos o programa, inserimos o comando "./hello shell". O programa primeiro verifica se o comando de entrada é uma operação interna, caso contrário, ele começa a chamar a função fork para criar um novo processo. Em seguida, use a função execve para carregar o hello na memória e a CPU controlará a operação do fluxo lógico do programa, incluindo interrupção, troca de contexto e tratamento de exceção. Por fim, o processo termina e é reciclado pelo processo pai, o que marca o fim da "vida" de hello.
Por meio desse processo, entendemos o ciclo de vida do hello no computador, desde o código-fonte até o arquivo executável, passando pela criação e finalização do processo. Esse processo é o que os programadores passam o tempo todo no mundo da programação e é a base sobre a qual eles constroem o software.

Palavras-chave: pré-processamento; compilação; montagem; link; processo; armazenamento; vida útil do programa

Índice

Capítulo 1 Visão geral - 4 -
1.1 Introdução ao HELLO - 4 -
1.2 Ambiente e ferramentas - 4 -
1.3 Resultados intermediários - 4 -
1.4 Resumo deste capítulo - 4 -Capítulo
2 Pré-processamento - 5 -
2.1 O conceito e a função do pré-processamento - 5 -
2.2 Comandos de pré-processamento no UBUNTU - 5 -
2.3 Análise dos resultados do pré-processamento de HELLO - 5 -
2.4 Resumo deste capítulo - 5 -
Capítulo 3 Compilação - 6 -
3.1 O conceito e a função de compilação - 6 -
3.2 Comandos de compilação no UBUNTU - 6 -
3.3 Análise do resultado da compilação de HELLO - 6 -
3.4 Resumo deste capítulo - 6 -
Capítulo 4 compilação - 7 -
4.1 O conceito e a função da compilação - 7 -
4.2 Comandos de compilação no UBUNTU - 7 -
4.3 Formato ELF de destino relocável - 7 -
4.4 Análise do resultado do HELLO.O - 7 -
4.5 Resumo deste capítulo - 7 -
Capítulo 5 Link - 8 -
5.1 O conceito e a função do link - 8 -
5.2 Comando Link no UBUNTU - 8 -
5.3 Destino executável Formato do arquivo HELLO - 8 -
5.4 Espaço de endereçamento virtual do HELLO - 8 -
5.5 Análise do processo de realocação de enlaces - 8 -
5.6 HELLO Execution Process - 8 -
5.7 HELLO Dynamic Link Analysis - 8 -
5.8 Resumo do Capítulo - 9 -
Capítulo 6 HELLO Process Management - 10 -
6.1 Conceito e Função do Processo - 10 -
6.2 Descrever Resumidamente a Função do SHELL-BASH E o Fluxo de Processamento - 10 -
6.3 Processo de criação do processo FORK de HELLO - 10 -
6.4 Processo EXECVE de HELLO - 10 -
6.5 Execução do processo de HELLO - 10 -
6.6 Exceção e processamento de sinal de HELLO - 10 -
6.7 Resumo deste capítulo - 10 -
Capítulo 7 Capítulo HELLO Storage Management - 11 -
7.1 HELLO Espaço de Endereço de Memória - 11 -
7.2 Endereço Lógico INTEL para Conversão de Endereço Linear - Gerenciamento de Segmento - 11 -
7.3 HELLO Conversão de Endereço Linear para Endereço Físico - Gerenciamento de Página - 11 -
7.4 Transformação de VA para PA suportada por TLB e tabela de páginas de quatro níveis - 11 -
7.5 Acesso à memória física suportado por CACHE de três níveis - 11 -
7.6 Mapeamento de memória quando o processo HELLO é FORK - 11 -
7.7 Mapeamento de memória quando o processo HELLO é EXECVE - 11 -
7.8 Falha de página e página tratamento de interrupção de falha - 11 -
7.9 Gerenciamento de alocação dinâmica de armazenamento - 11 -
7.10 Resumo deste capítulo - 12 -
Capítulo 8 Gerenciamento IO de HELLO - 13 -
8.1 Método de gerenciamento de dispositivos IO do LINUX - 13 -
8.2 Breve descrição da interface UNIX IO e suas funções - 13 - 8.3
Análise de implementação de PRINTF - 13 -
8.4 Análise de implementação de GETCHAR - 13 -
8.5 Este capítulo Resumo- 13
-Conclusão- 14
-Apêndice- 15
-Referências- 16-

Capítulo 1 Visão geral
1.1 Introdução ao Hello
1. Crie o arquivo .c escrevendo o programa hello com o editor gcc e obtenha o programa fonte de hello.c.
2. Pré-processá-lo através do pré-processador C (cpp) para gerar o arquivo hello.i (processamento de include define).
3. Traduza-o com um compilador C (ccl) para gerar o arquivo de linguagem assembly hello.s.
4. Traduza-o em um arquivo de objeto relocável hello.o por meio do montador (as).
5. Execute o programa vinculador ld para combinar hello.o e o arquivo de destino do sistema para criar um arquivo de destino executável hello. (Conforme mostrado na figura)
6. Digite ./shell por meio do shell, o shell cria um novo processo por meio da função fork e, em seguida, chama execve para mapear a memória virtual e cria um espaço para o programa hello por meio do mmap.
7. A CPU busca o código e os dados das seções .text e .data na memória virtual, e o escalonador planeja uma fatia de tempo para o processo e aciona uma sub-rotina de tratamento de exceção quando há uma exceção.
8. Quando a execução do programa termina, o processo pai recicla o processo hello e os processos filhos criados por ele, e o kernel exclui as estruturas de dados relevantes.
1.2 Ambiente e ferramentas
1. Ambiente de hardware: CPU X64; 2,4 GHz;
2. Ambiente de software: Windows11 64 bits; Vmware 17; Ubuntu 17.1
3. Ferramentas: codeblocks; gdb; Objdump; vs2022
1.3 Resultados intermediários
Função de nome de arquivo
hello.c source Programa
hello.i Arquivo pré-processado
hello.s Arquivo de montagem compilado
hello.o Arquivo executável de destino relocável montado
hello Arquivo executável vinculado
hello.elf Formato ELF de hello.o
hello1.txt Desmontagem de hello.o
hello2.txt Código de desmontagem
de hello hello1.elf hello Formato ELF
1.4 Resumo deste capítulo Este
capítulo geralmente apresenta o processo de "vida" do programa hello, bem como as informações básicas do ambiente de software e hardware, ferramentas de desenvolvimento e depuração durante o experimento.

Capítulo 2 Pré-processamento
2.1 Conceito e função de pré-processamento
1. Conceito de pré-processamento: O pré-processador (cpp) modifica o programa C original de acordo com o comando que começa com o caractere #.
2. Função de pré-processamento: Modifique o código-fonte de acordo com as instruções de pré-processamento no código-fonte. O pré-processamento insere o código-fonte do arquivo de cabeçalho no arquivo de destino do pacote de arquivos de cabeçalho do sistema. As macros e os identificadores constantes foram todos substituído pelos códigos e valores correspondentes Substitua e, finalmente, gere o arquivo .i.

2.2 Comandos de pré-processamento no Ubuntu
O comando para pré-processar o arquivo hello.c no Linux é: gcc -E hello.c
insira a descrição da imagem aqui

2.3 Análise do resultado do pré-processamento do Hello
Utilizando o comando: gcc -E hello.c -o hello.i, pode ser gerado o arquivo hello.i, com um total de mais de 3000 linhas, que ainda é um programa em linguagem C legível arquivo de texto. A função do pré-processamento é expandir as macros no programa original e incluir o conteúdo do arquivo de cabeçalho no arquivo. Por exemplo, declarar funções, definir estruturas, definir variáveis, definir macros, etc. Se houver um comando #define no código, os símbolos correspondentes serão substituídos.Adicione uma descrição da imagem

As informações da biblioteca do arquivo de cabeçalho envolvidas no arquivo hello.c no início do arquivo hello.i:
o final do arquivo é o conteúdo principal do arquivo hello.c:
insira a descrição da imagem aqui

2.4 Resumo deste capítulo
Este capítulo apresenta os conceitos e funções relacionados ao pré-processamento. Depois de verificar o arquivo hello.i na prática, ele complementa e substitui o programa-fonte. O pré-processador executa muitas operações de expansão no código-fonte. Após o pré-processamento O resultado ainda é um arquivo de origem em linguagem C legal.

Capítulo 3 Compilação
3.1 Conceito e Função de Compilação
Compilação é o processo de tradução de um programa escrito na linguagem fonte para uma linguagem binária reconhecível por um computador. Inclui cinco etapas: análise léxica, análise sintática, verificação semântica e geração intermediária de código, otimização de código e geração de código objeto. A principal função do compilador é permitir que o computador entenda o programa na linguagem fonte, de modo a escrever uma linguagem que o computador possa executar. Ao contrário das linguagens interpretadas, os compiladores não precisam traduzir o programa linha por linha para o idioma de destino.
3.2 Comandos compilados no Ubuntu
Digite o comando gcc -S hello.i -o hello.s -fno-PIC -no-pie -m64 no terminal para obter o resultado da compilação hello.s. Como mostrado abaixo:insira a descrição da imagem aqui

3.3 Análise do resultado da compilação de Hello
3.3.1 Parte inicial da montagem
Nome da seção
function.file declara arquivo-fonte.código de texto
section.section.rodata
segmento de dados somente leitura.globl
declara
variável global.type declara se um símbolo é um tipo de função ou a data
type.size Declare size.string
Declare a string.align
Declare como alinhar instrução ou endereço de armazenamento de dados

3.3.2 Data
① String
Existem duas strings no programa, ambas no segmento de dados somente leitura, conforme mostrado na figura:insira a descrição da imagem aqui

A matriz em hello.c é usada como o segundo parâmetro da função principal e cada elemento da matriz é um ponteiro para um tipo de caractere. O endereço inicial do array é armazenado em -32 (%rbp) na pilha, conforme mostra a figura:

Ele é chamado duas vezes para encontrar os parâmetros e passá-los para printf. Essas duas strings são utilizadas como parâmetros da função printf, conforme mostra a figura:

②Variável local i
A função principal declara uma variável local i, que será colocada na pilha quando o compilador compilar. Conforme mostrado na figura, a variável local i é colocada em -4 (%rbp) na pilha, conforme mostrado na figura:

③Parâmetro argc
O parâmetro argc é utilizado como o parâmetro passado pelo usuário para main (o compilador obterá o tamanho de argc de acordo com os parâmetros passados ​​pelo usuário, ou seja, o número de parâmetros passados). Também é colocado na pilha.
④Array: char *argv[]
char *argv[] é o segundo parâmetro da função principal, e cada elemento do array é um ponteiro para um tipo de caractere (argv[0] geralmente indica o arquivo executável inserido no nome da linha de comando ). O endereço inicial do array é armazenado na posição -32 (%rbp) na pilha e é passado para printf por duas chamadas.

⑤ Número imediato
O número imediato é refletido diretamente no código assembly.

3.3.3 A função global
hello.c declara uma função global int main(int argc, char *argv[]), o código assembly mostra que a função main é uma função global, conforme mostra a figura:insira a descrição da imagem aqui

3.3.3 Operação de atribuição
A operação de atribuição em hello.c é i=0 no loop for; ela é realizada usando a instrução mov no código assembly, conforme mostrado na figura:insira a descrição da imagem aqui

A instrução mov é dividida de acordo com o tamanho do byte do operando:
movb: um byte
movw: "palavra"
movl: "palavra dupla"
movq: "palavra quad"

3.3.4 Operação aritmética
A operação aritmética em hello.c é i++, linguagem assembly addl $1, -4(%rbp), e outras operações aritméticas são mostradas na figura:insira a descrição da imagem aqui

3.3.5 Operações relacionais ①
if(argc!=4); em hello.c é uma declaração de julgamento condicional. Ao compilar, esta instrução é compilada como: cmpl $4,-20(%rbp), e a condição é definida após o código de comparação e julgue se deve pular de acordo com o código de condição. Como mostrado na imagem:

② i<9 em hello.c é compilado como cmpl $8,-4(%rbp) como uma instrução de condição de loop de julgamento, e o código de condição é definido para preparar o próximo arquivo para usar o código de condição para pular. Como mostrado na imagem:

3.3.6 Instruções de transferência de controle
Em linguagem assembly, primeiro defina o código de condição e, em seguida, execute a transferência de controle de acordo com o código de condição. Em hello.c, existem as seguintes instruções de transferência de controle: ①
Determine se argc é igual a 4. Se argc é igual a 4, não execute a instrução if, caso contrário, a instrução if é executada e o código assembly correspondente é:

②No loop for, cada vez que é julgado se i é menor ou igual a 8 para decidir se deve continuar o loop, o código assembly correspondente é:insira a descrição da imagem aqui

3.3.7 Operação da função
As seguintes operações são executadas ao chamar uma função: (assumindo que a função A chama a função B)
① Controle de transferência: ao chamar o procedimento A, o contador do programa (%rip) deve ser definido para o endereço inicial da função A code e, em seguida, return , o contador do programa (%rip) é definido para o endereço da instrução seguinte à instrução callA.
② Passagem de dados: A função A deve ser capaz de fornecer um ou mais parâmetros para a função B, e B deve ser capaz de retornar um valor para A.
③ Alocar e liberar memória: No início, B pode precisar alocar espaço para variáveis ​​locais (o espaço para variáveis ​​locais está na pilha) e, antes de retornar, esses espaços devem ser liberados.
Operação da função em hello.c:
função principal: parâmetro é int argc, char *argv[]
função printf: parâmetro é argv[1], argv[2]
função de saída: parâmetro é 1
sleep function: parâmetro é atoi(argv[ 3 ])
função getchar: sem parâmetros
3.4 Resumo deste capítulo
Este capítulo apresenta principalmente o processo básico do compilador que processa o programa em linguagem C, a função muda do código-fonte para o código assembly equivalente e o compilador converte respectivamente os dados do Linguagem C, conversão de tipos, instruções de atribuição, operações aritméticas, operações lógicas/bit, operações relacionais, transferência de controle e operações de função são analisadas. capacidades anti-engenharia.

Capítulo 4 Assembly
4.1 O conceito e a função do assembly
A linguagem Assembly (Assembly Language) é qualquer linguagem de baixo nível utilizada em computadores eletrônicos, microprocessadores, microcontroladores ou outros dispositivos programáveis, também conhecida como linguagem simbólica. É um tipo de linguagem de máquina em que os códigos de operação são substituídos por mnemônicos e os códigos de endereço são substituídos por símbolos de endereço ou etiquetas. Em diferentes dispositivos, a linguagem assembly corresponde a diferentes conjuntos de instruções em linguagem de máquina, que são convertidas em instruções de máquina por meio do processo de montagem. Existe uma correspondência um-para-um entre uma linguagem assembly específica e um conjunto de instruções de linguagem de máquina específico, portanto, a linguagem assembly pode ser diferente em diferentes plataformas.
O papel da linguagem assembly:
(1) Fornecer uma linguagem de baixo nível mais próxima do hardware do computador para programas mais eficientes.
(2) Forneça uma maneira mais direta para os programadores entenderem melhor como os computadores funcionam.
(3) Pode ser usado para implementar sistemas de software complexos, como sistemas embarcados, sistemas operacionais e drivers.
(4) A linguagem assembly pode ser usada para escrever malware, como vírus e cavalos de Tróia. 4.2 Comando gcc hello.s -c -o hello.o
compilado no Ubuntu

insira a descrição da imagem aqui

4.3 Formato de elfo alvo relocável

Comando: readelf -a hello.o > hello.elf
insira a descrição da imagem aqui

O conteúdo do arquivo .elf:
(1) Cabeçalho ELF:
O cabeçalho ELF (cabeçalho ELF) começa com uma sequência de 16 bytes, que descreve o tamanho da palavra e a ordem de bytes do sistema que gerou o arquivo. O restante do cabeçalho ELF contém informações que ajudam o vinculador a analisar e interpretar o arquivo objeto, incluindo o tamanho do cabeçalho ELF, o tipo de arquivo objeto (como relocável, executável ou compartilhado) e o tipo de máquina (como x86-64), o deslocamento do arquivo da tabela de cabeçalho de seção e o tamanho e o número de entradas na tabela de cabeçalho de seção. A localização e o tamanho das diferentes seções são descritos por uma tabela de cabeçalho de seção, onde cada seção no arquivo objeto possui uma entrada de tamanho fixo. Como mostrado na imagem:
insira a descrição da imagem aqui

(2) Cabeçalho da seção:
registre o nome, tipo, endereço, deslocamento, tamanho, tamanho geral, bandeira, link, informações e alinhamento de cada seção. Como mostrado na imagem:
insira a descrição da imagem aqui

(3) Seção de realocação:
.rela.dyn armazena as informações que precisam ser corrigidas na seção .text; quaisquer instruções que chamam funções externas ou referenciam variáveis ​​globais precisam ser corrigidas; instruções que chamam funções externas precisam ser realocadas; referências para global Instruções para variáveis ​​requerem realocação; instruções para chamar funções locais não requerem realocação; não há informações de realocação no arquivo de objeto executável. Os programas que precisam ser realocados são .L0 e .L1 em ​​printf, puts, exit, sleepsecs, getchar, sleep e .rodata. Como mostrado na imagem:
insira a descrição da imagem aqui

.rel.plt salva as informações da tabela de realocação e pode usar o método de conexão preguiçosa. Como mostrado na imagem:
insira a descrição da imagem aqui

(4) Tabela de símbolos:
.symtab, uma tabela de símbolos, que armazena informações sobre funções e variáveis ​​globais definidas e referenciadas no programa Alguns programadores acreditam erroneamente que um programa deve ser compilado com a opção -g para obter informações da tabela de símbolos. Praticamente todo arquivo de objeto relocável possui uma tabela de símbolos em .symtab (a menos que o programador a remova deliberadamente com o comando STRIP). No entanto, ao contrário da tabela de símbolos em um compilador, a tabela de símbolos .symtab não contém entradas para variáveis ​​locais. Como mostrado na imagem:
insira a descrição da imagem aqui

4.4 Análise do resultado do
Comando Hello.o: objdump -d -r hello.o > hello1.txt
insira a descrição da imagem aqui

Diferenças de hello.s:
(1) Transferência de filial:
hello.s:

ola1.txt:

A instrução de salto desmontada não usa um nome de segmento como .L3, mas um endereço definido. No código de desmontagem, a transferência de ramificação é expressa como o endereço da função + o deslocamento no segmento. O operando da instrução de salto no código de desmontagem não usa um nome de segmento, porque o nome do segmento é apenas um mnemônico para facilitar a escrita em linguagem assembly, portanto obviamente não existe após a montagem em linguagem de máquina, mas um endereço definido.
(2) A chamada para a função corresponde à entrada de realocação
hello.s:

ola1.txt :

No arquivo relocável, after call não é mais o nome específico da função, mas uma informação direcionada pela entrada de realocação. No arquivo de montagem, pode-se ver que o nome do arquivo é adicionado diretamente após a chamada.
(3) O valor imediato torna-se hello.s em formato hexadecimal
:

ola1.txt :

No arquivo objeto relocável, todos os números imediatos são expressos em hexadecimal, porque a conversão entre hexadecimal e binário é mais conveniente do que decimal, então todos são convertidos em hexadecimal.
4.5 Resumo deste capítulo
Neste capítulo, hello.s foi montado, e o arquivo de objeto relocável hello.o foi gerado, e o cabeçalho ELF, tabela de cabeçalho de seção, tabela de símbolos e seção relocável do arquivo relocável foram analisados, e o hello A diferença entre os códigos de desmontagem .s e hello.o, e a correspondência entre linguagem assembly e linguagem de máquina é analisada.

Capítulo 5 Link
5.1 Conceito e função de link Link
(Link) é um mecanismo de transferência de parâmetros e comandos de controle entre vários módulos em um programa de computador eletrônico, e é uma relação de conexão entre vários módulos em um programa de computador. Durante a execução do programa, cada módulo interage e coopera por meio de links para realizar tarefas específicas.
O conceito de links pode ser aplicado em diferentes campos. No campo da ciência da computação, um link pode se referir ao relacionamento de conexão entre os elementos do programa, como diferentes arquivos, estruturas de dados e funções. No campo da Internet, um link pode se referir a uma relação de conexão entre páginas da Web, e uma página da Web pode ser saltada de uma página da Web para outra clicando em um link. No campo de sistemas operacionais e protocolos de rede, um link pode se referir a uma relação de conexão entre um dispositivo e um aplicativo, como um link em File Transfer Protocol (FTP).
A função do link é estabelecer o link entre os vários módulos, para que eles possam se comunicar entre si, compartilhar dados e recursos e realizar tarefas de computação complexas. Por meio de links, cada módulo do programa pode ser modificado e atualizado de forma independente, sem afetar o funcionamento dos outros módulos. Isso facilita muito a manutenção e as atualizações do programa. Ao mesmo tempo, os links também fornecem aos usuários uma forma de uso mais flexível e conveniente: os usuários podem pular para páginas, abrir arquivos, executar operações, etc., clicando nos links.
5.2 Comando Link no Ubuntu
ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64- linux- gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
insira a descrição da imagem aqui

5.3 O formato do arquivo de destino executável hello
Comando: readelf -a hello > hello1.elf

(1) Cabeçalho ELF: A diferença entre o cabeçalho do arquivo hello e o cabeçalho do arquivo hello.o é mostrada na figura abaixo: hello é um arquivo de objeto executável com 27 seções. Como mostrado na imagem:
insira a descrição da imagem aqui

(2) Cabeçalho da seção: declara todas as informações da seção em hello, incluindo tamanho e deslocamento. Como mostrado na imagem:

insira a descrição da imagem aqui

(3) Seção de realocação.rela.text:
insira a descrição da imagem aqui

(4) Tabela de símbolos.symtab: armazena funções e variáveis ​​globais definidas e referenciadas no programa.
insira a descrição da imagem aqui

5.4 O espaço de endereço virtual de hello
O endereço virtual começa em 0x401000 e termina em 0x401ff0.
insira a descrição da imagem aquiinsira a descrição da imagem aqui

5.5 Análise do processo de realocação de links
Comando: objdump -d -r hello > hello2.txt
e hello.o comparação de arquivos desmontados constatou que há muitas seções em hello2.txt. Há apenas uma seção .text em hello1.txt, e há apenas uma função principal, e o endereço da função também é o padrão 0x000000. Existem três seções.init, .plt e .text em hello2.txt, e há são muitas funções em cada seção. O código da função da biblioteca foi vinculado ao programa, cada seção do programa se torna mais completa e o endereço do salto também é referenciado. Conforme mostrado na figura:
pode-se ver que o número geral de seções aumentou e o número geral de seções do programa aumentou devido aos módulos carregados, como printf.o no link. Ao mesmo tempo, o endereço da memória virtual também é ligeiramente diferente. É equivalente a redefinir o endereço de memória para ele.
insira a descrição da imagem aqui

O processo de realocação é dividido em duas etapas:
(1) Seção de realocação e definição do símbolo: Nesta etapa, o vinculador mescla todas as seções do mesmo tipo em uma nova seção de agregação do mesmo tipo. Por exemplo, as seções .data de todos os módulos de entrada são todas mescladas em uma seção, que se torna a seção .data do arquivo de objeto executável de saída.
(2) Referências de símbolo na seção de realocação: nesta etapa, o vinculador modifica as referências a cada símbolo nas seções de código e dados para que apontem para o endereço de tempo de execução correto. Para executar esta etapa, o vinculador conta com entradas relocáveis ​​e os dados analisados ​​na Seção 5.3.
5.6 processo de execução hello
(1) iniciar a execução: _start, _libc_start_main
(2) executar main: _main, _printf, _exit, _sleep, _getchar
(3) sair: sair
nome do programa endereço do programa
_start 0x400550
_libc_start_main 0x40057a
Main 0x400582
_printf 0x400500
_exit 0x
400530_ dormir 0x400540_getchar
0x400510

5.7 Análise de link dinâmico do Hello
A ideia básica do link dinâmico é adiar o processo de vinculação até o tempo de execução. Quando o programa estiver em execução, os módulos que precisam ser carregados serão vinculados dinamicamente. Portanto, a vinculação dinâmica pode montar dinamicamente o programa em tempo de execução sem a necessidade de determinar o caminho completo de todos os módulos em tempo de compilação. Ao chamar uma função de biblioteca compartilhada, o compilador não tem como prever o endereço de tempo de execução dessa função, pois biblioteca que o define Os módulos podem ser carregados em qualquer lugar durante a execução. A abordagem normal é gerar um registro de realocação para essa referência e, em seguida, o vinculador dinâmico resolve isso quando o programa é carregado.
A ligação atrasada é implementada por meio de GOT e PLT.De acordo com o arquivo hello ELF, a localização da tabela inicial do GOT é 0x601000.
5.8 Resumo deste capítulo
Este capítulo apresenta principalmente o conceito e a função dos links. Os links podem ser divididos em definição de símbolo e realocação, entender o formato ELF de arquivos executáveis, analisar o espaço de endereço virtual de hello, processo de realocação, processo de execução e dinâmica processo de link, com uma compreensão mais profunda de links.

Capítulo 6 hello process management
6.1 O conceito e a função do processo
Processo (Processo) é um programa no computador sobre uma atividade em execução em um conjunto de dados, é a base da alocação e agendamento de recursos do sistema e a base da estrutura do sistema operacional . Na ciência da computação, um processo é uma única execução de um programa em um computador. É um conceito abstrato usado para descrever a unidade básica de alocação de recursos da CPU. Um processo é uma atividade em execução de um programa com funções independentes em um determinado conjunto de dados, aplica-se e possui recursos do sistema e é um conceito dinâmico. Um processo é uma entidade ativa, não apenas o código do programa, mas também a atividade atual, representada pelo valor do contador do programa e pelo conteúdo do registrador de processamento.
O papel do processo é:
(1) Melhorar a utilização de recursos: O processo é a unidade básica para alocação e agendamento de recursos no sistema. Ao executar várias tarefas simultaneamente por meio de vários processos, os recursos do sistema podem ser totalmente utilizados e a utilização de recursos pode ser melhorada .
(2) Realize a separação de tarefas: o processo realiza a separação do programa, e diferentes processos são independentes uns dos outros e não interferem e afetam uns aos outros, o que ajuda a proteger a estabilidade e a segurança do sistema.
(3) Fornecer interatividade: o processo pode interagir com o usuário por meio de operações de entrada e saída, realizar a depuração e teste do programa e fornecer uma melhor experiência do usuário.
(3) Suporta processamento paralelo: O processo suporta execução simultânea de múltiplas tarefas, que podem realizar processamento paralelo e melhorar a eficiência de execução e velocidade de resposta do programa.
Resumindo, um processo é um conceito importante do sistema operacional, a unidade básica do sistema para gerenciamento e escalonamento de recursos e a base para execução simultânea e processamento paralelo.
6.2 Breve descrição da função e fluxo de processamento do Shell-bash
Shell (também conhecido como Bash) é um interpretador de linha de comando usado para executar comandos e scripts em Unix, Linux e outros sistemas operacionais. É uma interface de usuário importante no sistema operacional que permite ao usuário interagir com o computador.
O papel do Shell Shell:
(1) Fornecer uma interface para o usuário interagir com o computador. Os usuários podem inserir comandos no shell, que serão interpretados e executados pelo shell.
(2) Execute o script. Shell pode executar vários arquivos de script, como scripts bash, scripts Python, scripts Perl, etc.
(3) Processo de gestão. Shell pode iniciar, parar e gerenciar processos no sistema operacional.
(4) Acesse o sistema de arquivos. Shell fornece comandos para acessar o sistema de arquivos, como cd, ls, mkdir, etc.
Executar tarefas de administração do sistema. O Shell pode executar várias tarefas de gerenciamento do sistema, como modificar variáveis ​​de ambiente, instalar software e assim por diante.

Fluxo de processamento:
(1) Abrindo o Shell: O usuário abre o terminal e entra na interface de linha de comando Shell (também chamada de interpretador de linha de comando Shell).
(2) Prompt: O Shell exibe um prompt (também conhecido como "$") para instruir o usuário a inserir o início do comando.
(3) Comando de entrada do usuário: o usuário insere o comando a ser executado, como cd, ls, mkdir, etc.
(4) Receber comando: Shell recebe o comando digitado pelo usuário e o salva na memória.
(5) Comandos de análise: o Shell analisa os comandos inseridos pelo usuário e os divide em unidades menores, como palavras, frases, parâmetros, etc.
(6) Localizar comandos: Shell procura comandos no caminho de comando do sistema.
(7) Execute o comando: Se o comando for encontrado, o Shell executará o comando e exibirá o resultado. Se o comando não for encontrado, o shell exibirá uma mensagem de erro.
(8) Sair do Shell: Quando o usuário insere o comando de saída, o Shell sai e fecha o terminal.
6.3 O processo de criação do processo de bifurcação de Hello
Depois de inserir a linha de comando ./hello 2021112957 Wen Jiajie no terminal, o shell primeiro analisa o comando que inserimos. Como o comando que inserimos não é um comando interno do shell, o shell chamará fork() para criar Um processo filho que é quase, mas não idêntico ao processo pai. Por meio da função fork, o processo filho obtém a mesma cópia, mas independente, do espaço de endereço virtual em nível de usuário do processo pai, com um PID diferente.
6.4 Processo execve do Hello
Depois de chamar a função fork() para criar um processo filho, o processo filho chama a função exceve para carregar e executar um novo programa no contexto do processo filho atual, ou seja, o programa hello, que é chamado uma vez e nunca retorna.
Quando a função execve() é executada, todos os espaços de dados do usuário do processo atual serão limpos primeiro e a imagem do novo processo será carregada neste espaço. Em seguida, copie o conteúdo das novas matrizes argv e envp para o novo espaço de dados do usuário. Finalmente, defina o ponteiro da pilha do novo espaço de dados do usuário como o ponteiro da pilha do novo programa e pule para o ponto de entrada do novo programa para iniciar a execução do processo de 6.5 Hello A abstração de chave fornecida pelo processo para o programa
aplicativo : um fluxo de controle lógico independente, que dá a ilusão de que nosso programa está usando exclusivamente o processador. Um espaço de endereço privado que fornece a ilusão de que nosso programa tem uso exclusivo do sistema de memória. A abstração do processo fornecida pelo sistema operacional: (1) Fluxo de controle lógico: Se quisermos percorrer o programa com um depurador, veremos uma série de valores de contador de programa (PC) que correspondem exclusivamente aos valores contidas no programa.Instruções em um arquivo de objeto executável ou em um objeto compartilhado que é vinculado dinamicamente a um programa em tempo de execução. Essa sequência de valores do PC é chamada de fluxo de controle lógico, ou simplesmente fluxo lógico. (2) Troca de contexto: Se a chamada do sistema for bloqueada porque está esperando a ocorrência de um evento, o kernel pode suspender o processo atual e alternar para outro processo.O contexto é o estado exigido pelo kernel para reiniciar um processo interrompido. Compare o fluxo de controle de exceção de alto nível. (3) Fatia de tempo: Cada período de tempo em que um processo executa parte de seu fluxo de controle é chamado de fatia de tempo. (4) Modo de usuário e modo de kernel: O shell permite que os usuários tenham a oportunidade de modificar o kernel; portanto, algumas medidas de proteção precisam ser definidas para proteger o kernel, como limitar o tipo de instruções e o escopo da ação. (5) Informação de contexto: O contexto é o estado exigido pelo kernel para reiniciar um processo preemptado, que consiste em registradores de uso geral, registradores de ponto flutuante, contadores de programa, pilhas de usuário, registradores de status, pilhas de kernel e vários dados do kernel estruturas composição de valor.









Execução do processo hello: Após o processo chamar a função execve, o processo alocou um novo espaço de endereço virtual para o programa hello. Inicialmente, hello é executado no modo de usuário e gera hello 2021112957 Wen Jiajie e, em seguida, chama a função sleep. O processo entra no modo kernel e executa o manipulador de sinal antes de retornar ao modo de usuário. Durante o processo em execução, a CPU alterna continuamente o contexto, de modo que o processo em execução seja dividido em fatias de tempo e a CPU seja alternadamente ocupada por outros processos para realizar o escalonamento do processo.
insira a descrição da imagem aqui

6.6 Olá, exceção e processamento de sinal
(1) A exceção pode ser dividida em 4 categorias: interrupção (interrupção), armadilha (armadilha), falha (falha) e terminação (abortar). A seguir estão os atributos dessas quatro categorias de exceções:
Interrupção: Uma exceção causada por hardware ou software, como entrada de teclado, interrupção de relógio, etc.
Trap: Um mecanismo fornecido pelo compilador ou pelo sistema operacional para capturar certos tipos de exceções.
Falha: Uma exceção causada por um erro de programa, como divisão por zero, array fora dos limites, etc.
abort: lançado explicitamente pelo programador para encerrar a execução do programa em uma emergência.
Todos os quatro tipos de exceções podem ocorrer em um programa e exigem que o programador escreva o código apropriado para lidar com elas. O tratamento de exceções pode melhorar a robustez e a confiabilidade do programa, para que o programa possa lidar melhor com várias condições anormais. ,
(2) Resultado da operação
1. Operação normal
insira a descrição da imagem aqui

2. Pressione Ctrl-z

O resultado padrão de ctrl-z é suspender o trabalho em primeiro plano. O processo hello não é reciclado, mas é executado em segundo plano. Você pode ver com o comando ps que o processo hello não foi reciclado. Chame fg para trazê-lo para o primeiro plano. Se a impressão for finalizada, o processo será reciclado, conforme a figura abaixo:
insira a descrição da imagem aquiinsira a descrição da imagem aqui

3. Pressione Ctrl+c
insira a descrição da imagem aqui

![Insira a descrição da imagem aqui](https://img-blog.csdnimg.cn/aed6154308e34f228ac5ab1a03bf7571.png#pic_center
Digitar Ctrl+c no teclado fará com que o kernel envie um sinal SIGINT para cada processo no grupo de processos em primeiro plano, por padrão A situação é encerrar o trabalho de foreground, usar ps para visualizar o grupo de processos de foreground e descobrir que não há nenhum processo hello, conforme mostrado na figura:
insira a descrição da imagem aqui

4. Pressão aleatória ininterrupta
A entrada irrelevante é armazenada em buffer para stdin e é enviada para o resultado junto com o comando printf. Como mostrado na imagem:
insira a descrição da imagem aqui

6.7 Resumo deste capítulo
(este capítulo apresenta o conceito e a função do processo, o processo de processamento e a função do shell-bash e se concentra na análise da chamada de fork para criar um novo processo, chamando a função execve para executar hello, a execução do processo processo de hello e hello encontrado durante exceção de tempo de execução e manipulação de sinal.

Capítulo 7 gerenciamento de armazenamento do hello
7.1 espaço de endereçamento da memória do hello
1. Endereço lógico: o endereço que aparece no código assembly após a compilação do programa. Um endereço lógico é usado para especificar um operando ou o endereço de uma instrução. É um identificador de segmento mais um offset especificando um endereço relativo dentro do segmento, expresso como identificador de segmento: offset dentro do segmento.
2. Endereço linear: Uma etapa no processo de conversão de endereço lógico para endereço físico. O endereço lógico é convertido em um endereço linear por meio do mecanismo de segmento, que é uma forma combinada de descritor: offset, e o endereço linear é usado como entrada no mecanismo de paginação.
3. Endereço virtual: É um endereço linear.
4. Endereço físico: A UCP encontra o endereço correspondente da memória física real através do endereçamento do barramento de endereços. O acesso da CPU à memória é realizado através do barramento frontal conectando a CPU e o chip ponte norte. Os endereços de memória transmitidos no barramento frontal são todos endereços de memória física.
7.2 Conversão de Endereço Lógico Intel para Endereço Linear - Gerenciamento de Segmento
Na plataforma Intel, o formato do endereço lógico é identificador de segmento: deslocamento dentro do segmento. Um identificador de segmento consiste em um campo de 16 bits chamado seletor de segmento. Os primeiros 13 bits são um número de índice. Os próximos 3 bits contêm alguns detalhes de hardware. O mecanismo de segmentação converte um endereço lógico em um endereço linear da seguinte forma:

  1. Determinar o endereço base do segmento (Segment Base Address): Cada segmento tem um endereço base do segmento correspondente, que é o endereço inicial do segmento na memória.
  2. Calcular o deslocamento (Offset): O deslocamento no endereço lógico refere-se ao endereço relativo ao endereço base do segmento atual. O endereço de memória real pode ser calculado pelo deslocamento correspondente no endereço lógico e no endereço base do segmento.
  3. Adicionar endereço de base de segmento e deslocamento: Adicione endereço de base de segmento e deslocamento para obter endereço linear.
    7.3 Conversão do endereço linear do Hello para endereço físico - gerenciamento de paginação
    O endereço linear, ou seja, a conversão entre endereço virtual (VA) e endereço físico (PA) é realizada através do mecanismo de paginação.
    O mecanismo de paginação converte endereços virtuais em endereços físicos da seguinte forma:
  4. Determine o endereço inicial da tabela de páginas (Page Table Start Address): A tabela de páginas é uma tabela usada para converter endereços virtuais em endereços físicos. O sistema operacional carregará o endereço inicial da tabela de páginas na memória.
  5. Determine a entrada da tabela de páginas (Page Table Entry, PTE): Na tabela de páginas, cada entrada é chamada de entrada da tabela de páginas. Cada entrada da tabela de páginas contém um endereço físico e um bit de permissão de acesso.
  6. Determine o número da página no endereço virtual: O endereço virtual consiste em duas partes: o número da página e o deslocamento dentro da página. O número da página é usado para procurar a entrada da tabela de páginas correspondente na tabela de páginas.
  7. Encontre a entrada da tabela de páginas correspondente na tabela de páginas: Por meio do número da página, o sistema operacional pode encontrar a entrada da tabela de páginas correspondente na tabela de páginas.
  8. Verifique se a entrada da tabela de páginas contém um endereço físico válido: Antes de acessar um endereço virtual, o sistema operacional deve garantir que o endereço virtual corresponda a um endereço físico válido.
  9. Verifique o bit de permissão de acesso da entrada da tabela de páginas: Se a entrada da tabela de páginas contiver um endereço físico válido, o sistema operacional também precisará verificar o bit de permissão de acesso da entrada da tabela de páginas para determinar se ele tem permissão para acessar o endereço.
  10. Adicione o endereço físico e o deslocamento: Se o bit de permissão de acesso permitir o acesso ao endereço físico, o sistema operacional adicionará o endereço físico e o deslocamento no endereço virtual para obter o endereço físico real.

7.4 Transformação de VA para PA suportada por TLB e tabela de páginas de quatro níveis

  1. Determine o endereço inicial da tabela de páginas: O sistema operacional carregará o endereço inicial da tabela de páginas na memória.
  2. Determinando o número da página e o deslocamento dentro do endereço virtual: Um endereço virtual consiste em duas partes: o número da página e o deslocamento dentro da página. O número da página é usado para procurar a entrada da tabela de páginas correspondente na tabela de páginas e o deslocamento da página é usado para calcular uma parte do endereço físico depois que a entrada da tabela de páginas correspondente é encontrada.
  3. Encontre a entrada da tabela de páginas correspondente na tabela de páginas: Por meio do número da página, o sistema operacional pode encontrar a entrada da tabela de páginas correspondente na tabela de páginas.
  4. Verifique se a entrada da tabela de páginas contém um endereço físico válido: Antes de acessar um endereço virtual, o sistema operacional deve garantir que o endereço virtual corresponda a um endereço físico válido.
  5. Verifique o bit de permissão de acesso da entrada da tabela de páginas: Se a entrada da tabela de páginas contiver um endereço físico válido, o sistema operacional também precisará verificar o bit de permissão de acesso da entrada da tabela de páginas para determinar se ele tem permissão para acessar o endereço.
  6. Adicione o endereço físico e o deslocamento: Se o bit de permissão de acesso permitir o acesso ao endereço físico, o sistema operacional adicionará o endereço físico e o deslocamento no endereço virtual para obter o endereço físico real.
  7. Verifique se o TLB foi atingido: Se o TLB (Translation Lookaside Buffer) armazenar em cache o resultado da conversão de endereço virtual para endereço físico que acabou de ser realizada, o sistema operacional pode obter diretamente o endereço físico do TLB sem realizar outra conversão de endereço virtual para endereço físico .
  8. Repita as etapas 1 a 7 até que o endereço físico final seja encontrado.
    7.5 Acesso à memória física suportado pelo cache L3
    O cache L3 (cache L3) é o nível de cache da CPU do computador. Normalmente, o cache da CPU é dividido em quatro níveis: L1, L2, L3 e L4. Entre eles, os caches L1 e L2 geralmente são integrados na CPU, enquanto os caches L3 estão localizados na placa-mãe e o L4 é um dispositivo de armazenamento de maior velocidade, como o SSD. Quando a CPU acessa a memória, ela precisa seguir uma determinada sequência e mecanismo de acesso. A seguir estão as etapas de acesso à memória física suportadas pelo Cache de três níveis:
  9. Determine se os dados que precisam ser acessados ​​estão em um registrador da CPU. Se os dados já estiverem em um registrador, a CPU pode acessar diretamente esses dados.
  10. Se os dados não estiverem em um registrador, a UCP primeiro verifica o cache de nível 1 (cache L1). Se os dados estiverem no cache L1, a CPU pode acessar diretamente esses dados.
  11. Se os dados não estiverem no cache L1, a CPU verifica o cache L2 (cache L2). Se os dados estiverem no cache L2, a CPU pode acessar diretamente esses dados.
  12. Se os dados não estiverem no cache L2, a CPU verifica o cache L3 (cache L3). Se os dados estiverem no cache L3, a CPU pode acessar diretamente esses dados.
  13. Se os dados não estiverem no cache L1 nem no cache L2 e no cache L3, a CPU precisa ler os dados da memória principal (como a RAM). Ler dados da memória principal é muito mais lento do que ler dados do cache.
  14. Para melhorar a eficiência do acesso da CPU aos dados, o sistema operacional e o hardware gerenciarão os dados no cache de acordo com fatores como frequência e importância do uso dos dados. Se os dados não estiverem mais sendo usados ​​com frequência em algum momento, eles podem ser removidos do cache para liberar espaço para novos dados.
    7.6 Mapeamento de memória quando o processo hello bifurca
    Quando o programa chama a função fork(), ele retorna duas vezes: uma vez no processo pai e uma vez no processo filho. Isso é obtido usando a técnica copy-on-write, que permite que os processos pai e filho compartilhem o mesmo espaço de endereço virtual.
    Após a chamada fork(), os espaços de endereço virtual dos processos pai e filho são mapeados para a mesma memória física, mas são independentes um do outro. Isso significa que eles podem acessar o mesmo endereço virtual ao mesmo tempo, mas não interferem entre si. Isso ocorre porque cada processo tem seu próprio espaço de endereço virtual e o sistema operacional garante que eles não entrem em conflito.
    No Linux, o relacionamento de mapeamento de memória entre o processo pai e o processo filho é representado pela área de controle de cópia (VMA, Virtual Memory Area). Cada objeto VMA representa um intervalo de endereços virtuais dentro de um processo. O objeto VMA contém informações sobre os direitos de acesso para esse intervalo de endereços, páginas de memória física mapeadas e assim por diante.
    Após a chamada fork(), os objetos VMA dos processos pai e filho são copiados para refletir seus respectivos espaços de endereço virtual. No entanto, as páginas reais da memória física são copiadas apenas quando são acessadas. Isso ocorre porque a função fork() usa a tecnologia Copy-on-Write, que permite que os processos pai e filho compartilhem as mesmas páginas de memória física até que um deles tente modificar essas páginas.
    7.7 Mapeamento de memória durante o processo hello execve
    No sistema Linux, a função execve() é usada para carregar a nova imagem do processo no espaço de endereço do processo existente, que implementa o mecanismo de compartilhamento e substituição de arquivos e possui recursos mais flexíveis e eficientes.
    Quando a função execve() é chamada, as seguintes operações são executadas:
  15. Abra um arquivo: chame a função open() para obter a estrutura de dados do arquivo do caminho de arquivo especificado.
  16. Copiar parâmetros: copie os parâmetros da linha de comando e as variáveis ​​de ambiente do espaço de endereço do usuário para o espaço de endereço do kernel.
  17. Liberar memória: liberar memória relacionada ao programa original, como arquivos abertos, bibliotecas compartilhadas, etc.
  18. Modificar tabelas de páginas: atualize as tabelas de páginas do processo para refletir o layout do espaço de endereço do novo processo.
  19. Execução do código: Após modificar a tabela de páginas, a UCP passa a executar o código do novo processo.
    7.8 Falha de página e processamento de interrupção de falha de página
    Falha de página (Page Fault) refere-se à situação em que a página (ou página) que precisa ser acessada não está na memória principal (RAM) quando o computador executa o programa. Uma falha de página ocorre quando um programa acessa uma página que não está na memória principal.
    O processamento de interrupção de falha de página refere-se ao processo no qual o sistema operacional intervém e lida com a falha quando ocorre uma falha de página. A seguir estão as etapas básicas do tratamento de falhas de página:
  20. Gerar uma interrupção: quando um programa acessa uma página que não está na memória principal, a CPU gera um sinal de interrupção de falha de página para transferir o controle para o sistema operacional.
  21. Execução do manipulador de interrupção: O sistema operacional salvará o contexto do programa atual (incluindo status de registro, contador de programa, etc.) e iniciará a execução do manipulador de interrupção de falha de página.
  22. Determine a causa da falha de página: O manipulador de interrupção de falha de página determinará primeiro a causa da falha de página, que pode ser porque a página não existe na memória principal (falha de página) ou a página existe na memória principal, mas não tem direitos legais de acesso (falha de proteção).
  23. Lidar com falhas de página: Se for uma falha de página, o sistema operacional precisa carregar a página necessária do armazenamento auxiliar (geralmente o disco rígido) na memória principal, atualizar a estrutura de dados, como a tabela de páginas, e tornar a página visível ao programa.
  24. Tratamento de falhas de proteção: Em caso de falhas de proteção, o sistema operacional verifica se o programa possui permissões suficientes para acessar a página. Se os privilégios forem insuficientes, o sistema operacional encerra a execução do programa ou concede os privilégios apropriados.
  25. Restaurando o contexto: Depois de lidar com falhas de página ou falhas de proteção, o sistema operacional restaurará o contexto do programa interrompido, incluindo status de registro, contador de programa, etc.
  26. Reexecutar a instrução interrompida: O sistema operacional retorna o controle ao programa interrompido e reexecuta a instrução que causou a interrupção da falha de página.
    7.9 Gerenciamento de alocação de armazenamento dinâmico
    Os métodos e estratégias básicos de gerenciamento de alocação de armazenamento dinâmico são os seguintes:
  27. First Fit: Organize a área livre de acordo com o endereço inicial de pequeno para grande, procure desde o início da cadeia até encontrar uma área livre que atenda aos requisitos e extraia dela um espaço de memória igual ao tamanho solicitado.
  28. Cycle First Adaptation (Next Fit): Ao procurar por uma área livre, em vez de procurar pela cabeça da corrente todas as vezes, ele começa a procurar pela próxima área livre da área livre encontrada da última vez até encontrar uma área livre que atenda aos requisitos Até agora e extraia dele um espaço de memória igual ao tamanho solicitado.
  29. Best Fit: Sempre aloque a menor área livre que atenda aos requisitos para o trabalho solicitante, ou seja, na tabela de áreas livres, organize a área livre de pequeno para grande, construa um índice e quando o trabalho do usuário solicitar memória Quando houver espaço, encontre a primeira área livre que satisfaça o trabalho na tabela de índice e dê a ela.
  30. Pior ajuste: Sempre atribua a maior área livre ao trabalho solicitante, e as partições livres na tabela de área livre (cadeia de área livre) devem ser classificadas por tamanho de grande para pequeno, começando pelo cabeçalho para encontrar a primeira A partição livre que atende aos requisitos é alocado para o trabalho.
  31. Tabela de espaços disponíveis (Lista Livre): tabela de diretórios e lista encadeada.
  32. Marcação de limite: Existem marcas nos limites superior e inferior de cada área de memória para identificar se a área é um bloco ocupado ou um bloco livre.
    A seguir está o processo geral de solicitação dinâmica de memória:
    1. Determine os requisitos de memória: Primeiro, o programa precisa determinar o tamanho de memória necessário. Isso pode ser determinado calculando o tamanho da estrutura de dados, a quantidade de dados que precisam ser armazenados, etc.
    2. Chame a função de alocação de memória: Em geral, a linguagem de programação ou o sistema operacional fornece uma função de alocação de memória, como malloc() na linguagem C ou new operator em C++. O programa pode chamar essas funções para solicitar o espaço de memória necessário.
    3. Especifique o tamanho da memória: Ao chamar a função de alocação de memória, você precisa especificar o tamanho da memória necessária. A função tenta encontrar um bloco de memória de tamanho suficiente e alocá-lo ao programa.
    4. Alocar espaço de memória: A função de alocação de memória procurará um espaço contínuo de tamanho suficiente na área de memória disponível. Se for encontrado espaço suficiente, ele é marcado como alocado e um ponteiro para o bloco de memória é retornado.
    5. Tratamento de erros: Se não houver espaço de memória contínuo suficiente para alocação, a função de alocação de memória pode retornar um ponteiro nulo ou lançar uma exceção, indicando que a alocação de memória falhou. Os programas podem executar o tratamento de erros com base no valor de retorno, como liberar memória alocada e executar as ações apropriadas.
    6. Usando a memória: Uma vez que a memória é alocada com sucesso e um ponteiro para o bloco de memória é retornado, o programa pode usar o espaço da memória para armazenar dados ou realizar outras operações.
    7. Liberar memória: Depois de solicitar dinamicamente a memória, quando a memória não for mais necessária, a memória deve ser liberada explicitamente. Isso pode ser feito chamando uma função de desalocação de memória, como free() em C ou o operador delete em C++. Depois que a memória é liberada, o espaço de memória pode ser realocado para outros programas que precisam dele.
    7.10 Resumo do Capítulo
    Este capítulo apresenta principalmente o espaço de endereço de memória do hello, o gerenciamento de segmento da intel e o gerenciamento de página do hello. Usando o intel Core7 em um ambiente especificado, ele apresenta a conversão do endereço virtual VA para o endereço físico PA, o acesso à memória física e analisa a memória bifurcada do processo hello mapeamento durante execução de processo hello, falha de página e processamento de interrupção de falha de página e gerenciamento dinâmico de alocação de armazenamento.

Capítulo 8 gerenciamento de IO do hello
8.1 Método de gerenciamento de dispositivo Linux IO
(organizado no seguinte formato por você, exclua ao editar)
Modelagem de dispositivo:
gerenciamento de dispositivo de arquivo: interface unix io
​​​​8.2 Breve introdução à interface Unix IO e suas funções
(organizadas no seguinte formato por você mesmo, exclua ao editar)
8.3 Análise da implementação de printf
(o seguinte formato é auto-arranjado e excluído ao editar)
https://www.cnblogs.com/pianist/p/3315801.html
Gerar informações de exibição de vsprintf, para escrever a função do sistema, para interceptar -System call int 0x80 ou syscall, etc.
Sub-rotina do driver de exibição de caracteres: de ASCII para a biblioteca de fontes para exibir vram (armazenando informações de cores RGB de cada ponto).
O chip do display lê o vram linha por linha de acordo com a taxa de atualização, e transmite cada ponto (componente RGB) para o display de cristal líquido através da linha de sinal.
8.4 Análise da implementação de getchar
(arranjado no seguinte formato, delete ao editar)
exceção assíncrona - processamento de interrupção de teclado: sub-rotina de processamento de interrupção de teclado. O código de varredura de chave aceito é convertido em código ascii e salvo no buffer do teclado do sistema.
Getchar chama a função de sistema de leitura, lê o código ascii da chave por meio da chamada do sistema e não retorna até que a tecla Enter seja recebida.
8.5 Resumo deste capítulo
(O seguinte formato é organizado automaticamente e excluído durante a edição)
(Capítulo 8 1 ponto)
Conclusão
O processo pelo qual Hello passou é o seguinte:

  1. Entrada: Insira o código-fonte para obter o programa-fonte de hello.c.
  2. Pré-processamento: O pré-processamento com o comando gcc -E faz com que hello.c se transforme em hello.i.
  3. Compilar: Compile com o comando gcc -S para fazer hello.i crescer em hello.s (arquivo de linguagem de montagem).
  4. Montagem: Compilação usando o comando gcc -c faz hello.s crescer em hello.o (arquivo de objeto relocável)
  5. Link: link hello.o com o arquivo de objeto relocável e a biblioteca de vínculo dinâmico para se tornar o programa de objeto executável
    hello, e o programa executável hello nasce oficialmente.
  6. Execute: Digite ./hello no shell 2021112957 Wen Jiajie 1
  7. Crie um subprocesso: como a entrada do terminal não é um comando shell interno, o shell chama a função fork () para criar um subprocesso.
  8. Programa de carregamento: o shell chama a função execve, inicia o carregador, mapeia a memória virtual e, após inserir a entrada do programa, o programa começa a carregar a memória física e, em seguida, entra na função principal.
  9. Execução de instruções: A CPU aloca uma fatia de tempo para o processo.Em uma fatia de tempo, hello aproveita os recursos da CPU e executa seu próprio fluxo lógico de controle sequencialmente.
  10. Memória de acesso: A MMU mapeia o endereço de memória virtual usado no programa em um endereço físico por meio da tabela de páginas.
  11. Aplicar dinamicamente para memória: printf chamará malloc para solicitar memória no heap do alocador dinâmico de memória.
  12. Gerenciamento de sinal: Quando o programa está em execução, digitamos Ctrl+c, o kernel enviará um sinal SIGINT para o processo e encerrará o trabalho em primeiro plano. Quando Ctrl+z é inserido, o kernel enviará um sinal SIGTSTP para o processo e impedirá que o trabalho em primeiro plano seja suspenso.
  13. Encerramento: quando a execução do processo filho é concluída, o kernel faz com que o processo pai recicle o processo filho e passe o status de saída do processo filho para o processo pai. O kernel exclui todas as estruturas de dados criadas para este processo. Neste ponto, a vida do hello acabou!

Anexo
hello.c Programa fonte
hello.i Arquivo pré-processado
hello.s Arquivo de montagem compilado
hello.o Arquivo de execução de destino relocável compilado
hello Arquivo executável vinculado
hello.elf Formato ELF de hello.o
hello1 .txt hello.o disassembly
hello2.txt hello disassembly
hello1.elf olá formato ELF

Referências
[1] Compreensão aprofundada do livro original do sistema de computador 3ª edição - versão em texto.pdf
[2] https://blog.csdn.net/sy06106/article/details/118274118

Acho que você gosta

Origin blog.csdn.net/m0_64206188/article/details/130834441
Recomendado
Clasificación