- Aprender
- Computação e TI
- Ciência da computação
- Sistemas operacionais
- Processos e chamadas de sistema
Processos e chamadas de sistema
IntermediárioAula 1 de 9 do curso Curso de Sistemas operacionais
O que você vai aprender
- 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 operacionalExplique 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 operacionalAcompanhe 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.
- Descreva as principais funções de um sistema operacional
- 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 eleDiferencie 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 estadosUse 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.
- Explique o que é um processo e quais informações o sistema operacional mantém sobre ele
- 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 forkChame fork, diferencie o pai do filho pelo valor de retorno e preveja o que cada processo vê e imprime.
- Execute outro programa com execSubstitua 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 processosEncerre 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 redirecionamentoCombine 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.
- Crie um processo filho com fork
- 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úcleoExplique 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 funcionaAcompanhe 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çõesClassifique 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.
- Diferencie o modo usuário do modo núcleo
- 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 limitadaExplique 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 custoAcompanhe 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.
- Explique a execução direta limitada
Guia de estudo de Processos e chamadas de sistema
1Explique 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:
- O dispositivo de rede informa a chegada de dados. O núcleo os disponibiliza ao processo que faz o download.
- O SO compartilha o tempo de CPU entre o navegador, o compilador e outros processos prontos.
- 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 compartilhamento | O que é dividido | Exemplo |
|---|---|---|
| Compartilhamento de tempo | Uso de um recurso em intervalos sucessivos | Turnos de CPU |
| Compartilhamento de espaço | Capacidade ocupada por diferentes usuários ao mesmo tempo | Regiõ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
| Projeto | Onde a maioria dos serviços executa | Principal relação entre vantagens e custos |
|---|---|---|
| Núcleo monolítico | Sistemas de arquivos, drivers e rede executam em modo núcleo | Chamadas diretas são eficientes; um erro em um driver pode danificar o núcleo |
| Micronúcleo | Uma pequena parte central privilegiada dá suporte a processos de serviço em modo usuário | O isolamento de serviços ajuda a conter falhas; a comunicação acrescenta trabalho |
| Núcleo híbrido | Combina elementos das duas abordagens | O 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.
2Descreva 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:
- Seu código executável e seus dados, com partes carregadas quando necessário nos sistemas modernos.
- 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. - Memória para alocação dinâmica e o estado inicial dos registradores.
- Um ambiente de entrada e saída, incluindo descritores de arquivo herdados.
- 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
| Estado | Significado | Exemplo |
|---|---|---|
| Em execução | Executando em uma CPU | Um compilador gerando instruções |
| Pronto | Apto a executar, esperando por tempo de CPU | Um processamento cujo turno acabou de terminar |
| Bloqueado | Esperando por um evento antes de poder continuar | Uma 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.
| Intervalo | CPU | Estado de A | Estado de B |
|---|---|---|---|
| 0–2 ms | A | Em execução | Pronto |
| 2–5 ms | B | Bloqueado | Em execução |
| 5–6 ms | B | Pronto | Em execução |
| 6–7 ms | A | Em execução | Finalizado |
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.
3Crie 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 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ção | Novo processo? | Efeito principal |
|---|---|---|
fork() | Sim | Cria um filho com estado de execução copiado |
| Exec bem-sucedido | Não | Substitui 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
ppesquisam noPATH; 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.
WNOHANGretorna 0 quando existe um filho correspondente, mas ele não tem um resultado pronto.- Repita uma espera interrompida em caso de
EINTR;ECHILDsignifica que não resta nenhum filho correspondente. - Um zumbi terminou; um órfão perdeu seu pai e ainda pode estar executando.
SIGKILLeSIGSTOPnão podem ser capturados nem ignorados.exitdescarrega os buffers da biblioteca C;_exitnã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.
4Explique 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.
| Plataforma | Modo da aplicação | Modo do núcleo |
|---|---|---|
| x86-64 no Linux ou Windows | Anel 3 | Anel 0 |
| AArch64 | EL0 | EL1 |
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:
- A função que encapsula a chamada coloca o número de serviço 1 em
rax, o descritor 1 emrdi, o endereço do buffer emrsie a contagem 3 emrdx. syscallpreserva 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.- O código de entrada do núcleo estabelece a pilha do núcleo do processo e salva o estado de usuário restante.
- 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. - 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
| Evento | Causa | Momento em relação à instrução atual |
|---|---|---|
| Armadilha de chamada de sistema | O programa solicita deliberadamente um serviço | Síncrono |
| Exceção ou falha | A instrução atual precisa de tratamento especial | Síncrono |
| Interrupção de hardware | Um temporizador ou dispositivo sinaliza à CPU | Assí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.
5Explique 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:
- A executa instruções de usuário. O temporizador o interrompe.
- 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.
- 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.
- 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.
- 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ção | Transição de modo? | Mudança de processo? |
|---|---|---|
| Um serviço não bloqueante retorna a quem o chamou | Usuário → núcleo → usuário | Não |
| O temporizador é tratado e o mesmo processo é retomado | Usuário → núcleo → usuário | Não |
| A bloqueia e B é retomado | Entrada no núcleo, depois caminho de retorno de B | Sim |
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
1Veja o que o sistema operacional oferece
12 passos
- 1Introduçã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?
- 2Explicaçã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.
- 3ExemploAcompanhe 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.
- 4Ideia-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.
- 5DesafioLocalize 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.
- 6Explicaçã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.
- 7Explicaçã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.
- 8ExemploCalcule 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%.
- 9Conexã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.
- 10Explicaçã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.
- 11DesafioConecte 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.
- 12Recapitulaçã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.
2Acompanhe um processo no trabalho de CPU e de entrada e saída
13 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhe uma instância ativa
- 2ExplicaçãoTransforme um arquivo em um processo
- 3ExplicaçãoPreserve a próxima instrução
- 4ExemploLeia uma listagem de processos
- 5DesafioInterprete endereços iguais
- 6DicaInterprete descritores como recursos herdados
- 7ExplicaçãoPergunte o que falta
- 8ExemploComece a linha do tempo
- 9ExemploConclua a linha do tempo
- 10DesafioAltere a decisão sobre o processo pronto
- 11ConexãoReconheça nomes reais de estados
- 12DesafioRelacione registros à capacidade da CPU
- 13RecapitulaçãoInterprete o estado como aptidão
3Construa um comando com chamadas de processo
23 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoDê uma função a cada chamada
- 2ExplicaçãoDivida uma execução em duas
- 3ExemploAcompanhe um valor privado
- 4ExemploConte o ramo, não as chamadas no código-fonte
- 5DesafioConte os caminhos que imprimem
- 6DicaVerifique o que foi copiado
- 7ExplicaçãoSubstitua o programa do filho
- 8ExemploPasse strings de argumentos separadas
- 9DesafioPreveja uma substituição bem-sucedida
- 10Ideia-chaveUse o intervalo antes de exec
- 11ExplicaçãoColete o resultado de um filho
- 12ExemploDecodifique o término normal
- 13ExplicaçãoNão deixe resultados sem coleta
- 14DesafioTrate a interrupção corretamente
- 15DicaEscolha uma solicitação de término
- 16ExplicaçãoRetorne a um prompt em primeiro plano
- 17ExemploRedirecione a saída antes da substituição
- 18DesafioPreserve o destino da saída
- 19ExemploConecte as duas extremidades do canal
- 20Ideia-chaveInicie antes de esperar
- 21ConexãoAcompanhe o trabalho em segundo plano
- 22DesafioAcompanhe uma sequência de comandos sem saída
- 23RecapitulaçãoAcompanhe identidade, imagem e resultado
4Acompanhe uma requisição através da fronteira de privilégios
15 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoPergunte por que houve entrada no núcleo
- 2ExplicaçãoLimite o que as aplicações podem controlar
- 3ExemploSepare identidade de privilégio
- 4DesafioPreveja o resultado de uma instrução proibida
- 5ExplicaçãoEncontre a chamada de sistema efetiva
- 6ExemploPrepare uma escrita
- 7ExemploEntre e valide
- 8ExemploInterprete o retorno
- 9DesafioAcompanhe uma leitura bloqueada
- 10ExplicaçãoClassifique pela causa
- 11ExemploCorrija ou encerre
- 12DicaMantenha os sinais na camada de processo
- 13DesafioClassifique a conclusão de uma operação de dispositivo
- 14DesafioSepare dois tipos de entrada
- 15RecapitulaçãoAcompanhe causa, verificações e resultado
5Mantenha o controle enquanto programas executam diretamente
15 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoMantenha velocidade e controle juntos
- 2ExplicaçãoResolva dois problemas de controle
- 3ExemploPrepare a máquina e o processo
- 4ConexãoVeja a fraqueza da abordagem cooperativa
- 5ExemploInterrompa um laço infinito
- 6DesafioIdentifique o mecanismo ausente
- 7ExplicaçãoSalve o caminho de usuário interrompido
- 8ExemploSalve também o caminho do núcleo
- 9ExemploRetorne pelo estado salvo de B
- 10DesafioIdentifique uma transição apenas de modo
- 11ExplicaçãoConte os custos após uma troca
- 12ExemploMonte o denominador do ciclo completo
- 13DesafioEstime uma mudança na duração do turno
- 14DesafioRelacione uma chamada bloqueada à retomada
- 15RecapitulaçãoAcompanhe a quem pertence o caminho de retorno
Questões de Processos e chamadas de sistema
Conjunto 1: Recursos, processos e proteção10 Questões de Prática
Questão 1 de 10
Mostrar respostaOcultar resposta
Questão 2 de 10
Mostrar respostaOcultar resposta
Questão 3 de 10
Mostrar respostaOcultar resposta
Questão 4 de 10
Mostrar respostaOcultar resposta
Questão 5 de 10
Mostrar respostaOcultar resposta
Questão 6 de 10
Mostrar respostaOcultar resposta
Questão 7 de 10
Mostrar respostaOcultar resposta
Questão 8 de 10
Mostrar respostaOcultar resposta
Questão 9 de 10
Mostrar respostaOcultar resposta
Questão 10 de 10
Mostrar respostaOcultar resposta
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
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");
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
/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?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
exit(515). Após uma espera bem-sucedida e um teste WIFEXITED verdadeiro, o que WEXITSTATUS informa?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
waitpid(child, &status, WNOHANG) retorna 0. O que aconteceu?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
SIGKILL deixa a entrada visível. Que ação libera o registro de resultado mantido para esse filho?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
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?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
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
errno é ENOENT. O que o processo que fez a chamada deve concluir?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
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
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
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");
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
execvp para um programa que não existe. Sua próxima instrução imprime um erro. Por que essa instrução pode executar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
errno EINTR. O que o pai deve fazer para continuar coletando os estados dos filhos?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
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?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
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
WIFSIGNALED após waitpid ser bem-sucedido. Qual macro fornece o número do sinal que o encerrou?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
SIGSEGV. Qual sequência diferencia os dois eventos?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
dup2(fd, 0), fecha fd incondicionalmente e executa o comando com exec. Por que a entrada padrão do comando pode ficar fechada?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");
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Abstração
Anel de proteção
Armadilha
B
Bloco de controle de processo
Buffer de saída
Buffer de tradução de endereços
C
Canal de comunicação
Chamada de sistema
Código de saída
Coleta do estado de término
Comando interno
Compartilhamento de espaço
Compartilhamento de tempo
Concorrência
Contador de programa
Contexto do núcleo
Controle cooperativo
Cópia na escrita
D
Descrição de arquivo aberto
Descritor de arquivo
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.Escalonador
Espaço de endereçamento
Espera não bloqueante
Estado de bloqueado
Estado de execução
Estado de pronto
Estado retornado por wait
Exceção
exec
Execução direta limitada
F
Falta de página
Fatia de tempo
Fechamento na execução
Fim de arquivo
fork
Função de biblioteca que encapsula uma chamada de sistema
I
Identificador de processo
Instrução privilegiada
Interpretador de comandos
Interrupção de hardware
Interrupção do temporizador
M
Mecanismo
Micronúcleo
Modo núcleo
Modo usuário
Multiprogramação
N
Nível de exceção
Núcleo
Núcleo monolítico
Número da chamada de sistema
P
Paralelismo
PATH
execvp pesquisam nomes de executáveis.Pilha do núcleo
Política
Ponteiro de pilha
Processamento em lote
Processo
Processo filho
Processo órfão
Processo pai
Processo zumbi
Programa
R
Redirecionamento
Retorno de armadilha
S
Sinal
Sistema operacional
Sobrecarga
T
Tabela de armadilhas
Tarefa em segundo plano
Troca de contexto
Troca de modo
U
Utilização da CPU
V
Vetor de argumentos
Virtualização
Sobre esta aula
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.