Processos e chamadas de sistema

Aprenda como um sistema operacional compartilha uma CPU entre muitos programas em execução e ainda mantém cada programa em seu próprio espaço. Você verá o que é um processo e como ele passa pelos estados de execução, pronto e bloqueado; criará processos em C com fork, executará novos programas com exec e coletará seus resultados com wait, até chegar a um interpretador de comandos simples com redirecionamento e canais de comunicação. Depois, examine o funcionamento interno: os modos usuário e núcleo, como uma chamada de sistema aciona uma armadilha para entrar no núcleo, como exceções e interrupções diferem e como a execução direta limitada e a interrupção do temporizador permitem que o sistema operacional retome a CPU e passe de um processo para outro.

IntermediárioAula 1 de 9 do curso Curso de Sistemas operacionais

O que você vai aprender

  1. Descreva as principais funções do sistema operacional e a história e as ideias de projeto que explicam como ele compartilha e protege a máquina.
    • Descreva as principais funções de um sistema operacional
      Explique como o sistema operacional transforma o hardware em recursos mais fáceis de usar, compartilhados e protegidos para os programas.
    • Explique como a multiprogramação e o compartilhamento de tempo moldaram o sistema operacional
      Acompanhe a passagem do processamento em lote para a multiprogramação e o compartilhamento de tempo, veja por que cada etapa exigiu novas proteções e compare as principais formas de organizar um núcleo.
  2. Explique o que é um processo, o que o sistema operacional registra sobre cada um e como os processos passam pelos estados de execução, pronto e bloqueado.
    • Explique o que é um processo e quais informações o sistema operacional mantém sobre ele
      Diferencie programa de processo, descreva o estado da máquina e o espaço de endereçamento de um processo e explique como o SO cria e acompanha um processo.
    • Acompanhe um processo ao longo de seus estados
      Use os estados de execução, pronto e bloqueado e suas transições para acompanhar como os processos compartilham uma CPU e reconheça os estados adicionais informados por sistemas reais.
  3. Use a API de processos do Unix em C para criar processos filhos, executar novos programas neles, coletar seus resultados, encerrá-los e combinar essas chamadas em um interpretador de comandos simples.
    • Crie um processo filho com fork
      Chame fork, diferencie o pai do filho pelo valor de retorno e preveja o que cada processo vê e imprime.
    • Execute outro programa com exec
      Substitua o programa em um processo com exec, passe os argumentos corretamente e explique por que o Unix separa a criação de um processo do carregamento de um programa.
    • Aguarde processos filhos, leia os códigos de saída e encerre processos
      Encerre processos com exit e sinais, colete o resultado de um filho com wait e waitpid, decodifique seu estado retornado e impeça que processos zumbis e órfãos causem problemas.
    • Construa um interpretador de comandos simples com fork, exec, wait e redirecionamento
      Combine as chamadas de processo em um ciclo de interpretador de comandos que executa comandos, redireciona entrada e saída, conecta comandos com canais de comunicação e executa tarefas em segundo plano.
  4. Explique como o hardware separa o código privilegiado do núcleo dos programas de usuário e como os programas atravessam essa fronteira de forma controlada por meio de chamadas de sistema, exceções e interrupções.
    • Diferencie o modo usuário do modo núcleo
      Explique o que cada modo da CPU permite, quais operações são privilegiadas e o que acontece quando um programa ultrapassa os limites de seu modo.
    • Explique passo a passo como uma chamada de sistema funciona
      Acompanhe uma chamada de sistema desde a função de biblioteca que a encapsula, passando pela armadilha que leva ao núcleo e pelo retorno, e interprete seu valor de retorno e seus erros.
    • Diferencie armadilhas, exceções e interrupções
      Classifique os eventos que levam a CPU ao núcleo, explique como funciona a tabela de tratadores do núcleo e preveja o que acontece após cada tipo de evento.
  5. Explique como o sistema operacional executa programas diretamente na CPU em velocidade máxima enquanto mantém o controle e como passa a CPU de um processo para outro.
    • Explique a execução direta limitada
      Explique os dois problemas de executar programas diretamente na CPU e como o modo usuário, as armadilhas e a interrupção do temporizador os resolvem, passo a passo, da inicialização ao término do processo.
    • Acompanhe uma troca de contexto e estime seu custo
      Acompanhe cada etapa da passagem da CPU de um processo para outro, diferencie troca de contexto de troca de modo e estime quanto tempo de CPU as trocas custam.

Guia de estudo de Processos e chamadas de sistema

5 capítulos

1

Explique o que um sistema operacional faz

O SO oferece a programas comuns formas úteis de processar dados, armazená-los e usar dispositivos enquanto compartilha a máquina com segurança. Suas responsabilidades explicam por que uma aplicação de desktop não precisa de seu próprio driver de disco e por que um programa com falha não deve interromper tudo. Acompanhe a passagem de computadores que executavam uma tarefa por vez para sistemas interativos e depois compare como diferentes projetos de núcleo dividem o trabalho privilegiado.

1.1 Descreva as principais funções de um sistema operacional

Um navegador, um compilador e um reprodutor de música podem progredir em um mesmo computador. Cada um precisa de tempo de CPU, memória e acesso a dispositivos. O sistema operacional (SO) fornece esses recursos por meio de serviços que os programas podem usar sem controlar toda a máquina.

Forneça recursos úteis aos programas

A virtualização transforma um recurso físico em um recurso virtual conveniente. A virtualização da CPU permite que muitos processos se comportem como se cada um tivesse seu próprio processador. O SO executa cada um por um tempo e preserva seu estado entre os turnos. A virtualização da memória dá a cada processo seu próprio espaço de endereçamento: os endereços que suas instruções podem usar.

Uma abstração oculta detalhes por trás de uma interface útil. Um arquivo é uma abstração para bytes armazenados. Um programa pode chamar write sem saber se esses bytes vão para um SSD ou uma unidade de rede. Uma chamada de sistema fornece uma forma controlada de solicitar um serviço do núcleo. O trabalho específico de cada dispositivo fica por trás da interface.

Compartilhe recursos e proteja programas

Como gerenciador de recursos, o SO decide quem recebe tempo de CPU, memória e atendimento dos dispositivos. Ele procura manter o hardware útil enquanto dá aos programas acesso razoável. O compartilhamento também exige proteção: um programa não deve sobrescrever a memória privada de outro nem desabilitar o temporizador para manter a CPU.

O hardware ajuda a impor esse limite. As instruções das aplicações executam em modo usuário com permissões limitadas. O código do núcleo executa com o privilégio necessário para gerenciar a máquina. Um temporizador pode interromper um programa mesmo que ele nunca solicite nada ao SO.

Para um notebook que baixa um arquivo enquanto compila código, acompanhe três tarefas separadas:

  1. O dispositivo de rede informa a chegada de dados. O núcleo os disponibiliza ao processo que faz o download.
  2. O SO compartilha o tempo de CPU entre o navegador, o compilador e outros processos prontos.
  3. O compilador solicita operações de arquivo para armazenar sua saída. Seu código não controla diretamente o controlador de armazenamento.
Separe o núcleo da interface

O núcleo é a parte central privilegiada. O interpretador de comandos, a área de trabalho e a maioria das bibliotecas de sistema executam como programas comuns, mesmo quando são distribuídos com o SO. Fechar uma janela da área de trabalho não remove o núcleo. Um servidor sem interface gráfica ainda precisa de gerenciamento de processos, proteção de memória e serviços de dispositivos.

Em uma CPU lógica, o código de usuário e o código do núcleo se alternam. Enquanto o compilador executa instruções comuns de usuário, o núcleo não está executando ao mesmo tempo nessa CPU. Chamadas de sistema, exceções e interrupções de hardware devolvem o controle ao núcleo.

Escolha como e depois escolha quem

Um mecanismo fornece uma operação; uma política escolhe como usá-la. Salvar os registradores de um processo e restaurar os de outro é um mecanismo. Escolher qual processo pronto recebe o próximo turno é uma política. Manter os dois separados permite que o SO altere suas escolhas de escalonamento sem substituir o mecanismo de troca.

O projeto de um SO equilibra abstrações úteis, desempenho, proteção, confiabilidade, segurança e uso de energia. O tempo adicional de CPU e a memória gastos no gerenciamento são sobrecarga. Em toda a área, três temas recorrentes são virtualização, concorrência (atividades em andamento ao mesmo tempo) e persistência, que mantém dados armazenados após o fim de um programa ou uma falta de energia.

Principais conclusões

  • A virtualização dá aos programas visões convenientes do hardware compartilhado.
  • Abstrações como arquivos ocultam detalhes dos dispositivos por trás de operações úteis.
  • O núcleo é privilegiado; o interpretador de comandos e a área de trabalho são programas comuns.
  • Um mecanismo fornece uma operação; uma política escolhe quando ou para quem ela é usada.
  • Proteção e baixa sobrecarga são objetivos do projeto de um SO.

1.2 Explique como a multiprogramação e o compartilhamento de tempo moldaram o sistema operacional

Os sistemas operacionais evoluíram à medida que os computadores passaram de executar tarefas isoladas a atender muitas atividades ao mesmo tempo. Cada mudança resolveu um problema prático: hardware ocioso, interação lenta ou falhas que se propagavam entre programas.

Dos lotes ao trabalho sobreposto

No processamento em lote, as tarefas submetidas executam uma após a outra, sem interação com quem as submeteu. Os primeiros softwares de apoio ajudavam principalmente a carregar tarefas e executar operações comuns de entrada e saída. Se a tarefa atual precisasse esperar por um dispositivo, a CPU poderia ficar ociosa.

A multiprogramação mantém várias tarefas na memória. Quando uma bloqueia para entrada e saída, outra pode processar dados. Suponha que uma tarefa processe dados repetidamente por 20 ms e espere pela entrada e saída de disco por 80 ms. Sozinha, sua utilização da CPU é 20 / 100 = 20%. Com duas tarefas assim e sobreposição adequada, o processamento útil pode ocupar 40% do tempo decorrido. A espera pelo dispositivo não diminuiu; o tempo de CPU que ficaria ocioso foi preenchido.

O ganho depende de haver trabalho pronto. Adicionar tarefas não pode elevar a utilização da CPU acima de 100%, e tarefas que esperam pelo mesmo dispositivo ainda podem enfrentar um gargalo. Várias tarefas na memória também precisam de proteção: uma não deve sobrescrever outra nem danificar os dados do próprio SO.

Torne a interação ágil

O compartilhamento de tempo dá aos processos turnos curtos na CPU. Um terminal ou ambiente gráfico pode responder enquanto outros programas continuam trabalhando. Esperar uma tarefa em lote terminar antes de atender à tecla pressionada por outro usuário seria uma experiência bem diferente.

Método de compartilhamentoO que é divididoExemplo
Compartilhamento de tempoUso de um recurso em intervalos sucessivosTurnos de CPU
Compartilhamento de espaçoCapacidade ocupada por diferentes usuários ao mesmo tempoRegiões de memória ou espaço em disco

Concorrência significa que várias atividades estão em andamento durante o mesmo período. Suas instruções podem ser intercaladas em uma CPU lógica. Paralelismo significa que as instruções executam de fato simultaneamente em diferentes recursos de processamento. A multiprogramação não exige hardware paralelo.

Coloque serviços dentro ou fora do núcleo
ProjetoOnde a maioria dos serviços executaPrincipal relação entre vantagens e custos
Núcleo monolíticoSistemas de arquivos, drivers e rede executam em modo núcleoChamadas diretas são eficientes; um erro em um driver pode danificar o núcleo
MicronúcleoUma pequena parte central privilegiada dá suporte a processos de serviço em modo usuárioO isolamento de serviços ajuda a conter falhas; a comunicação acrescenta trabalho
Núcleo híbridoCombina elementos das duas abordagensO limite exato entre os serviços depende do projeto

O Linux é monolítico e pode carregar módulos no núcleo. MINIX 3, QNX e seL4 ilustram projetos de micronúcleo. O Windows NT e o XNU da Apple são comumente descritos como projetos híbridos. Um driver em modo usuário muitas vezes pode ser reiniciado após uma falha, enquanto um driver privilegiado tem acesso que pode fazer sua falha afetar todo o sistema.

Os primeiros sistemas para computadores pessoais, como MS-DOS e o Mac OS clássico, não tinham a proteção de memória entre processos esperada hoje. Sistemas modernos combinam regras do SO com sua aplicação pelo hardware. Turnos curtos de CPU, por si só, não impediriam que um programa alterasse a memória de outro; compartilhamento e proteção resolvem partes diferentes do problema.

Principais conclusões

  • A multiprogramação preenche as esperas por entrada e saída com o processamento de outra tarefa.
  • O compartilhamento de tempo dá suporte ao uso interativo com turnos curtos de CPU.
  • O trabalho concorrente pode ser intercalado em uma CPU; o trabalho paralelo executa simultaneamente.
  • O compartilhamento de tempo divide turnos; o compartilhamento de espaço divide capacidade.
  • A organização do núcleo equilibra chamadas diretas a serviços e maior isolamento entre eles.
  • Várias tarefas residentes exigem proteção de memória e de privilégios.
2

Descreva os processos e os estados pelos quais passam

Um arquivo de programa é apenas o ponto de partida. Para executá-lo, o SO constrói e acompanha um processo ativo com memória, registradores, descritores abertos e um lugar na lista de processos. Aprenda a interpretar esse estado e depois acompanhe dois processos enquanto o trabalho da CPU se sobrepõe às esperas por dispositivos. A diferença entre esperar por uma CPU e esperar por um evento explica qual processo o escalonador pode escolher.

2.1 Explique o que é um processo e quais informações o sistema operacional mantém sobre ele

Um executável em disco pode ser iniciado repetidamente. Cada inicialização precisa de seu próprio estado mutável para que uma instância possa pausar, retomar ou falhar de forma independente. Essa instância ativa é um processo.

Identifique a instância em execução

Um programa consiste em instruções e dados estáticos armazenados. Um processo combina um programa com sua memória, seus registradores da CPU e seu estado de entrada e saída atuais. Três interpretadores de comandos podem executar o mesmo arquivo executável e manter variáveis, diretórios atuais e históricos diferentes.

O contador de programa (PC) identifica a próxima instrução. O ponteiro de pilha localiza a pilha ativa. Registradores gerais guardam valores intermediários. Quando um processo para de executar, é preciso preservar estado suficiente para que ele continue na instrução correta com os valores corretos.

Interprete a organização do espaço de endereçamento

O espaço de endereçamento de um processo é a memória que suas instruções podem endereçar. Uma organização comum usada no ensino coloca código e dados estáticos próximos aos endereços mais baixos, uma área de alocação dinâmica acima deles e uma pilha próxima aos endereços mais altos. A área de alocação dinâmica comporta objetos alocados dinamicamente; a pilha dá suporte a chamadas de função e ao estado local. Nessa organização, a área de alocação dinâmica cresce em direção aos endereços mais altos e a pilha em direção aos mais baixos. As organizações reais dependem do sistema, e implementações de malloc podem obter memória de mais de uma forma.

Dois processos podem imprimir o mesmo endereço virtual para uma variável e ainda assim manter valores diferentes nele. Endereços virtuais pertencem à visão de memória de um processo. Números de endereço iguais não estabelecem uma área de armazenamento gravável compartilhada.

Inicie um programa

Para iniciar uma nova imagem de programa, o SO prepara estas partes:

  1. Seu código executável e seus dados, com partes carregadas quando necessário nos sistemas modernos.
  2. Uma pilha de usuário inicial e informações de inicialização, incluindo as strings de argumentos e o vetor de argumentos usados pelo ambiente de execução para chamar main.
  3. Memória para alocação dinâmica e o estado inicial dos registradores.
  4. Um ambiente de entrada e saída, incluindo descritores de arquivo herdados.
  5. Um ponto de entrada onde o código de inicialização do programa começa.

No Unix, descritores de arquivo são pequenos números inteiros que identificam recursos de entrada e saída abertos. Por convenção do interpretador de comandos, 0 é a entrada padrão, 1 a saída padrão e 2 o erro padrão. Eles costumam estar conectados a um terminal. Um processo pode herdar descritores adicionais; os três números conhecidos não são um limite nem uma garantia de que todo processo sempre os tenha abertos.

Examine o registro do núcleo

O bloco de controle de processo (PCB) registra informações de que o núcleo precisa para gerenciar um processo: PID, estado, contexto de execução salvo, informações de memória, descritores abertos, pai, proprietário, diretório atual e informações de contabilização. O Linux usa task_struct; o xv6 usa struct proc. Uma tabela ou lista de processos organiza esses registros.

Enquanto um processo executa, seus registradores atuais estão na CPU. Na entrada no núcleo, seus registradores de usuário são salvos em sua pilha do núcleo; uma troca de contexto também preserva o contexto de execução do núcleo. O PCB não é um espelho continuamente atualizado de cada registrador de usuário.

Por exemplo, uma listagem de ps no Linux pode conter 8240 8200 S bash e 8291 8240 R ps. O segundo processo tem PID 8291 e PID do pai 8240, então é filho do interpretador de comandos. R abrange processos em execução e prontos. Um PID identifica um processo ativo dentro de seu espaço de nomes de PID, mas, após a liberação de seus recursos, esse número pode ser reutilizado.

Principais conclusões

  • Um processo é uma instância de programa com estado de memória, registradores e entrada e saída.
  • Endereços virtuais idênticos em processos diferentes podem identificar dados diferentes.
  • O PCB mantém o registro de gerenciamento do núcleo, não os registradores atuais da CPU atualizados continuamente.
  • Descritores de arquivo são herdados; 0, 1 e 2 têm funções convencionais.
  • PIDs podem ser reutilizados após o término de um processo e a liberação de seus recursos.

2.2 Acompanhe um processo ao longo de seus estados

Um processo pode existir sem executar uma instrução naquele momento. Seu estado informa ao SO se ele pode usar uma CPU ou se precisa primeiro esperar por outra coisa. Essa distinção explica por que um computador ocupado pode ter muitos processos, mas apenas alguns executando em um instante.

Decida do que o processo precisa
EstadoSignificadoExemplo
Em execuçãoExecutando em uma CPUUm compilador gerando instruções
ProntoApto a executar, esperando por tempo de CPUUm processamento cujo turno acabou de terminar
BloqueadoEsperando por um evento antes de poder continuarUma leitura do terminal esperando por entrada

Um processo bloqueado não consome tempo de CPU enquanto está bloqueado. Seu dispositivo ou evento ainda pode progredir. Um processo pronto já tem o que precisa, exceto uma CPU.

As transições básicas são pronto → em execução quando selecionado, em execução → pronto quando retirado da CPU, em execução → bloqueado quando precisa esperar e bloqueado → pronto quando seu evento ocorre. A conclusão do evento torna um processo apto a executar; ela não lhe concede a CPU imediatamente. Escolher entre os processos aptos é função do escalonador.

Acompanhe o trabalho sobreposto

Use uma CPU lógica e ignore a sobrecarga de troca de contexto. O processo A processa dados por 2 ms, espera pela entrada e saída de disco por 3 ms e depois processa dados por 1 ms. O processo B precisa de 4 ms de tempo de CPU. A começa primeiro.

IntervaloCPUEstado de AEstado de B
0–2 msAEm execuçãoPronto
2–5 msBBloqueadoEm execução
5–6 msBProntoEm execução
6–7 msAEm execuçãoFinalizado

Em 2 ms, A inicia a entrada e saída e bloqueia. B processa dados enquanto o disco trabalha. Em 5 ms, a entrada e saída de A termina, mas o SO deixa B concluir seu quarto milissegundo de CPU antes de executar A. Essa é uma escolha de política, não uma transição adicional diretamente de bloqueado para em execução.

O trabalho total da CPU é 2 + 1 + 4 = 7 ms, e ambos terminam após 7 ms. Se A executasse até o fim antes de B começar, o tempo decorrido seria 2 + 3 + 1 + 4 = 10 ms. A utilização da CPU seria 7 / 10 = 70%, pois nenhum outro trabalho preencheria a espera por entrada e saída.

Leia os estados em um sistema real

O ps do Linux reúne os estados de execução e pronto em R. Ele mostra suspensão interrompível como S, suspensão ininterruptível, muitas vezes para entrada e saída, como D, um processo parado como T e um processo encerrado cujo estado de término ainda não foi coletado como Z. Um processo parado não está simplesmente esperando pela conclusão de uma operação comum de entrada e saída. Um zumbi terminou de executar e mantém apenas o registro necessário para informar seu resultado.

Um novo processo também passa pela criação antes de estar pronto. Em um sistema com quatro CPUs lógicas, no máximo quatro processos com uma única thread podem executar simultaneamente. Filas de prontos organizam o trabalho apto a executar; listas de bloqueados acompanham os eventos de que os processos precisam. Uma troca de contexto preserva o estado de execução de um processo e restaura o de outro quando o SO muda qual processo executa.

Principais conclusões

  • Pronto significa esperando por uma CPU; bloqueado significa esperando por um evento.
  • A conclusão de uma operação de entrada e saída muda um processo de bloqueado para pronto.
  • Sobrepor esperas por dispositivos a trabalho útil de CPU pode reduzir o tempo total decorrido.
  • O Linux informa os estados de pronto e de execução com o mesmo código, R.
  • Um processo zumbi terminou, mas seu estado de término ainda não foi coletado.
  • O número de CPUs lógicas limita a execução simultânea de processos com uma única thread.
3

Crie e controle processos com fork, exec e wait

A interface de processos do Unix separa a criação de um filho, a substituição de seu programa e a coleta de seu resultado. Acompanhe o que cada chamada altera e o que preserva, incluindo variáveis privadas e o estado compartilhado de arquivos abertos. Depois, reúna as chamadas para reproduzir o comportamento de um interpretador de comandos: comandos em primeiro plano, redirecionamento, sequências de comandos conectados por canais e tarefas em segundo plano. Cada etapa depende de saber qual processo a está executando.

3.1 Crie um processo filho com fork

fork() cria um processo filho a partir do processo pai que faz a chamada. Ambos continuam imediatamente após a chamada, de modo que um caminho do código-fonte passa a ter duas execuções. Seu valor de retorno informa a cada processo qual é seu papel.

Trate os três resultados
pid_t rc = fork();
if (rc < 0) {
    perror("fork");
    exit(1);
} else if (rc == 0) {
    printf("child %ld\n", (long) getpid());
} else {
    printf("parent of %ld\n", (long) rc);
}

Use <unistd.h> para fork e getpid, <stdio.h> para impressão e <stdlib.h> para exit. Em caso de sucesso, o pai recebe o PID positivo do filho; o filho recebe 0. Em caso de falha, o processo que faz a chamada recebe -1, errno explica o erro e nenhum filho é criado.

Se o pai tem PID 5000 e o filho tem 5001, o pai imprime “parent of 5001” e o filho imprime “child 5001”. Não há garantia de qual deles executará primeiro. getpid() retorna o PID do próprio processo que faz a chamada; getppid() retorna o PID de seu pai. O filho não reinicia main nem repete as instruções anteriores ao fork.

Acompanhe a memória privada e a entrada e saída compartilhada

Pai e filho começam com conteúdos iguais de memória comum em espaços de endereçamento separados. Se x é 10 antes da chamada e o filho altera sua cópia para 20, a cópia do pai permanece 10. Até o endereço impresso de x pode ser igual: esses endereços virtuais iguais pertencem a espaços de endereçamento de processos diferentes.

A cópia na escrita evita copiar antecipadamente todas as páginas de memória. Pai e filho compartilham inicialmente páginas físicas protegidas contra escrita. Quando um deles escreve em uma página privada, o núcleo providencia uma cópia privada antes de permitir a escrita. Isso preserva o comportamento de memórias separadas e reduz o custo quando um filho logo chama exec.

Os descritores de arquivo se comportam de forma diferente. Suas entradas copiadas se referem à mesma descrição de arquivo aberto subjacente, que inclui o deslocamento atual no arquivo. Leituras sequenciais por um descritor herdado avançam esse deslocamento compartilhado. A tabela de descritores é copiada, mas o estado subjacente do arquivo aberto no núcleo pode continuar compartilhado.

Conte apenas os forks que um processo alcança

Se todas as chamadas forem bem-sucedidas, fork(); fork(); deixa quatro processos: ambos os processos produzidos pela primeira chamada executam a segunda. Um laço de três iterações alcançado por todos os processos deixa 23=8 processos, incluindo o original. Um fork dentro de um ramo exclusivo do pai é alcançado por menos processos, então a duplicação repetida deixa de descrever o programa inteiro.

Descarregue o texto em buffer deliberadamente

printf pode deixar texto em um buffer no espaço de usuário. fork copia esse buffer junto com o restante da memória. Se a saída for redirecionada para um fluxo totalmente armazenado em buffer, o texto impresso antes do fork poderá ser descarregado por ambos os processos no término normal.

Em printf("start\n"); fork();, o terminal usual, que usa buffer por linha, descarrega a quebra de linha antes do fork. Um arquivo redirecionado pode retê-la e receber duas cópias. fflush(stdout) antes de fork() envia o texto pendente uma vez. A duplicação vem dos dados em buffer copiados, e não da execução do printf anterior duas vezes.

Principais conclusões

  • fork() retorna o PID do filho no pai, 0 no filho ou -1 em caso de falha.
  • Ambos os caminhos bem-sucedidos continuam logo após fork().
  • Alterações na memória privada comum do filho não mudam os valores do pai.
  • Descritores herdados podem compartilhar uma descrição de arquivo aberto e seu deslocamento.
  • A contagem de processos depende de quais processos alcançam cada fork.
  • Descarregue a saída pendente antes de um fork quando uma descarga duplicada for indesejada.

3.2 Execute outro programa com exec

Um processo pode manter sua identidade enquanto muda o programa que executa. A família exec realiza essa substituição. Combiná-la com fork permite que um pai continue seu trabalho enquanto um filho se transforma em outro programa.

Substitua a imagem, mantenha o processo

Uma chamada exec bem-sucedida substitui o código, os dados estáticos, a área de alocação dinâmica e a pilha antigos. O núcleo prepara os argumentos e o estado de entrada do novo programa e depois inicia seu código de inicialização. O ponto da chamada anterior deixa de existir, então uma chamada exec bem-sucedida nunca retorna a ele.

O PID, a relação com o pai e o diretório atual permanecem. Os descritores abertos permanecem, exceto os marcados para fechamento na execução. As ações dos sinais capturados voltam ao padrão; os sinais ignorados geralmente continuam ignorados. As credenciais normalmente permanecem as mesmas, mas as permissões de um executável para definir o ID de usuário ou de grupo podem alterar as credenciais efetivas quando o sistema permite.

OperaçãoNovo processo?Efeito principal
fork()SimCria um filho com estado de execução copiado
Exec bem-sucedidoNãoSubstitui a imagem do programa do processo que faz a chamada
Construa um vetor de argumentos válido

Para executar wc -l notes.txt, o filho pode usar:

char *args[] = {"wc", "-l", "notes.txt", NULL};
execvp(args[0], args);
perror("execvp");
_exit(127);

As strings são argumentos separados, não uma única linha de comando combinada. Por convenção, args[0] identifica o programa. O NULL final é um ponteiro que marca o fim do array; não é a string “NULL”. Sem um terminador válido, a chamada pode ler além do array.

As linhas após execvp tratam a falha. Se o programa não for encontrado, exec retorna -1 e define errno, por exemplo, como ENOENT. Então _exit encerra o filho sem descarregar os buffers de saída da biblioteca C herdados. Retornar ao ciclo do interpretador de comandos após uma falha criaria acidentalmente outro caminho de execução do interpretador.

Interprete o nome da variante

O v em execv significa que os argumentos chegam como um vetor; as variantes l recebem uma lista. Uma variante p procura um nome de programa no PATH. Uma variante e fornece um ambiente explícito. A chamada de sistema subjacente do Linux é execve; a biblioteca fornece as variantes mais convenientes.

execvp("wc", args) procura no PATH. execv("wc", args) trata “wc” como um caminho relativo no diretório atual. Um caminho absoluto como /usr/bin/wc pode ser usado onde esse executável estiver instalado. Exec não interpreta a sintaxe do interpretador de comandos: uma string > em args é apenas um argumento, a menos que um programa a interprete.

Use o intervalo antes da substituição

No exemplo com o pai de PID 6000 e o filho de PID 6001, o filho se transforma em wc, mas continua com PID 6001. Seu pai pode esperar por esse PID. Antes de exec, o filho pode redirecionar descritores, conectar um canal de comunicação ou mudar de diretório; o programa substituto herda o ambiente preparado.

Essa separação é fundamental para o interpretador de comandos do Unix. O CreateProcess do Windows combina a criação e o carregamento do programa, usando parâmetros para configurar o novo processo. O POSIX também oferece posix_spawn para uma operação combinada.

Principais conclusões

  • Exec substitui uma imagem de programa sem criar um processo nem mudar seu PID.
  • Em caso de sucesso, exec nunca retorna; as instruções seguintes tratam a falha.
  • Termine vetores de argumentos com um ponteiro NULL.
  • As variantes p pesquisam no PATH; uma variante que recebe apenas um caminho não o faz.
  • A configuração de descritores antes de exec permite que um programa inalterado participe de redirecionamentos e canais de comunicação.

3.3 Aguarde processos filhos, leia os códigos de saída e encerre processos

Um pai precisa saber se um filho terminou e como terminou. O encerramento do processo e a coleta do resultado são etapas separadas: o filho termina, e depois o pai coleta o registro restante no núcleo.

Encerre um processo

Retornar de main ou chamar exit(status) encerra a execução normal. exit descarrega os buffers de saída da biblioteca C e executa os tratadores de saída registrados. _exit(status) encerra sem essa liberação de recursos pela biblioteca, o que é útil após uma falha de exec em um filho criado com fork. Por convenção, 0 significa sucesso e um valor diferente de zero significa falha.

Um sinal também pode encerrar um processo. SIGTERM solicita o encerramento e pode ser tratado; SIGKILL não pode ser capturado nem ignorado. SIGSTOP para a execução e também não pode ser capturado nem ignorado; SIGCONT retoma um processo parado. Um terminal normalmente envia SIGINT para Ctrl-C e SIGTSTP para Ctrl-Z à tarefa em primeiro plano. Um sinal é uma notificação de software a um processo, não uma interrupção de hardware.

Aguarde um resultado

wait(&status) espera por um filho e retorna seu PID. waitpid(pid, &status, 0) seleciona um PID positivo de um filho específico. O resultado de um filho que terminou e cujo estado ainda não foi coletado pode ser coletado imediatamente. Caso contrário, uma espera bloqueante comum suspende o pai até que um resultado correspondente esteja disponível.

Com WNOHANG, waitpid retorna 0 quando existem filhos correspondentes, mas nenhum tem um resultado pronto. Um resultado positivo identifica o filho cujo estado foi coletado. Um resultado de -1 é um erro: ECHILD significa que não resta nenhum filho correspondente; EINTR significa que um tratador de sinal interrompeu a espera.

Decodifique o estado antes de usá-lo
int status;
pid_t r;
do {
    r = waitpid(child, &status, 0);
} while (r == -1 && errno == EINTR);
if (r == -1) {
    perror("waitpid");
} else if (WIFEXITED(status)) {
    printf("exit %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
    printf("signal %d\n", WTERMSIG(status));
}

Esse código usa <sys/wait.h>, <errno.h> e <stdio.h>. O estado armazenado codifica a forma de término e também um resultado. WEXITSTATUS tem significado depois que WIFEXITED é verdadeiro; WTERMSIG é usado após WIFSIGNALED.

Apenas os oito bits menos significativos de um valor de saída normal estão disponíveis. Portanto, exit(300) produz 44, enquanto exit(-1) produz 255. No Linux, exit(3) geralmente produz o estado retornado por wait bruto 768; imprimir esse inteiro bruto não é decodificar o código de saída.

Colete o estado de zumbis e reconheça órfãos

Um processo zumbi terminou, mas mantém seu PID e resultado até que uma espera os colete. Ele não executa código, então enviar SIGKILL não pode removê-lo. Um pai de longa duração precisa coletar o estado de cada filho. Para coletar todos os filhos, continue esperando após cada PID positivo, tente novamente em caso de EINTR e pare em ECHILD; informe outros erros.

Um processo órfão perdeu seu pai e ainda pode executar. O Linux lhe atribui como novo pai um subcoletor adequado ou o processo init do espaço de nomes, que pode coletar seu resultado mais tarde. As permissões comuns para enviar sinais dependem das identidades e dos privilégios dos usuários. Executar como root concede permissões, não execução em modo núcleo.

Principais conclusões

  • Cada espera bem-sucedida coleta o resultado de um filho.
  • Decodifique o término normal e o término por sinal com suas respectivas macros de estado.
  • WNOHANG retorna 0 quando existe um filho correspondente, mas ele não tem um resultado pronto.
  • Repita uma espera interrompida em caso de EINTR; ECHILD significa que não resta nenhum filho correspondente.
  • Um zumbi terminou; um órfão perdeu seu pai e ainda pode estar executando.
  • SIGKILL e SIGSTOP não podem ser capturados nem ignorados.
  • exit descarrega os buffers da biblioteca C; _exit não.

3.4 Construa um interpretador de comandos simples com fork, exec, wait e redirecionamento

Um interpretador de comandos transforma texto de comando em criação de processos, configuração de descritores e coleta de resultados. É um programa comum de usuário que usa os mesmos serviços protegidos do SO disponíveis a outros programas.

Inicie um comando externo

Para wc -l notes.txt, o interpretador constrói o vetor de argumentos e inicia o comando. As palavras analisadas formam um vetor de argumentos terminado em nulo, args:

pid_t child = fork();
if (child == -1) {
    perror("fork");
} else if (child == 0) {
    execvp(args[0], args);
    perror("execvp");
    _exit(127);
} else {
    int status;
    pid_t result;
    do {
        result = waitpid(child, &status, 0);
    } while (result == -1 && errno == EINTR);
    if (result == -1) perror("waitpid");
    else if (WIFEXITED(status)) {
        printf("exit %d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("signal %d\n", WTERMSIG(status));
    }
}

Esse código usa <unistd.h>, <sys/wait.h>, <errno.h> e <stdio.h>. O interpretador de comandos que o envolve lê e analisa cada comando, executa esse caminho de inicialização e depois exibe seu próximo prompt.

Somente o filho substitui seu programa; o interpretador pode coletar a falha de um comando como resultado de término. Comandos internos como cd precisam alterar o próprio estado do interpretador: mudar o diretório de um filho deixaria o interpretador pai onde estava.

Redirecione um descritor antes de exec

Para ls -l > out.txt, o filho abre o destino para escrita, criando-o ou truncando-o. Depois, faz a saída padrão se referir a esse arquivo:

int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) { perror("open"); _exit(1); }
if (dup2(fd, STDOUT_FILENO) == -1) {
    perror("dup2"); _exit(1);
}
if (fd != STDOUT_FILENO && close(fd) == -1) {
    perror("close"); _exit(1);
}

open precisa de <fcntl.h>; as chamadas de descritores e _exit usam <unistd.h>. O modo de criação é filtrado pela máscara de criação de arquivos do processo. A verificação fd != STDOUT_FILENO mantém o descritor 1 aberto mesmo que o próprio open tenha retornado 1. Após a configuração bem-sucedida, exec preserva o descritor 1 e ls escreve normalmente sem saber do redirecionamento.

Uma abordagem mais antiga fecha o descritor 1 antes de abrir o arquivo: open retorna o descritor não usado de menor número. dup2 expressa o destino explicitamente. O redirecionamento de entrada conecta, de forma semelhante, um arquivo legível ao descritor 0.

Conecte dois comandos

Para ls | wc -l, pipe(fds) cria uma extremidade de leitura fds[0] e uma extremidade de escrita fds[1]. O filho produtor conecta a extremidade de escrita ao descritor 1. O filho consumidor conecta a extremidade de leitura ao descritor 0. Após a duplicação, cada um fecha os descritores originais do canal de que não precisa. O pai fecha suas cópias e espera pelos dois filhos.

Crie os dois filhos antes de esperar por qualquer um deles. Um produtor pode encher o canal e bloquear; o leitor precisa existir para esvaziá-lo. O leitor só alcança o fim de arquivo quando todos os descritores de escrita estão fechados e os dados do canal armazenados em buffer foram consumidos. Uma extremidade de escrita não usada mantida aberta pelo pai pode fazer wc esperar indefinidamente.

Mantenha o prompt disponível

Para sleep 60 &, o interpretador inicia o filho e apresenta o próximo prompt sem esperar imediatamente. Depois, usa esperas não bloqueantes ou responde a SIGCHLD para coletar o estado dos filhos concluídos.

O código de saída também controla combinações de comandos. make && ./app inicia o segundo comando apenas após sucesso; make || echo failed inicia o segundo apenas após falha. Essas decisões usam o resultado decodificado, não o estado codificado bruto retornado por wait.

Principais conclusões

  • Um comando externo executa em um filho; um comando interno que altera o estado executa no interpretador de comandos.
  • O redirecionamento altera descritores no filho antes de exec.
  • Um canal de comunicação conecta a saída padrão de um produtor à entrada padrão de um consumidor.
  • Inicie os dois lados antes de esperar e feche as extremidades não usadas do canal.
  • O fim de arquivo exige que todas as extremidades de escrita do canal estejam fechadas depois que os dados em buffer forem consumidos.
  • A execução em segundo plano adia a espera; o interpretador ainda precisa coletar o estado do filho mais tarde.
4

Explique o modo usuário, o modo núcleo e as chamadas de sistema

Programas de usuário precisam de serviços do SO sem assumir o controle da máquina. Os modos de privilégio do hardware criam essa fronteira, e caminhos de entrada controlados permitem que o núcleo atenda a requisições e eventos. Acompanhe uma chamada de sistema por sua função de biblioteca e seu tratador no núcleo e depois diferencie armadilhas deliberadas, falhas de instrução e interrupções de dispositivos por suas causas e possíveis resultados.

4.1 Diferencie o modo usuário do modo núcleo

O núcleo precisa permitir que programas processem dados rapidamente e ao mesmo tempo impedir operações que poderiam assumir o controle da máquina. O modo de privilégio da CPU é a fronteira de hardware que torna ambos possíveis.

Separe processamento de controle privilegiado

No modo usuário, as instruções podem acessar a memória permitida ao processo e realizar processamento comum. Elas não podem alterar livremente a configuração de hardware que protege outros programas. No modo núcleo, também chamado de modo supervisor ou privilegiado, o SO pode realizar as operações protegidas necessárias para gerenciar a máquina.

Operações privilegiadas incluem programar o temporizador, desabilitar interrupções, instalar tratadores de interrupção, alterar registradores de controle de tradução de memória, controlar diretamente dispositivos protegidos e parar a CPU. Se qualquer aplicação pudesse desabilitar o temporizador, turnos curtos de CPU dependeriam da cooperação dessa aplicação.

PlataformaModo da aplicaçãoModo do núcleo
x86-64 no Linux ou WindowsAnel 3Anel 0
AArch64EL0EL1

O AArch64 também fornece EL2 para hipervisores e EL3 para firmware seguro. Esses nomes identificam níveis de privilégio do hardware, não diferentes tipos de contas de usuário.

Entre em um endereço controlado

O código de usuário não pode elevar seu próprio privilégio e escolher qualquer endereço do núcleo para executar. A entrada ocorre por uma chamada de sistema, uma exceção causada por instrução ou uma interrupção de hardware. A CPU transfere o controle para um ponto de entrada configurado pelo SO. O código de retorno do núcleo restaura o estado de usuário salvo e reduz o nível de privilégio para retomar o código da aplicação.

O núcleo também protege seus dados com permissões de memória. Um processo pode ter endereços do núcleo em sua organização virtual e ainda assim não ter permissão em modo usuário para acessá-los. Conhecer um endereço não concede acesso aos bytes nele armazenados.

Considere uma aplicação no Linux x86-64 executando hlt, uma instrução destinada a parar uma CPU até que ocorra um evento. Em modo usuário, a instrução causa uma exceção. O Linux normalmente envia SIGSEGV, cuja ação padrão encerra esse processo. A aplicação não para todo o computador; a fronteira de proteção transforma a tentativa de operação privilegiada em um evento tratado pelo núcleo.

Mantenha identidade e modo separados

Um processo root ou administrador continua sendo um programa em modo usuário enquanto suas próprias instruções executam. Sua identidade de usuário lhe dá permissão mais ampla para solicitar serviços. Ele ainda entra no núcleo por caminhos controlados, e o núcleo verifica a requisição.

Por exemplo, um utilitário de manutenção privilegiado pode ter permissão para abrir um arquivo protegido. O utilitário faz a solicitação por uma chamada de sistema; ele não se transforma em código do núcleo apenas porque a requisição é permitida. Por outro lado, quando o código do núcleo atende à chamada read de um usuário comum, ele executa em modo núcleo mesmo que o processo que fez a chamada não tenha identidade administrativa.

Em uma CPU lógica, isso ocorre em sequência: instruções de usuário, entrada no núcleo, trabalho do núcleo e retorno às instruções de usuário. Não é a execução simultânea de um programa e de uma segunda tarefa do SO nessa mesma CPU. CPUs lógicas separadas podem executar em modos diferentes no mesmo instante.

Principais conclusões

  • O modo usuário permite processamento comum enquanto restringe operações de controle da máquina.
  • A entrada no núcleo usa pontos de entrada configurados pelo SO.
  • Root é uma identidade de usuário; o modo núcleo é um estado de privilégio da CPU.
  • Uma instrução proibida em modo usuário gera uma exceção para que o núcleo possa responder.

4.2 Explique passo a passo como uma chamada de sistema funciona

Um programa C muitas vezes parece chamar uma função comum quando precisa de um serviço do SO. Por trás de uma função que encapsula uma chamada de sistema, uma transição controlada da CPU permite que o código do núcleo valide e execute essa requisição.

Diferencie uma chamada de biblioteca de uma requisição ao núcleo

A biblioteca C implementa strlen no espaço de usuário sem uma chamada de sistema. printf formata dados e pode armazená-los em buffer; uma chamada posterior a write transfere bytes por meio do núcleo. Uma pequena função que encapsula read organiza os argumentos conforme a convenção de chamadas de sistema da plataforma e executa a instrução de entrada.

O POSIX descreve interfaces para sistemas semelhantes ao Unix. O Linux implementa muitos serviços amplamente agrupados em controle de processos, operações de arquivo e dispositivo, informação, comunicação e proteção. Um número da chamada de sistema seleciona um serviço na tabela de despacho do núcleo. Os números são específicos da plataforma: o Linux x86-64 usa 1 para write e 39 para getpid; o AArch64 usa 64 para write.

Acompanhe uma requisição de escrita

Para write(1, "hi\n", 3) no Linux x86-64, acompanhe a requisição em ordem:

  1. A função que encapsula a chamada coloca o número de serviço 1 em rax, o descritor 1 em rdi, o endereço do buffer em rsi e a contagem 3 em rdx.
  2. syscall preserva o endereço de retorno e os indicadores em registradores, entra no modo núcleo e salta para o ponto de entrada configurado. Ela não troca de pilha automaticamente.
  3. O código de entrada do núcleo estabelece a pilha do núcleo do processo e salva o estado de usuário restante.
  4. O despacho do núcleo seleciona write; a implementação verifica o descritor, o acesso ao buffer de usuário e a requisição antes de passar os bytes ao recurso de saída.
  5. Em uma escrita completa e bem-sucedida, o resultado é 3 bytes. O código de retorno do núcleo restaura o estado de usuário e retoma a função que encapsula a chamada, que retorna a quem a chamou.

O descritor 1 pode identificar um terminal, arquivo ou canal de comunicação. O número, por si só, não identifica o dispositivo de saída. Uma escrita também pode retornar uma contagem menor de bytes escritos com sucesso; quem precisa transferir todos os bytes acompanha a quantidade realmente escrita.

Verifique erros na interface

O núcleo trata ponteiros de usuário e outros argumentos como não confiáveis. Uma requisição para copiar de um buffer inacessível pode falhar com EFAULT; a cópia controlada evita conceder ao processo que faz a chamada acesso à memória do núcleo.

No Linux, um erro do núcleo é um código de erro negativo. A função usual da biblioteca C informa -1 e define errno. Por exemplo, se open("missing.txt", O_RDONLY) falhar porque o arquivo não existe, errno será ENOENT. perror("missing.txt") pode exibir “missing.txt: No such file or directory”. Examine errno após uma falha informada, não como teste principal de sucesso: uma chamada bem-sucedida não precisa limpar um erro anterior.

Considere o tempo gasto no núcleo

Uma chamada que pode terminar naquele momento pode retornar ao mesmo processo. Uma leitura bloqueante do terminal pode, em vez disso, colocar esse processo no estado de bloqueado e deixar outro executar. O evento de entrada torna o leitor pronto; depois de escalonado, ele conclui o caminho de retorno.

A entrada no núcleo acrescenta trabalho em relação a uma chamada de função normal: salvar estado, mudar de privilégio, verificar argumentos e restaurar a execução. O armazenamento em buffer pela biblioteca pode combinar pequenas saídas em menos escritas. No Linux, strace ls executa ls enquanto informa suas chamadas de sistema, argumentos e resultados.

Principais conclusões

  • Uma função de biblioteca pode fazer todo o seu trabalho no espaço de usuário ou invocar chamadas de sistema.
  • Um número de serviço seleciona uma operação do núcleo; quem faz a chamada não escolhe um endereço arbitrário do núcleo.
  • O código do núcleo valida descritores, ponteiros e outros argumentos.
  • Use o valor de retorno para detectar falha e depois examine errno.
  • Uma chamada de sistema bloqueante pode causar uma troca de contexto; a entrada no núcleo, por si só, não necessariamente causa.
  • Os números de chamadas de sistema e as convenções de registradores dependem do SO e da arquitetura.

4.3 Diferencie armadilhas, exceções e interrupções

A entrada no núcleo pode significar uma requisição de serviço, um problema de instrução ou um evento de dispositivo. Classificar a causa ajuda você a prever o que o SO fará e se a mesma instrução deve executar novamente.

Pergunte o que causou o evento
EventoCausaMomento em relação à instrução atual
Armadilha de chamada de sistemaO programa solicita deliberadamente um serviçoSíncrono
Exceção ou falhaA instrução atual precisa de tratamento especialSíncrono
Interrupção de hardwareUm temporizador ou dispositivo sinaliza à CPUAssíncrono

Síncrono significa relacionado à instrução em execução. Isso não significa que a mesma entrada cause exatamente as mesmas falhas em toda execução. Uma leitura pode causar uma falta quando sua página está ausente e ser bem-sucedida mais tarde, quando a página estiver residente. Assíncrono significa que a causa do evento é independente da instrução atual, como um disco concluindo um trabalho anterior.

Manuais de CPU usam esses termos de maneiras diferentes. Acompanhe a causa: requisição deliberada de serviço, evento causado por instrução ou notificação de hardware.

Encaminhe ao tratador correto

Na inicialização, o SO instala endereços protegidos de tratadores. Uma tabela de armadilhas ou interrupções mapeia tipos de evento para rotinas do núcleo. No x86-64, a tabela de descritores de interrupção (IDT) atende interrupções e exceções; syscall usa um endereço de entrada em um registrador especial do processador. Apenas código privilegiado pode configurar esses destinos.

Em uma interrupção a partir do modo usuário, o hardware x86-64 salva um quadro de retorno em uma pilha do núcleo. O código de entrada salva registradores adicionais antes de fazer o trabalho. O caminho de syscall preserva seu estado inicial de retorno em registradores e depende do código de entrada para estabelecer a pilha do núcleo. Tratar todos os caminhos de entrada como ações idênticas do hardware oculta uma diferença importante de implementação.

Escolha o resultado

Uma página válida ausente da memória pode ser carregada, após o que a instrução que causou a falta é executada novamente. Um acesso à memória que o processo não pode usar normalmente leva o Linux a enviar SIGSEGV. Portanto, uma exceção nem sempre é fatal e nem sempre é uma solicitação para pular a instrução atual.

Para uma instrução real de divisão inteira idiv no x86-64 com divisor zero, a CPU gera uma exceção de erro de divisão. O Linux normalmente entrega SIGFPE; sua mensagem conhecida menciona ponto flutuante mesmo para essa instrução inteira. Já a divisão inteira do AArch64 fornece zero para um divisor zero. A divisão por zero em C tem comportamento indefinido, então código C portátil não pode depender de nenhum dos dois resultados da máquina.

Quando uma leitura de disco termina, o tratador de interrupção registra a conclusão e torna pronto o processo que esperava. Esse processo pode executar mais tarde. Uma interrupção do temporizador dá ao SO a chance de reconsiderar quem usa a CPU naquele momento, mas o tratador também pode retornar ao mesmo processo.

Diferencie um sinal de uma interrupção

Uma interrupção de hardware notifica a CPU; um sinal notifica um processo por meio do SO. Uma exceção pode levar o núcleo a enviar um sinal, e um processo pode solicitar um sinal para outro por meio de kill. Conclusões normais de dispositivos são interrupções que não implicam erros de programa.

O código do núcleo também precisa proteger dados compartilhados enquanto eventos ocorrem. Ele pode desabilitar interrupções brevemente em uma CPU e usar travas quando necessário. Longos períodos com interrupções desabilitadas atrasam o atendimento aos dispositivos; desabilitar apenas as interrupções locais não impede que outra CPU acesse dados compartilhados.

Principais conclusões

  • Armadilhas e exceções surgem da instrução atual; interrupções de hardware têm uma causa externa.
  • Uma falha recuperável pode ser corrigida e sua instrução executada novamente.
  • Tabelas de entrada protegidas impedem que programas de usuário substituam tratadores do núcleo.
  • Um sinal tem como destino um processo; uma interrupção de hardware é destinada ao tratamento pela CPU.
  • A conclusão de uma operação de dispositivo torna pronto um processo bloqueado, e uma interrupção do temporizador não precisa mudar de processo.
5

Explique como o sistema operacional compartilha a CPU com execução direta limitada

A execução rápida e o controle do SO precisam funcionar juntos. A execução direta limitada permite que instruções comuns executem no hardware enquanto modos, pontos de entrada protegidos e um temporizador mantêm o núcleo no controle. Acompanhe o que acontece quando o SO retorna ao mesmo processo e quando retoma outro. O estado salvo e os custos de troca explicam como turnos curtos de CPU podem dar suporte ao trabalho interativo.

5.1 Explique a execução direta limitada

Executar cada instrução de aplicação em um simulador de software tornaria caro o compartilhamento da CPU. Deixar uma aplicação controlar a máquina sem limites tornaria o compartilhamento inseguro. A execução direta limitada (LDE) usa a execução comum no hardware com um caminho protegido de volta ao SO.

Resolva dois problemas diferentes de controle

Execução direta significa que as instruções comuns de um processo executam na própria CPU. O SO não examina e aprova cada instrução aritmética. Restam dois problemas: restringir operações que poderiam danificar a máquina e recuperar o controle quando um programa não para voluntariamente.

O modo usuário resolve o problema da restrição. Armadilhas de chamadas de sistema permitem que um programa solicite serviços sem escolher código privilegiado arbitrário. Um temporizador de hardware resolve o problema do controle: ele pode interromper uma aplicação que não faz nenhuma chamada. Juntos, esses mecanismos dão suporte à virtualização da CPU com baixa sobrecarga.

Prepare antes de as aplicações executarem

Na inicialização, o núcleo configura os tratadores de eventos e inicializa o temporizador. Essas operações de configuração são privilegiadas. Programas de usuário executados depois não podem redirecionar os tratadores nem desabilitar o temporizador para evitar perder seus turnos.

Para iniciar um processo, o SO cria seu registro de gerenciamento e sua imagem de memória, prepara os argumentos de inicialização e uma pilha de usuário e estabelece os valores iniciais dos registradores. Ele prepara um contexto inicial salvo para que o caminho normal de retorno do núcleo ao usuário possa iniciar o programa. Conceitualmente, o processo começa retornando de uma armadilha que nunca acionou.

A organização detalhada do quadro salvo depende da CPU e do SO. A ideia útil de projeto é a reutilização: o mesmo mecanismo de restauração pode retomar um processo suspenso ou iniciar um processo recém-preparado.

Retorne ao núcleo voluntariamente

Durante uma chamada de sistema, o hardware preserva o estado inicial de retorno e transfere o controle. O código de entrada do núcleo salva os registradores adicionais na pilha do núcleo do processo. Depois de concluir uma requisição não bloqueante, o núcleo restaura o processo e retorna ao modo usuário. Se a requisição bloquear, outro processo pronto poderá executar primeiro.

Quando main retorna, o processamento de saída do ambiente de execução acaba solicitando ao núcleo que encerre o processo. Sua memória pode ser liberada enquanto seu registro de término permanece até que um pai o colete.

Retorne sem cooperação

Um sistema cooperativo depende de programas fazerem chamadas, cederem a CPU ou causarem exceções. Um laço infinito que não faz nada disso pode monopolizar uma CPU. O Mac OS clássico ilustra essa fraqueza.

Um projeto não cooperativo usa uma interrupção do temporizador para forçar a entrada no núcleo. Considere um modelo didático com uma CPU lógica e um temporizador periódico a cada 4 ms. O temporizador produz 1000 / 4 = 250 disparos por segundo. Um processo de usuário preso em while (1) { } é interrompido no próximo disparo. O núcleo pode então executar outro processo pronto; o laço não precisa primeiro chegar a uma chamada de sistema.

O disparo é uma oportunidade para agir, não uma exigência de trocar de processo. O escalonador pode deixar o processo interrompido continuar. O tratamento do temporizador e a restauração do estado salvo são mecanismos; a escolha do escalonador é uma política. O tempo real de resposta também depende do trabalho do núcleo e de quando as interrupções podem ser tratadas, então o período do temporizador, por si só, não é uma garantia de tempo de resposta da aplicação.

Principais conclusões

  • A LDE executa instruções comuns de usuário diretamente, com limites impostos pelo hardware.
  • O modo usuário restringe operações; chamadas de sistema fornecem serviços privilegiados de forma controlada.
  • Um temporizador permite que o SO recupere o controle sem cooperação do código de usuário.
  • A configuração na inicialização protege os endereços dos tratadores e a configuração do temporizador.
  • Um disparo do temporizador dá uma escolha ao escalonador; ele não exige uma troca de processo.

5.2 Acompanhe uma troca de contexto e estime seu custo

Entrar no modo núcleo não necessariamente muda o processo em execução. Uma troca de contexto ocorre quando o núcleo preserva o estado de execução de um processo e retoma o de outro. Isso exige tanto a preservação do estado de usuário quanto uma mudança no próprio contexto de execução do núcleo.

Acompanhe a saída de A e a entrada de B

Para uma troca no x86-64 causada pelo temporizador, acompanhe as camadas:

  1. A executa instruções de usuário. O temporizador o interrompe.
  2. O hardware passa para o modo núcleo e salva o quadro de retorno da interrupção de A na pilha do núcleo de A. O código de entrada salva ali os outros registradores de usuário de A.
  3. O escalonador escolhe B. A rotina de troca salva o contexto de execução do núcleo de A e restaura o contexto do núcleo salvo de B, incluindo seu ponteiro da pilha do núcleo.
  4. O núcleo muda para o contexto do espaço de endereçamento de B conforme necessário. Agora ele executa em nome de B usando a pilha do núcleo de B.
  5. O código de saída do núcleo restaura os registradores de usuário de B. A instrução de retorno restaura o estado de retorno de B e entra no modo usuário. B retoma de onde havia parado.

O primeiro salvamento preserva a execução de usuário interrompida de A. O segundo preserva o ponto em que o núcleo estava ao tratar A. Eles não são duas cópias completas e idênticas do mesmo conjunto de registradores. Um PCB ou uma estrutura de contexto associada e a pilha do núcleo de cada processo trabalham juntos para preservar o estado.

Identifique o evento e a decisão

Uma chamada bloqueante, o término de um processo ou uma decisão após o temporizador podem levar a uma troca. Uma interrupção de dispositivo pode tornar outro processo pronto, e o escalonamento pode então selecioná-lo. Todo o mecanismo de troca executa no núcleo; uma aplicação não salva seus próprios registradores para se oferecer à interrupção.

SituaçãoTransição de modo?Mudança de processo?
Um serviço não bloqueante retorna a quem o chamouUsuário → núcleo → usuárioNão
O temporizador é tratado e o mesmo processo é retomadoUsuário → núcleo → usuárioNão
A bloqueia e B é retomadoEntrada no núcleo, depois caminho de retorno de BSim

Uma troca de contexto preserva o estado de execução necessário para continuar. O tempo passa e o mundo externo pode mudar: deslocamentos compartilhados em arquivos e entradas externas não ficam congelados enquanto um processo está pausado.

Considere os custos diretos e indiretos

O trabalho direto inclui executar o escalonador, salvar e restaurar registradores e selecionar um espaço de endereçamento. Os caches de tradução podem precisar de ajustes; traduções identificadas podem evitar o descarte de todas as entradas antigas. Seguem-se custos indiretos quando B precisa preencher novamente os caches da CPU ou as traduções de endereços em cache substituídas por A. O custo depende do hardware e da carga de trabalho, então use valores medidos ou informados ao calcular a sobrecarga.

Suponha que uma troca, incluindo seus efeitos relevantes, custe 5 µs e que turnos úteis de CPU durem 10 ms. Converta 10 ms em 10.000 µs. A parcela gasta em trocas é 5 / (10.000 + 5), cerca de 0,05%. Para turnos úteis de 1 ms, é 5 / 1005, cerca de 0,50%; em 0,1 ms, é 5 / 105, cerca de 4,8%.

O denominador inclui o trabalho útil mais o tempo de troca em um ciclo repetido. Um turno menor pode melhorar as oportunidades para trabalho interativo, mas gasta uma fração maior em trocas repetidas. O temporizador e o código de troca tornam possíveis turnos curtos; a política de escalonamento precisa ponderar esse custo em relação à capacidade de resposta.

Principais conclusões

  • A entrada no núcleo pode retornar ao mesmo processo sem uma troca de contexto.
  • Salvar o estado de usuário na entrada e salvar o contexto do núcleo durante uma troca têm finalidades diferentes.
  • Retomar B exige a pilha do núcleo, os registradores salvos e o contexto do espaço de endereçamento de B.
  • Os custos de troca incluem operações diretas de controle e o preenchimento posterior de caches ou traduções.
  • A parcela de sobrecarga é o custo da troca dividido pelo turno útil mais o custo da troca.
  • Turnos mais curtos aumentam a parcela gasta em trocas para um custo fixo por troca.

Exercícios resolvidos de Processos e chamadas de sistema

5 Lições Interativas

1

Veja o que o sistema operacional oferece

12 passos
Acompanhe os recursos compartilhados desde uma requisição de aplicação até os serviços do núcleo e compare os projetos que tornam possível o uso compartilhado.
  1. 1
    IntroduçãoComece com vários programas

    Imagine um notebook compilando um projeto enquanto toca música e recebe dados de rede. Cada aplicação precisa de tempo de CPU, memória e entrada e saída, mas nenhuma controla toda a máquina.

    Acompanhe quem fornece esses recursos, como várias tarefas progridem e onde os serviços privilegiados executam. A pergunta recorrente é concreta: que trabalho a aplicação solicita e que trabalho o SO precisa gerenciar?

  2. 2
    ExplicaçãoOculte os detalhes dos dispositivos

    O SO oferece abstrações: interfaces úteis que ocultam mecanismos de nível inferior. Um programa abre um arquivo e lê bytes sem implementar comandos para cada dispositivo de armazenamento.

    Uma chamada de sistema solicita um serviço do núcleo por uma interface controlada. O núcleo verifica e realiza o trabalho protegido. A aplicação ainda decide quais dados deseja; ela não recebe permissão para controlar diretamente o dispositivo.

  3. 3
    ExemploAcompanhe um arquivo de saída do compilador

    Um compilador produz bytes para um executável e solicita uma escrita. O núcleo verifica o recurso de saída e trata o trabalho de armazenamento por trás da interface de arquivo.

    Ao mesmo tempo, o SO gerencia a memória do compilador e compartilha o tempo de CPU com o reprodutor de música. Uma ação (salvar um arquivo) envolve, portanto, várias responsabilidades do SO: abstração, gerenciamento de recursos e proteção dos outros programas.

  4. 4
    Ideia-chaveDê a cada processo uma máquina utilizável

    A virtualização apresenta um recurso físico por meio de uma visão privada útil. Um processo pode progredir como se tivesse um processador próprio, embora suas instruções se alternem com as de outros processos.

    Sua visão da memória também é privada: o endereço que ele usa não precisa identificar os mesmos bytes físicos em outro processo. A virtualização da CPU e da memória dá aos programas modelos convenientes enquanto o SO gerencia o hardware realmente compartilhado.

  5. 5
    DesafioLocalize a parte privilegiada

    Um computador não tem ambiente gráfico instalado, mas executa serviços, armazena arquivos e compartilha tempo de CPU. Ele perdeu a parte central essencial do SO? Explique.

    ---reveal---

    Não. O núcleo continua gerenciando processos, memória e serviços de dispositivos. Um ambiente gráfico é um programa de usuário sobre esses serviços. Sua ausência muda a interface que uma pessoa vê, não a necessidade do trabalho de gerenciamento de recursos do núcleo.

  6. 6
    ExplicaçãoSepare a operação da escolha

    Um mecanismo torna uma ação possível. Salvar os registradores de um processo e restaurar os de outro fornece o mecanismo para a troca.

    Uma política escolhe como usá-lo, por exemplo, decidindo qual processo pronto recebe o próximo turno de CPU. A distinção importa quando um projeto muda: uma nova regra de escolha pode reutilizar o mesmo código confiável de troca. Mecanismos eficientes, por si só, não decidem o que é justo.

  7. 7
    ExplicaçãoPreencha uma espera que deixaria a CPU ociosa

    Um sistema em lote pode deixar a CPU ociosa enquanto sua tarefa atual espera por entrada e saída. A multiprogramação mantém outras tarefas disponíveis e executa uma delas durante essa espera.

    A tarefa que espera não processa dados e o dispositivo não ficou mais rápido. O ganho vem da sobreposição de trabalhos independentes: um processo espera enquanto outro usa a CPU. Vários programas residentes também exigem proteção para que um não possa sobrescrever a memória de outro.

  8. 8
    ExemploCalcule o ganho de utilização

    Uma tarefa processa dados por 20 ms e depois espera por entrada e saída por 80 ms. Sozinha, um ciclo de 100 ms contém apenas 20 ms de trabalho de CPU: a utilização é de 20%.

    Com uma segunda tarefa assim e sobreposição adequada, outros 20 ms de processamento podem preencher o tempo que ficaria ocioso, chegando a até 40% nesta ilustração. A melhoria usa produtivamente o tempo de espera; adicionar tarefas não pode elevar a utilização da CPU acima de 100%.

  9. 9
    ConexãoDê turnos curtos ao trabalho interativo

    O compartilhamento de tempo dá turnos curtos de CPU aos processos para que o trabalho interativo não precise esperar por uma tarefa em lote inteira. Isso é concorrência mesmo em uma CPU lógica: várias atividades progridem por intercalação.

    O paralelismo exige execução simultânea real em recursos de processamento separados. A capacidade de memória e de disco ilustra uma divisão diferente, o compartilhamento de espaço, em que diferentes usuários ocupam partes separadas ao mesmo tempo.

  10. 10
    ExplicaçãoEscolha um limite para os serviços

    Um núcleo monolítico, como o Linux, executa a maioria dos serviços em conjunto com privilégios. Chamadas diretas são eficientes, mas um driver com falha pode danificar o estado do núcleo.

    Um micronúcleo, como o seL4, deixa mais serviços em processos de usuário separados. O isolamento pode conter a falha de um serviço, ao custo de comunicação e transições de modo. O Windows NT e o XNU misturam abordagens. O limite determina quais falhas podem se espalhar e quais serviços podem ser reiniciados.

  11. 11
    DesafioConecte compartilhamento e proteção

    Um sistema com uma CPU executa várias tarefas e passa para outra sempre que uma espera pela entrada e saída de disco. Uma proposta permitiria que toda tarefa sobrescrevesse qualquer endereço de memória. Explique o benefício para a utilização e a falha dessa proposta.

    ---reveal---

    A multiprogramação preenche as esperas por entrada e saída com o processamento de outra tarefa. Escritas irrestritas permitiriam que uma tarefa danificasse outra ou o SO. Sobrepor trabalho melhora o uso dos recursos, mas o uso compartilhado ainda precisa de proteção de memória e de privilégios que seja efetivamente aplicada.

  12. 12
    RecapitulaçãoAnalise um projeto em três perguntas

    Para um projeto de SO, pergunte qual recurso é compartilhado, qual interface um programa vê e qual limite protege os outros trabalhos.

    Essas perguntas conectam a virtualização da CPU aos turnos de processos, os arquivos aos detalhes ocultos dos dispositivos e a organização do núcleo ao isolamento de falhas. Mecanismos realizam operações protegidas; políticas decidem como distribuir seu uso. A concorrência pode surgir da intercalação em uma CPU.

2

Acompanhe um processo no trabalho de CPU e de entrada e saída

13 passos
Leia um registro de processo e depois construa uma linha do tempo que separe a execução, a espera por uma CPU e a espera por um evento.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoAcompanhe uma instância ativa
  2. 2
    ExplicaçãoTransforme um arquivo em um processo
  3. 3
    ExplicaçãoPreserve a próxima instrução
  4. 4
    ExemploLeia uma listagem de processos
  5. 5
    DesafioInterprete endereços iguais
  6. 6
    DicaInterprete descritores como recursos herdados
  7. 7
    ExplicaçãoPergunte o que falta
  8. 8
    ExemploComece a linha do tempo
  9. 9
    ExemploConclua a linha do tempo
  10. 10
    DesafioAltere a decisão sobre o processo pronto
  11. 11
    ConexãoReconheça nomes reais de estados
  12. 12
    DesafioRelacione registros à capacidade da CPU
  13. 13
    RecapitulaçãoInterprete o estado como aptidão
3

Construa um comando com chamadas de processo

23 passos
Acompanhe um filho desde fork até a substituição de programa, a coleta de resultado, o redirecionamento e uma sequência de comandos conectados.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoDê uma função a cada chamada
  2. 2
    ExplicaçãoDivida uma execução em duas
  3. 3
    ExemploAcompanhe um valor privado
  4. 4
    ExemploConte o ramo, não as chamadas no código-fonte
  5. 5
    DesafioConte os caminhos que imprimem
  6. 6
    DicaVerifique o que foi copiado
  7. 7
    ExplicaçãoSubstitua o programa do filho
  8. 8
    ExemploPasse strings de argumentos separadas
  9. 9
    DesafioPreveja uma substituição bem-sucedida
  10. 10
    Ideia-chaveUse o intervalo antes de exec
  11. 11
    ExplicaçãoColete o resultado de um filho
  12. 12
    ExemploDecodifique o término normal
  13. 13
    ExplicaçãoNão deixe resultados sem coleta
  14. 14
    DesafioTrate a interrupção corretamente
  15. 15
    DicaEscolha uma solicitação de término
  16. 16
    ExplicaçãoRetorne a um prompt em primeiro plano
  17. 17
    ExemploRedirecione a saída antes da substituição
  18. 18
    DesafioPreserve o destino da saída
  19. 19
    ExemploConecte as duas extremidades do canal
  20. 20
    Ideia-chaveInicie antes de esperar
  21. 21
    ConexãoAcompanhe o trabalho em segundo plano
  22. 22
    DesafioAcompanhe uma sequência de comandos sem saída
  23. 23
    RecapitulaçãoAcompanhe identidade, imagem e resultado
4

Acompanhe uma requisição através da fronteira de privilégios

15 passos
Acompanhe uma requisição de serviço até o núcleo e depois compare-a com falhas de instrução e eventos externos de dispositivos.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoPergunte por que houve entrada no núcleo
  2. 2
    ExplicaçãoLimite o que as aplicações podem controlar
  3. 3
    ExemploSepare identidade de privilégio
  4. 4
    DesafioPreveja o resultado de uma instrução proibida
  5. 5
    ExplicaçãoEncontre a chamada de sistema efetiva
  6. 6
    ExemploPrepare uma escrita
  7. 7
    ExemploEntre e valide
  8. 8
    ExemploInterprete o retorno
  9. 9
    DesafioAcompanhe uma leitura bloqueada
  10. 10
    ExplicaçãoClassifique pela causa
  11. 11
    ExemploCorrija ou encerre
  12. 12
    DicaMantenha os sinais na camada de processo
  13. 13
    DesafioClassifique a conclusão de uma operação de dispositivo
  14. 14
    DesafioSepare dois tipos de entrada
  15. 15
    RecapitulaçãoAcompanhe causa, verificações e resultado
5

Mantenha o controle enquanto programas executam diretamente

15 passos
Acompanhe o controle proporcionado pelo temporizador até uma troca entre processos e depois estime a fração de tempo consumida pelas trocas.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoMantenha velocidade e controle juntos
  2. 2
    ExplicaçãoResolva dois problemas de controle
  3. 3
    ExemploPrepare a máquina e o processo
  4. 4
    ConexãoVeja a fraqueza da abordagem cooperativa
  5. 5
    ExemploInterrompa um laço infinito
  6. 6
    DesafioIdentifique o mecanismo ausente
  7. 7
    ExplicaçãoSalve o caminho de usuário interrompido
  8. 8
    ExemploSalve também o caminho do núcleo
  9. 9
    ExemploRetorne pelo estado salvo de B
  10. 10
    DesafioIdentifique uma transição apenas de modo
  11. 11
    ExplicaçãoConte os custos após uma troca
  12. 12
    ExemploMonte o denominador do ciclo completo
  13. 13
    DesafioEstime uma mudança na duração do turno
  14. 14
    DesafioRelacione uma chamada bloqueada à retomada
  15. 15
    RecapitulaçãoAcompanhe a quem pertence o caminho de retorno

Questões de Processos e chamadas de sistema

50 Questões de Prática em 5 conjuntos

Conjunto 1: Recursos, processos e proteção10 Questões de Prática

Questão 1 de 10

Um programa escreve um arquivo sem saber se seus dados vão para um SSD ou uma unidade de rede. Qual ideia do SO torna isso possível?
  1. Compartilhamento de tempo do dispositivo de armazenamento
  2. Uma abstração para operações de arquivo
  3. Execução paralela de requisições ao dispositivo
  4. Uma política de escalonamento para o programa
Mostrar respostaOcultar resposta
Resposta: B
Uma interface de arquivo oculta o trabalho específico do dispositivo por trás de operações que o programa pode solicitar. Isso é uma abstração: o programa lida com bytes e arquivos enquanto o SO trata os detalhes do dispositivo. O compartilhamento de tempo diz respeito a quando um recurso é usado, não à interface que oculta esses detalhes.

Questão 2 de 10

Um SO muda qual processo pronto recebe o próximo turno, mas mantém inalterada sua rotina de salvamento de registradores. O que mudou?
  1. A organização do espaço de endereçamento
  2. O mecanismo de privilégio
  3. O formato dos registradores
  4. A política de escalonamento
Mostrar respostaOcultar resposta
Resposta: D
A regra de seleção é uma política; a rotina inalterada de salvamento e restauração é um mecanismo. Um mecanismo de troca pode dar suporte a várias formas de escolher o próximo processo. Mudar a ordem dos turnos de CPU não altera, por si só, como a máquina preserva o estado dos registradores.

Questão 3 de 10

Uma tarefa processa dados repetidamente por 15 ms e depois espera por entrada e saída por 45 ms. Qual utilização da CPU ela alcança quando é a única tarefa e a sobrecarga de troca de contexto é ignorada?
  1. 25%
  2. 33,3%
  3. 75%
  4. 15%
Mostrar respostaOcultar resposta
Resposta: A
Cada ciclo dura 15 + 45 = 60 ms, e apenas 15 ms contêm processamento. Portanto, a utilização da CPU é 15 / 60 = 25%. Dividir 15 pelos 45 ms de espera resulta em 33,3%, mas a utilização usa todo o ciclo decorrido, incluindo o processamento e a espera.

Questão 4 de 10

Dois programas se alternam repetidamente em uma CPU lógica antes que qualquer um deles termine. Como sua execução é descrita?
  1. Paralela, com execução simultânea
  2. Sequencial, com cada um terminando antes do outro
  3. Concorrente, com execução intercalada
  4. Com compartilhamento de espaço, com divisão permanente da CPU
Mostrar respostaOcultar resposta
Resposta: C
Ambas as atividades estão em andamento durante o mesmo intervalo, então sua execução é concorrente. Uma única CPU lógica intercala suas instruções. A execução paralela exigiria recursos de processamento simultâneos; alternar turnos rapidamente não faz as instruções executarem no mesmo instante.

Questão 5 de 10

Uma listagem de processos mostra PID 9641, PPID 9600 e o nome de comando “sort”. Qual processo é seu pai?
  1. O processo cujo PID é 9600
  2. O processo cujo PID é 9641
  3. Todo processo chamado “sort”
  4. O processo criado após 9641
Mostrar respostaOcultar resposta
Resposta: A
O PPID identifica o processo pai, então o pai tem PID 9600. O PID 9641 identifica o próprio processo sort. Um nome de comando pode ser compartilhado por muitas instâncias de processo e não substitui a relação numérica com o pai registrada pelo núcleo.

Questão 6 de 10

Duas cópias de um programa iniciadas separadamente imprimem o mesmo endereço virtual para uma variável privada. Uma delas muda seu valor. O que acontece com a outra cópia?
  1. Ela vê a mesma escrita porque os endereços virtuais são iguais.
  2. Ela vê a escrita apenas depois que ambos os processos entram novamente no núcleo.
  3. Ela mantém seu valor apenas até que seu próximo contexto salvo seja restaurado.
  4. Ela mantém seu valor porque seu espaço de endereçamento é separado.
Mostrar respostaOcultar resposta
Resposta: D
Cada processo tem seu próprio espaço de endereçamento privado, então números de endereço virtual iguais podem identificar dados diferentes. Uma escrita em uma variável privada não atualiza a variável do outro processo. Endereços impressos iguais, por si só, não estabelecem um mapeamento de memória gravável compartilhado.

Questão 7 de 10

Um processo pode continuar processando dados, mas o temporizador encerrou seu turno de CPU. Qual é seu estado básico agora?
  1. Bloqueado, esperando a conclusão do temporizador
  2. Pronto, esperando outro turno de CPU
  3. Parado, esperando um sinal de retomada
  4. Zumbi, esperando que o pai colete seu estado
Mostrar respostaOcultar resposta
Resposta: B
O processo tem trabalho disponível e precisa apenas de uma CPU, então está pronto. Perder um turno não o torna bloqueado, pois nenhuma operação de entrada e saída nem outro evento necessário impede seu progresso. Os estados parado e zumbi descrevem situações diferentes: execução suspensa e execução concluída aguardando coleta.

Questão 8 de 10

Em uma CPU lógica, M está em execução enquanto N está bloqueado por uma leitura. O dispositivo conclui a leitura de N, e o núcleo retorna a M. Quais são os dois estados básicos imediatamente depois?
  1. M pronto; N em execução
  2. M em execução; N bloqueado
  3. M em execução; N pronto
  4. M bloqueado; N pronto
Mostrar respostaOcultar resposta
Resposta: C
A leitura concluída elimina o motivo do bloqueio de N, então N fica pronto. M continua executando porque o núcleo retornou a ele. A conclusão do dispositivo torna N apto a executar, mas não exige que o escalonador dê a CPU a N imediatamente; tratar a conclusão como uma troca automática ignora essa decisão.

Questão 9 de 10

Um utilitário de manutenção executa com identidade root e solicita uma operação protegida de arquivo. Em que modo suas próprias instruções de aplicação executam?
  1. Modo núcleo, pois root ignora as verificações de entrada
  2. Modo supervisor até que o arquivo seja fechado
  3. Modo usuário apenas quando o arquivo solicitado é público
  4. Modo usuário, com permissões verificadas pelo núcleo
Mostrar respostaOcultar resposta
Resposta: D
Root é uma identidade com permissões mais amplas para serviços, não um modo de execução da CPU. As instruções de aplicação do utilitário ainda executam em modo usuário e atravessam um caminho de entrada controlado para o trabalho do núcleo. Ter permissão para abrir um arquivo protegido não transforma o próprio utilitário em código do núcleo.

Questão 10 de 10

Um novo SO impõe restrições de modo usuário e entrada protegida por chamada de sistema, mas recupera a CPU apenas quando um programa faz uma chamada ou causa uma falha. Um programa de usuário entra em um laço aritmético infinito. Por que esse projeto pode falhar em compartilhar uma CPU?
  1. O laço pode evitar todos os caminhos configurados de volta ao SO.
  2. O laço cede a CPU automaticamente sempre que segue sua próxima ramificação.
  3. O núcleo executa ao lado do laço na mesma CPU lógica.
  4. A proteção do modo usuário força a escolha de um processo pronto após cada instrução.
Mostrar respostaOcultar resposta
Resposta: A
O programa pode continuar executando instruções permitidas sem fazer uma chamada nem causar uma exceção. Assim, o SO não tem um evento independente que force a entrada no núcleo para que ele possa executar outro processo. As restrições de privilégio protegem operações, mas uma interrupção do temporizador é necessária para recuperar o controle sem cooperação.
Conjunto 2: Crie, substitua e colete o resultado de um processo10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Um pai recebe 7421 de um fork bem-sucedido. Que valor esse mesmo fork retorna no filho?
  1. 7421
  2. -1
  3. 0
  4. O PID do pai
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Todo fork é bem-sucedido. Quantos processos alcançam puts("done") neste código?

if (fork() == 0) {
    fork();
}
puts("done");
  1. 2
  2. 3
  3. 4
  4. 5
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Um programa com uma única thread deixa “ready” pendente em um fluxo de saída totalmente armazenado em buffer e depois faz fork com sucesso. Ambos os processos chamam exit sem produzir mais saída. O que o destino pode receber?
  1. Nenhuma cópia, pois fork limpa a saída pendente
  2. Uma cópia, pois o fluxo tem um descritor
  3. Uma cópia apenas se o filho terminar primeiro
  4. Duas cópias, pois cada processo descarrega um buffer copiado
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Um processo com PID 8502 executa um novo programa com exec com sucesso. Qual é o identificador de processo do novo programa?
  1. 8502
  2. 8503
  3. 0
  4. O PID do pai
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Um programa está instalado em /opt/course/bin/checker, e PATH inclui /opt/course/bin. Não há arquivo chamado checker no diretório atual. Por que execv("checker", args) não consegue localizá-lo?
  1. Execv pesquisa no PATH apenas quando args[0] é um caminho absoluto.
  2. Execv pesquisa apenas no diretório do executável que faz a chamada.
  3. Execv trata o nome como um caminho e não pesquisa no PATH.
  4. Execv pesquisa no PATH apenas depois de falhar uma vez no diretório atual.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um filho chama exit(515). Após uma espera bem-sucedida e um teste WIFEXITED verdadeiro, o que WEXITSTATUS informa?
  1. 515
  2. 512
  3. 255
  4. 3
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um pai tem um filho em execução. Sua chamada waitpid(child, &status, WNOHANG) retorna 0. O que aconteceu?
  1. O filho terminou com sucesso.
  2. Nenhum resultado do filho está pronto, então o pai continua.
  3. O filho não pertence mais ao pai.
  4. O pai coletou o estado de um filho com PID 0.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um filho de um servidor está no estado Z. O envio de SIGKILL deixa a entrada visível. Que ação libera o registro de resultado mantido para esse filho?
  1. O pai o coleta com uma operação de espera.
  2. O filho trata SIGTERM e chama exit.
  3. O escalonador concede ao filho outro turno de CPU.
  4. O pai envia SIGCONT e depois SIGSTOP.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um interpretador de comandos redireciona a saída padrão de um filho para um arquivo e executa um comando com exec. O comando escreve normalmente, termina com sucesso e o interpretador apresenta outro prompt. O que explica por que o comando não precisou de código de redirecionamento?
  1. O pai repassa a saída do filho depois que wait retorna.
  2. Exec interpreta o texto de redirecionamento no vetor de argumentos.
  3. A configuração de descritores do filho permanece após a substituição do programa.
  4. Exec dá ao comando uma cópia nova dos descritores do pai.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Um interpretador de comandos inicia producer | consumer. O produtor termina, mas o consumidor espera indefinidamente após ler todos os bytes disponíveis. O interpretador fechou suas cópias do canal; o consumidor ainda mantém seu descritor original herdado da extremidade de escrita. O que resolve o bloqueio?
  1. Esperar pelo consumidor antes de fechar a extremidade de leitura do interpretador.
  2. Fechar a extremidade de escrita não usada no consumidor antes de exec.
  3. Fechar a extremidade de leitura do consumidor quando o produtor terminar.
  4. Fazer o interpretador coletar o estado do produtor antes de iniciar sua leitura.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 3: Acompanhe a entrada no núcleo e as trocas de CPU10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Por que a alteração da tabela de tratadores de interrupção precisa ser privilegiada?
  1. Para impedir que código de usuário redirecione a entrada protegida no núcleo
  2. Para garantir que cada programa use um número diferente de chamada de sistema
  3. Para tornar todos os eventos de hardware síncronos com as instruções de usuário
  4. Para exigir um novo processo sempre que houver entrada em um tratador
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Uma função C formata várias mensagens curtas em um buffer de saída e depois as transfere com uma escrita. Que custo esse agrupamento reduz?
  1. O número de caracteres que a aplicação precisa formatar
  2. A quantidade de memória necessária para o arquivo executável
  3. O número de processos necessários para armazenar as mensagens
  4. O trabalho repetido de entrada no núcleo e controle de chamadas de sistema
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Uma função C que encapsula open retorna -1 e errno é ENOENT. O que o processo que fez a chamada deve concluir?
  1. O arquivo foi aberto com sucesso com um descritor incomum.
  2. A requisição falhou porque o arquivo indicado não foi encontrado.
  3. A requisição criou um filho que ainda não terminou.
  4. O arquivo foi aberto, mas a contagem de bytes foi truncada.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Um controlador de rede informa que um pacote chegou enquanto um processo somava números inteiros. Que tipo de evento de entrada no núcleo é esse?
  1. Uma exceção aritmética síncrona
  2. Uma armadilha deliberada de chamada de sistema
  3. Uma interrupção de hardware assíncrona
  4. Um sinal de software enviado pela instrução de soma
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma instrução acessa uma página válida que não está residente no momento. O núcleo carrega a página com sucesso. Como a execução pode continuar?
  1. Pular a instrução porque seu resultado já foi armazenado.
  2. Encerrar o processo porque toda exceção é fatal.
  3. Criar um novo processo para executar as instruções restantes.
  4. Executar novamente a instrução com a página necessária disponível.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Em um modelo de temporizador periódico com um disparo a cada 5 ms, quantos disparos do temporizador ocorrem por segundo?
  1. 200
  2. 50
  3. 500
  4. 5000
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um processo entra no núcleo para um serviço não bloqueante. O tratador termina e retorna ao mesmo processo. O que ocorreu?
  1. Uma troca de contexto sem mudança de privilégio
  2. Uma transição de modo sem troca de contexto de processo
  3. Uma troca de processo que preservou o PID original
  4. Uma substituição do programa do processo por exec
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Em uma CPU lógica, A faz uma escrita não bloqueante que retorna a A; depois, um temporizador entra no núcleo e retorna a A. Em seguida, A faz uma leitura bloqueante e B é retomado; mais tarde, B termina por uma chamada de sistema e C é retomado. Nenhuma outra entrada no núcleo ocorre. Quantas entradas do modo usuário no núcleo e quantas trocas de contexto de processo ocorrem?
  1. 4 entradas no núcleo; 4 trocas de processo
  2. 3 entradas no núcleo; 2 trocas de processo
  3. 4 entradas no núcleo; 2 trocas de processo
  4. 4 entradas no núcleo; 3 trocas de processo
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Uma aplicação coloca um endereço de memória protegida do núcleo em um argumento de buffer de escrita e faz uma chamada de sistema. Por que a entrada controlada, por si só, não autoriza a leitura solicitada desse endereço?
  1. O núcleo ainda precisa validar o acesso do processo ao buffer.
  2. O número de serviço, por si só, autoriza todos os endereços de buffer fornecidos.
  3. A configuração dos registradores pela função de biblioteca já prova que o ponteiro é acessível.
  4. A troca de modo torna a memória protegida parte do buffer de usuário.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Um ciclo repetido de CPU fornece 2 ms de execução útil seguidos por um custo de troca de contexto de 8 µs. Aproximadamente que fração do ciclo é gasta em trocas?
  1. 4,00%
  2. 0,004%
  3. 0,80%
  4. 0,398%
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 4: Acompanhe o comportamento de processos e o compartilhamento de recursos10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Uma CPU lógica executa J por 3 ms. J então espera por entrada e saída por 5 ms e precisa de mais 2 ms de tempo de CPU. K está pronto inicialmente e precisa de 6 ms de tempo de CPU. K executa sempre que J bloqueia e continua até terminar; o custo de troca é ignorado. Quando ambos terminam?
  1. 10 ms
  2. 11 ms
  3. 14 ms
  4. 16 ms
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Qual valor armazenado permite que o núcleo identifique a próxima instrução ao retomar um contexto de execução pausado?
  1. O diretório atual
  2. O deslocamento no arquivo
  3. O PID do pai
  4. O contador de programa
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Um processo com uma única thread tem x privado igual a 6. Todo fork é bem-sucedido. Todos os processos alcançam puts. Quantas linhas contêm “9”?

pid_t p = fork();
if (p == 0) x += 3;
fork();
if (x == 9) puts("9");
  1. 2
  2. 1
  3. 3
  4. 4
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Antes de um fork bem-sucedido, um descritor de arquivo se refere ao deslocamento 0 em um arquivo aberto que contém “ABCDEFGH”. O pai lê exatamente três bytes, e depois o filho lê exatamente dois bytes pelo descritor herdado. Nenhuma outra operação ocorre. O que o filho lê?
  1. “AB”
  2. “BC”
  3. “DE”
  4. “FG”
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Um filho chama execvp para um programa que não existe. Sua próxima instrução imprime um erro. Por que essa instrução pode executar?
  1. Exec inicia o novo programa ao lado do caminho da chamada anterior.
  2. Toda chamada exec bem-sucedida retorna uma vez antes de substituir a memória.
  3. O pai executa automaticamente todas as instruções de erro do filho.
  4. A chamada exec malsucedida retorna sem substituir o programa anterior.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um pai com dois filhos ativos chama wait. Um tratador de sinal interrompe a espera, produzindo -1 e errno EINTR. O que o pai deve fazer para continuar coletando os estados dos filhos?
  1. Parar porque os estados de ambos os filhos foram coletados.
  2. Repetir a espera em vez de tratar EINTR como conclusão.
  3. Decodificar o estado inalterado com WEXITSTATUS.
  4. Enviar SIGKILL à sua própria entrada na tabela de processos.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um interpretador de comandos chama chdir apenas em um filho criado com fork para implementar cd, espera por esse filho e depois inicia outro comando. Por que o próximo comando começa no diretório antigo?
  1. Wait sempre redefine o diretório do pai para o valor da inicialização.
  2. Exec ignora toda configuração de diretório atual herdada.
  3. A mudança de diretório do filho não alterou o interpretador pai.
  4. Chdir exige que o próprio interpretador execute em modo núcleo.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um interpretador de comandos cria um canal de comunicação, cria com fork apenas o produtor e espera por ele antes de criar o consumidor. O produtor escreve mais do que o canal pode conter. Por que a sequência de inicialização pode travar?
  1. O produtor bloqueia com o canal cheio enquanto nenhum consumidor pode esvaziá-lo.
  2. A cópia da extremidade de leitura do pai consome bytes apenas depois que wait retorna.
  3. O produtor alcança o fim de arquivo quando o canal fica cheio pela primeira vez.
  4. O produtor não pode fechar sua saída até que o estado do pai seja coletado.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um serviço de dispositivo executa em um servidor separado em modo usuário e falha. O SO reinicia esse serviço enquanto aplicações sem relação com ele continuam. Que característica do projeto do núcleo ajuda a explicar esse resultado?
  1. Todos os serviços compartilham o mesmo espaço de endereçamento privilegiado.
  2. Toda requisição de dispositivo ignora as regras de proteção do núcleo.
  3. O escalonador garante que toda aplicação execute em paralelo.
  4. O serviço está isolado da pequena parte central privilegiada.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

O mesmo programa e a mesma entrada causam uma falta de página em uma execução, mas não em outra, porque a página necessária já está residente. Como a falta da primeira execução deve ser classificada?
  1. Assíncrona, porque as duas execuções foram diferentes
  2. Síncrona, porque a instrução de memória a causou
  3. Uma interrupção de hardware causada apenas pelo tempo decorrido
  4. Um sinal criado diretamente pelo buffer da biblioteca C
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 5: Diagnostique interações entre processos e o núcleo10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

O resultado de um filho satisfaz WIFSIGNALED após waitpid ser bem-sucedido. Qual macro fornece o número do sinal que o encerrou?
  1. WEXITSTATUS(status)
  2. WIFEXITED(status)
  3. WTERMSIG(status)
  4. WNOHANG
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Uma função que encapsula write informa que 5 bytes foram escritos de um buffer solicitado de 9 bytes. Quantos bytes solicitados ainda precisam ser transferidos?
  1. 4 bytes
  2. 5 bytes
  3. 9 bytes
  4. 14 bytes
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Um processo lê um endereço inválido no Linux, e o núcleo entrega SIGSEGV. Qual sequência diferencia os dois eventos?
  1. Um sinal ao processo é seguido por uma exceção de memória da CPU.
  2. Uma interrupção assíncrona de dispositivo é entregue diretamente como SIGSEGV.
  3. Uma armadilha deliberada de chamada de sistema solicita a entrega de SIGSEGV.
  4. Uma exceção de instrução é seguida por um sinal do SO ao processo.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Um SO impõe restrições de modo usuário e recebe interrupções do temporizador. Seu tratador do temporizador salva os registradores de usuário, mas sempre retorna ao mesmo processo. Uma correção proposta altera apenas a frequência dos disparos do temporizador. Outros processos permanecem prontos. Por que essa correção ainda pode falhar em dar turnos de CPU a eles?
  1. A entrada pelo temporizador, por si só, garante turnos justos antes do retorno do tratador.
  2. Recuperar o controle fornece um mecanismo, não uma política justa de seleção.
  3. Os limites do modo usuário fazem todo processo pronto executar no próximo disparo.
  4. Salvar os registradores de usuário avança automaticamente para o próximo processo pronto.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma carga de trabalho repete turnos úteis de CPU de 600 µs com custo de troca de 6 µs. Ela passa a usar turnos de 1200 µs com o mesmo custo. Como muda a parcela gasta em trocas?
  1. Ela sobe de cerca de 0,50% para cerca de 0,99%.
  2. Ela cai de cerca de 0,99% para cerca de 0,50%.
  3. Ela permanece em cerca de 1% porque o custo de troca não mudou.
  4. Ela cai de cerca de 9,9% para cerca de 5,0%.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um interpretador de comandos abre um arquivo para redirecionamento de entrada e recebe o descritor 0 porque a entrada padrão estava fechada. Ele chama dup2(fd, 0), fecha fd incondicionalmente e executa o comando com exec. Por que a entrada padrão do comando pode ficar fechada?
  1. Fechar fd fecha apenas uma entrada antiga; o descritor 0 permanece como cópia distinta.
  2. Exec fecha o descritor 0 porque dup2 definiu seu indicador de fechamento na execução.
  3. dup2 rejeita números de descritor iguais e remove a entrada.
  4. Fd já é igual a 0, então fechá-lo fecha a entrada desejada.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um programa tem x privado igual a 2. Todo fork é bem-sucedido. Quantos processos imprimem “hit”?

pid_t p = fork();
if (p > 0) {
    x = 7;
    fork();
}
if (x == 7) puts("hit");
  1. 2
  2. 3
  3. 4
  4. 1
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um interpretador de comandos inicia dois filhos. Um termina normalmente com código de saída 0; o outro é encerrado por um sinal. O interpretador precisa informar os dois resultados. Que abordagem de tratamento dos resultados é necessária?
  1. Coletar os dois filhos uma vez e depois decodificar ambos como términos normais.
  2. Coletar um resultado e decodificá-lo uma vez como saída e outra como sinal.
  3. Coletar os dois resultados e selecionar macros de decodificação separadamente para cada um.
  4. Coletar os dois resultados, mas decodificar seus inteiros brutos como códigos de saída.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

A bloqueia em uma leitura do terminal e B executa. Depois que a entrada torna A pronto, o escalonador seleciona A. Uma troca proposta restaura o contador de programa de usuário de A, mas mantém a pilha do núcleo e o contexto do espaço de endereçamento de B. Por que isso não pode retomar corretamente a leitura pendente de A?
  1. O quadro de retorno ao usuário de A restaura automaticamente o restante de seu caminho no núcleo.
  2. Alterar o contador de programa de usuário também seleciona a pilha do núcleo correspondente.
  3. A leitura pode terminar na pilha do núcleo de B usando apenas os registradores de usuário de A.
  4. É preciso selecionar o próprio contexto do núcleo, a pilha e o contexto do espaço de endereçamento de A.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Um programa de usuário conhece o endereço de uma rotina do núcleo e o número de uma chamada de sistema válida. Por que ele pode solicitar o serviço, mas não pode simplesmente chamar esse endereço privilegiado como uma função comum?
  1. Uma armadilha entra em um caminho configurado do núcleo que valida e encaminha a requisição.
  2. Uma chamada direta ganha privilégio sempre que seu destino corresponde a um número de serviço válido.
  3. Um número de serviço concede acesso ao núcleo antes da verificação dos argumentos.
  4. Uma chamada direta e uma armadilha herdam privilégio apenas do endereço de destino.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Travou em uma questão sua? Ajuda com Tarefas

Termos-Chave

75 Termos-Chave, cada um com sua definição

A

Abstração
Uma abstração é uma interface ou modelo útil que oculta detalhes de implementação de nível inferior enquanto preserva as operações de que os programas precisam.
A abstração de arquivo permite que um programa leia bytes sem conhecer o formato de comandos do dispositivo de armazenamento.
Anel de proteção
Um anel de proteção é um nível de privilégio do hardware x86; os sistemas mais comuns normalmente usam o anel 0 para núcleos e o anel 3 para aplicações.
A passagem do anel 3 de aplicação para o anel 0 do núcleo segue um caminho de entrada controlado configurado pelo SO.
Armadilha
Uma armadilha de chamada de sistema é uma entrada deliberada no núcleo causada por uma requisição de serviço de um programa, usando uma instrução de entrada protegida.
A requisição para escrever a saída causa uma armadilha de chamada de sistema mesmo quando não há erro no programa.

B

Bloco de controle de processo
Um bloco de controle de processo (PCB) é o registro que o núcleo mantém de um processo, incluindo sua identidade, seu estado, seus recursos e seu contexto salvo.
O núcleo consulta um bloco de controle de processo para encontrar informações de memória e o pai de um processo parado.
Buffer de saída
Um buffer de saída é uma área de armazenamento no espaço de usuário que mantém a saída formatada antes que uma biblioteca transfira esses bytes ao núcleo.
O texto pendente em um buffer de saída pode ser copiado por fork e depois descarregado uma vez por cada processo.
Buffer de tradução de endereços
Um buffer de tradução de endereços (TLB) armazena traduções de endereços em cache para que a CPU evite procurá-las repetidamente na memória.
Uma troca de processo pode exigir o preenchimento de traduções, embora entradas identificadas possam preservar traduções úteis em cache.

C

Canal de comunicação
Um canal de comunicação é um canal unidirecional de bytes gerenciado pelo núcleo, com uma extremidade de leitura e uma de escrita, que pode conectar processos cooperantes.
Um interpretador de comandos conecta a saída de um produtor a um canal de comunicação e a entrada de um consumidor à extremidade de leitura desse mesmo canal.
Chamada de sistema
Uma chamada de sistema é uma requisição controlada de um programa de usuário para um serviço do núcleo, feita por uma interface de entrada definida.
Abrir um arquivo exige uma chamada de sistema porque o código do núcleo gerencia o acesso ao recurso de arquivo aberto.
Código de saída
Um código de saída é o pequeno resultado que um processo informa ao terminar normalmente, por convenção 0 para sucesso e um valor diferente de zero para falha.
Após verificar o término normal, o pai extrai o código de saída com a macro de espera apropriada.
Coleta do estado de término
A coleta do estado de término consiste em obter o resultado de um filho encerrado com uma operação de espera para que o núcleo possa liberar seu registro de processo restante.
Uma espera bem-sucedida coleta o estado de término de um filho; um pai com vários filhos precisa continuar coletando resultados.
Comando interno
Um comando interno é uma operação implementada pelo próprio interpretador de comandos, permitindo que ela altere o estado persistente do próprio processo do interpretador.
O comando interno cd muda o diretório do interpretador de comandos para que comandos posteriores herdem o diretório recém-selecionado.
Compartilhamento de espaço
O compartilhamento de espaço divide a capacidade de um recurso em partes ocupadas por diferentes processos ou usuários ao mesmo tempo.
Regiões de memória separadas podem permanecer atribuídas a vários processos enquanto apenas um tem a CPU.
Compartilhamento de tempo
O compartilhamento de tempo divide o acesso a um recurso em turnos sucessivos, permitindo que vários processos ou usuários o compartilhem ao longo do tempo.
O compartilhamento de tempo dá a um editor outro turno de CPU enquanto um cálculo longo ainda está em andamento.
Concorrência
Concorrência significa que várias atividades estão em andamento durante o mesmo período, inclusive quando suas instruções são intercaladas.
Dois programas são concorrentes em uma CPU lógica quando cada um recebe turnos repetidamente antes que qualquer um termine.
Contador de programa
O contador de programa é um registrador da CPU que contém o endereço da próxima instrução em um contexto de execução.
Restaurar o contador de programa permite que um processo pausado continue na instrução correta após uma troca.
Contexto do núcleo
Um contexto do núcleo é o estado salvo de registradores e pilha necessário para retomar o caminho de execução do núcleo para um processo suspenso.
Ao trocar para o processo B, a rotina de troca restaura o contexto do núcleo de B antes que o código de saída do núcleo possa restaurar os registradores de usuário de B.
Controle cooperativo
O controle cooperativo depende de programas cederem a CPU, fazerem chamadas de sistema ou causarem exceções para que o SO volte a executar na CPU.
Um laço infinito de usuário expõe a fraqueza do controle cooperativo se nunca alcançar um caminho de volta ao SO.
Cópia na escrita
A cópia na escrita compartilha inicialmente páginas protegidas da memória física e cria cópias privadas quando escritas exigem conteúdos separados.
Um filho que chama exec imediatamente pode descartar páginas herdadas sem primeiro copiar cada página.

D

Descrição de arquivo aberto
Uma descrição de arquivo aberto é o estado mantido pelo núcleo para um arquivo aberto, incluindo seu deslocamento, que pode ser compartilhado por vários descritores.
Leituras do pai e do filho por um descritor herdado avançam o deslocamento em uma mesma descrição de arquivo aberto.
Descritor de arquivo
Um descritor de arquivo é um pequeno número inteiro na tabela de descritores de um processo que se refere a um arquivo aberto ou a outro recurso de entrada e saída.
Um interpretador de comandos conecta o descritor de arquivo 1 a um arquivo antes de executar um comando cuja saída é redirecionada.

E

errno
errno é um indicador de erro da biblioteca C definido por muitas interfaces em caso de falha e examinado depois que o valor de retorno informa um erro.
Uma chamada malsucedida a open pode definir errno como ENOENT, enquanto uma chamada bem-sucedida posterior não precisa limpar esse valor anterior.
Escalonador
O escalonador é o componente do SO que escolhe qual contexto de execução apto deve receber tempo de CPU em seguida.
Após a conclusão de uma leitura de disco, o escalonador pode manter o processo atual em execução antes de selecionar o leitor.
Espaço de endereçamento
Um espaço de endereçamento é o conjunto de endereços de memória disponíveis a um processo e o código, os dados, a área de alocação dinâmica e a pilha mapeados nessa visão.
Endereços virtuais iguais em dois espaços de endereçamento privados podem se referir a valores diferentes na memória física.
Espera não bloqueante
Uma espera não bloqueante verifica se há um resultado de filho disponível e retorna sem suspender quem fez a chamada quando nenhum resultado está pronto.
Um interpretador de comandos usa uma espera não bloqueante para coletar tarefas em segundo plano concluídas enquanto mantém o prompt disponível.
Estado de bloqueado
O estado de bloqueado significa que um processo não pode continuar até que um evento ocorra, como a disponibilização de uma entrada solicitada.
Uma leitura bloqueante do terminal coloca um processo no estado de bloqueado até que uma entrada permita que a requisição prossiga.
Estado de execução
O estado de execução significa que um processo está executando instruções em uma CPU no momento, diferentemente de estar apto a executar ou esperando por um evento.
Quando um compilador pronto recebe uma CPU, ele entra no estado de execução e pode executar sua próxima instrução.
Estado de pronto
O estado de pronto significa que um processo pode executar, mas está esperando que uma CPU fique disponível ou que o escalonador o selecione.
Um temporizador pode mudar um processamento inacabado do estado de execução para o estado de pronto sem nenhuma operação de entrada e saída.
Estado retornado por wait
Um estado retornado por wait é um valor codificado que descreve como um filho terminou ou mudou de estado, interpretado por meio das macros de espera correspondentes.
Um estado bruto retornado por wait de 768 no Linux pode codificar o código de saída normal 3, então imprimi-lo diretamente fornece o código errado.
Exceção
Uma exceção é um evento decorrente da instrução atual que exige tratamento especial da CPU e do núcleo, como uma falta de página.
Um acesso proibido à memória gera uma exceção antes que a aplicação possa continuar normalmente.
exec
exec substitui a imagem do programa do processo que faz a chamada por um novo programa, preservando a identidade do processo e determinados recursos.
O filho mantém seu PID quando exec substitui o código do interpretador de comandos pelo código do comando solicitado.
Execução direta limitada
A execução direta limitada executa instruções de usuário no hardware enquanto limites de privilégio e entradas causadas pelo temporizador preservam o controle do SO.
Uma aplicação em laço executa diretamente, mas ainda não pode desabilitar o temporizador que pode interrompê-la.

F

Falta de página
Uma falta de página é um evento causado por uma instrução de acesso à memória cuja tradução ou permissões exigem tratamento especial.
Uma página válida ausente pode ser carregada após uma falta de página, permitindo que o núcleo execute novamente a instrução interrompida.
Fatia de tempo
Uma fatia de tempo é um intervalo de execução de CPU atribuído a um trabalho antes que o escalonamento possa dar um turno a outro processo apto.
Com custo de troca fixo, tornar uma fatia de tempo mais curta aumenta a fração de tempo gasta em trocas.
Fechamento na execução
O fechamento na execução é um indicador de descritor que solicita seu fechamento automático quando um processo substitui seu programa com sucesso.
Um interpretador de comandos pode impedir que um descritor interno chegue ao comando marcando-o para fechamento na execução.
Fim de arquivo
O fim de arquivo é um resultado de leitura que indica que não há mais dados disponíveis; para um canal de comunicação, ele exige um buffer vazio e nenhuma extremidade de escrita aberta.
Um leitor pode esperar em vez de receber fim de arquivo se o interpretador de comandos mantiver acidentalmente uma extremidade de escrita do canal aberta.
fork
fork é a operação de criação de processo do Unix que cria um filho e retorna ao pai e ao filho após a mesma chamada.
Após um fork bem-sucedido, o pai recebe o PID do filho e o filho recebe o valor de retorno 0.
Função de biblioteca que encapsula uma chamada de sistema
Uma função de biblioteca que encapsula uma chamada de sistema é código no espaço de usuário que apresenta uma interface chamável e organiza os argumentos e a entrada para um serviço do núcleo.
A função de biblioteca que encapsula write segue a convenção de registradores de chamadas de sistema antes de entrar no núcleo.

I

Identificador de processo
Um identificador de processo (PID) é um identificador numérico atribuído a um processo e único entre os processos ativos no mesmo espaço de nomes de PID.
Uma listagem de processos usa o PID e o PID do pai para mostrar que um comando foi iniciado por um interpretador de comandos específico.
Instrução privilegiada
Uma instrução privilegiada altera o estado protegido da máquina e não pode ser executada livremente por código comum em modo usuário.
Desabilitar interrupções é uma operação privilegiada porque, caso contrário, poderia impedir que o SO recuperasse o controle.
Interpretador de comandos
Um interpretador de comandos é um programa de usuário que interpreta comandos e usa operações de processo e descritores para iniciar e conectar outros programas.
Um interpretador de comandos em primeiro plano espera pelo processo filho de seu comando antes de exibir o próximo prompt.
Interrupção de hardware
Uma interrupção de hardware é uma notificação de dispositivo ou temporizador que faz a CPU tratar um evento independentemente da instrução atual.
Um controlador de armazenamento gera uma interrupção de hardware quando uma transferência solicitada anteriormente é concluída.
Interrupção do temporizador
Uma interrupção do temporizador é um evento de hardware de um temporizador configurado que dá ao núcleo uma oportunidade de recuperar o controle de uma CPU.
Uma interrupção do temporizador pode entrar no núcleo mesmo enquanto o processo atual executa um laço sem chamadas de sistema.

M

Mecanismo
Um mecanismo é o meio de realizar uma operação do SO, como preservar o estado de um processo e restaurar o de outro.
O mecanismo de troca de contexto dá suporte a várias regras possíveis para decidir qual processo pronto executa em seguida.
Micronúcleo
Um micronúcleo mantém uma pequena parte central privilegiada e coloca mais serviços do SO em processos separados em modo usuário que se comunicam por mensagens.
Um driver em modo usuário que falhou em um projeto de micronúcleo muitas vezes pode ser reiniciado enquanto outros serviços continuam.
Modo núcleo
O modo núcleo é o nível de privilégio da CPU que permite ao SO gerenciar o estado protegido do hardware e executar instruções privilegiadas.
A CPU entra no modo núcleo para tratar uma requisição feita pela aplicação de um usuário comum.
Modo usuário
O modo usuário é o nível de privilégio da CPU que executa instruções de aplicação com acesso restrito à memória e a operações protegidas.
O editor de um administrador ainda executa em modo usuário e usa chamadas de sistema para solicitar serviços protegidos.
Multiprogramação
A multiprogramação mantém várias tarefas disponíveis na memória para que uma possa usar a CPU enquanto outra está bloqueada por entrada e saída.
A multiprogramação permite que um compilador processe dados enquanto outro processo espera a conclusão de uma leitura de armazenamento.

N

Nível de exceção
Um nível de exceção é um nível de privilégio de execução do AArch64, incluindo EL0 para aplicações e normalmente EL1 para o núcleo do SO.
O AArch64 nomeia os níveis de privilégio de forma diferente dos anéis x86, mas ainda separa as aplicações do núcleo.
Núcleo
O núcleo é a parte central privilegiada de um sistema operacional que controla operações protegidas de hardware e gerencia recursos dos processos.
Uma aplicação de terminal solicita ao núcleo que leia a entrada em vez de controlar o dispositivo de teclado por conta própria.
Núcleo monolítico
Um núcleo monolítico executa a maioria dos serviços do SO em conjunto no modo núcleo, permitindo chamadas diretas, mas compartilhando os riscos de falhas em código privilegiado.
Um módulo de driver do Linux se junta ao núcleo monolítico e pode danificar seu estado se contiver um erro grave.
Número da chamada de sistema
Um número da chamada de sistema é um identificador específico da arquitetura que o núcleo usa para encaminhar uma chamada à implementação de um serviço específico.
O Linux usa números de chamada de sistema diferentes para write no x86-64 e no AArch64, então números brutos não são portáveis.

P

Paralelismo
Paralelismo significa que trabalhos executam no mesmo instante em recursos de processamento separados, como CPUs lógicas diferentes.
Um compilador e um renderizador podem executar em paralelo quando cada um está em uma CPU lógica diferente.
PATH
PATH é uma variável de ambiente que lista diretórios nos quais interfaces como execvp pesquisam nomes de executáveis.
Um comando encontrado por meio do PATH pode ser iniciado com execvp mesmo quando seu arquivo não está no diretório atual.
Pilha do núcleo
Uma pilha do núcleo contém o estado de usuário salvo e os dados de execução do núcleo enquanto ele executa em nome de um processo ou contexto de execução.
A troca para outro processo inclui restaurar a pilha do núcleo usada pelo caminho de retorno salvo desse processo.
Política
Uma política é uma regra para escolher como usar os mecanismos disponíveis, por exemplo, decidir qual processo pronto deve receber tempo de CPU.
Uma política de escalonamento pode mudar enquanto o código de salvamento de registradores usado para trocar processos permanece o mesmo.
Ponteiro de pilha
O ponteiro de pilha é um registrador que localiza a posição atual de uma pilha usada para chamadas de função e estado de execução.
Uma troca de contexto restaura o ponteiro de pilha para que a execução retomada use sua própria pilha salva.
Processamento em lote
O processamento em lote reúne tarefas submetidas e as executa sem entrada interativa da pessoa que submeteu cada tarefa.
No processamento em lote, um cálculo submetido espera pelas tarefas anteriores em vez de receber um turno imediato.
Processo
Um processo é uma instância ativa de um programa junto com a memória, os valores de registradores e o estado de entrada e saída necessários para sua execução.
Dois processos que executam o mesmo arquivo executável podem manter variáveis privadas e diretórios atuais diferentes.
Processo filho
Um processo filho é um novo processo criado por um pai, com sua própria identidade e uma cópia da memória privada comum do pai.
O processo filho pode alterar uma variável privada após fork sem mudar o valor do pai.
Processo órfão
Um processo órfão é um processo cujo pai terminou e ao qual é atribuído um novo pai para que outro processo possa coletar seu resultado mais tarde.
Um órfão pode continuar processando dados após o término de seu pai original; ele não terminou necessariamente.
Processo pai
Um processo pai é o processo que cria um filho; ele pode usar operações de espera para coletar o resultado de término desse filho.
Um interpretador de comandos permanece como processo pai enquanto seu filho substitui o próprio programa por um comando externo.
Processo zumbi
Um processo zumbi é um processo encerrado cujo registro de resultado permanece no núcleo porque seu pai ainda não coletou esse resultado.
Um servidor de longa duração evita o acúmulo de zumbis ao coletar os estados de término dos filhos que terminaram.
Programa
Um programa consiste em instruções e dados armazenados que podem ser carregados para execução; o próprio arquivo não tem estado de execução mutável.
O mesmo arquivo de programa de interpretador de comandos pode ser usado para iniciar processos separados em três janelas de terminal.

R

Redirecionamento
O redirecionamento altera o descritor de entrada padrão, saída padrão ou erro padrão de um comando para que ele se refira a outro recurso aberto de entrada e saída.
O redirecionamento de saída permite que um programa inalterado escreva sua saída padrão habitual em um arquivo escolhido.
Retorno de armadilha
Um retorno de armadilha restaura o estado de execução salvo e retorna do tratamento no núcleo ao contexto de execução de usuário pretendido.
O SO pode preparar o estado inicial de um novo processo para que o caminho de restauração inicie esse programa pela primeira vez.

S

Sinal
Um sinal é uma notificação de software entregue pelo SO a um processo, com uma ação padrão ou um comportamento de tratamento instalado.
Um terminal envia SIGINT à sua tarefa em primeiro plano quando o usuário pressiona Ctrl-C, normalmente encerrando a tarefa.
Sistema operacional
Um sistema operacional gerencia recursos de hardware e fornece serviços que permitem aos programas executar, comunicar-se e armazenar dados com segurança.
O sistema operacional compartilha tempo de CPU entre um navegador e um compilador enquanto gerencia suas requisições de arquivo.
Sobrecarga
Sobrecarga é o tempo ou a memória adicional gasto para gerenciar uma operação em vez de realizar diretamente o trabalho útil da aplicação.
Salvar registradores durante uma troca de contexto acrescenta sobrecarga antes que outro processo retome o processamento.

T

Tabela de armadilhas
Uma tabela de armadilhas mapeia tipos de evento para destinos protegidos de tratadores do núcleo, com organização exata dependente da CPU.
Na inicialização, o SO prepara os destinos dos tratadores para que o código de usuário não possa redirecionar uma entrada posterior no núcleo.
Tarefa em segundo plano
Uma tarefa em segundo plano é um trabalho iniciado sem que o interpretador de comandos espere imediatamente por sua conclusão, permitindo que a entrada de comandos continue.
O interpretador de comandos apresenta o prompt enquanto uma tarefa em segundo plano executa, mas ainda precisa coletar o estado de término do filho quando ela termina.
Troca de contexto
Uma troca de contexto salva um contexto de execução e restaura outro; uma troca de processo também seleciona o espaço de endereçamento apropriado.
Quando um leitor bloqueia, uma troca de contexto permite que um processamento pronto seja retomado na mesma CPU lógica.
Troca de modo
Uma troca de modo altera o nível de privilégio da CPU, como ao entrar no núcleo para um serviço e retornar ao mesmo processo de usuário.
Uma chamada de sistema não bloqueante pode envolver trocas de modo sem retomar um processo diferente.

U

Utilização da CPU
A utilização da CPU é a fração de um intervalo decorrido durante a qual a CPU está ocupada, expressa como proporção ou porcentagem.
Se o processamento útil ocupa 9 ms em um intervalo de 12 ms sem sobrecarga, a utilização da CPU é 75%.

V

Vetor de argumentos
Um vetor de argumentos é um array de ponteiros para strings, terminado por um ponteiro nulo, fornecido a um novo programa como seus argumentos de linha de comando.
Um vetor de argumentos para um comando de contagem tem entradas separadas para o nome do programa, a opção e o nome do arquivo.
Virtualização
A virtualização apresenta um recurso físico compartilhado como um recurso virtual conveniente que cada programa pode usar por meio de uma visão mais simples.
A virtualização da CPU permite que centenas de processos progridam em uma máquina com apenas algumas CPUs lógicas.

Sobre esta aula

Por StudeliRelatar um erro

Crie seus próprios Materiais de Estudo

Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.

Começar