Vous êtes sur la page 1sur 14

Gustavo Torres Gimenes Nunes

RA 0030481721004

PARTE 1 – QUESTIONÁRIO SO ESTRUTURA

1) As principais funções de um SO são:


 Gerenciar os recursos e processos;
 Máquina Estendida (ou Abstração de Recursos).

2) Um sistema operacional é caracterizado como uma máquina estendida


pois fornece ao usuário uma interface de acesso aos dispositivos mais simples do
que as interfaces de baixo nível, facilitando a manipulação de arquivos e
aplicativos executadas pelo usuário.
As interfaces de abstração podem ou não serem parecidas com as
interfaces de baixo nível. Um exemplo pode ser encontrado no próprio sistema
Windows, em que a interface gráfica normal da área de trabalho (desktop) se
difere bastante da linha orientada de comando (prompt de comandos), ambos
utilizando a interface de abstração.

3) Um sistema operacional é caracterizado com um gerenciador de recursos,


pois o kernel – coração do sistema operacional – é responsável por gerir a
comunicação entre o hardware e a interface do usuário (ou processos do usuário).
Os programas são executados simultaneamente no software - ou seja,
várias aplicações estão sendo executadas todas ao mesmo tempo, mas seus
processos são executados um por vez em uma pequena quantidade de tempo.
Assim, quando um aplicativo solicita a utilização de um recurso (como, por
exemplo, a CPU ou a memória RAM), o sistema operacional e responsável por
definir as políticas de acesso ao recurso, como: quais programas terão acesso por
primeiro ao recurso e em que quantidade de tempo.

4) Spooling é o nome dado a uma técnica que copia dados, instruções ou


processos de um dispositivo para outro quando a velocidade de um dos
dispositivos é consideravelmente maior do que do outro. Os dados são
temporariamente armazenados em um buffer ou outro tipo de memória volátil até
que sejam requisitados para uso pelo dispositivo, programa ou sistema.
Um exemplo do uso de spooling é quando um dispositivo envia arquivos
(dados) a uma impressora - que podem ser enviados muito mais rápidos do que
uma impressora consegue, de fato, imprimir. Ao invés de pedir ao dispositivo que
espere até que a impressão do primeiro arquivo seja finalizada, através de um
programa a impressora armazena os dados restantes em uma memória para
serem usados conforme forem solicitados (organizados, geralmente, no algoritmo
FIFO – First in, fisrt out).

5) Multiprogramação é uma forma de processamento paralelo de programas


aos quais são executados “ao mesmo tempo”.
Portanto, o que ocorre é que o sistema operacional executa parte de um
programa parte de outro e assim por diante. Funcionando através de uma
interrupção no processamento do programa após um intervalo especifico de
tempo e da execução de um outro programa, também por um intervalo especifico
de tempo; técnica essa que otimiza o funcionamento do processador (que pode
ser planejado através de algoritmo FIFO – first in, first out; ou por mais curto
primeiro).
Um exemplo é a utilização de navegadores, reprodutores de áudio,
transferência de dados e downloads de aplicativos todos sendo executados ao
mesmo tempo em um computador, que estão sendo processados e executados
um de cada vez em um único processador.

6) Time-sharing é uma técnica que permite vários usuários usar e


compartilhar os recursos de um computador ao mesmo tempo.
O time-sharing nada mais é que uma extensão do conceito de
multiprogramação, ou seja, através de intervalo específico de tempo cada tarefa
requisitada é executada uma por vez, dando a impressão de que os diversos
terminais possuem acesso simultâneo ao mesmo recurso computacional.
Exemplo:

Na figura abaixo, cinco usuários diferentes estão solicitando atividades ao


mesmo recurso computacional (CPU), porém apenas o usuário cinco está ativo
enquanto os ouros permanecem em uma fila de espera. Quando o tempo de
execução do usuário cinco terminar, o controle passa para o próximo usuário na
fila (no sentido horário).

7) A paginação sob demanda é baseada na paginação simples onde a


memória lógica é dividida em páginas que possuem tamanhos fixos e podem ser
colocadas em qualquer quadro da memória física. A tabela de páginas é usada
para a conversão de endereços lógicos em físicos.
Viabilizou o uso da memória virtual por estender as páginas para memória
secundária (disco0 e apenas as páginas que o processo acessa são carregadas
para a memória física. O bit de válido/inválido indica se a página já está na
memória ou no disco.
Na conversão de endereço lógico a MMU (Unidade de Gerenciamento de
Memória) testa o bit válido/inválido e se o bit for válido o endereço é convertido e
o acesso é executado normalmente, caso contrário a MMU gera uma interrupção
de proteção e aciona o SO.
Algumas das características:
 Menos operações de E/S;
 Menos utilização de memória;
 Resposta mais rápida;
 Mais usuários/processos.
Exemplo:

8) Na segmentação parte da memória é removida do processo que está


sendo executado atualmente através do uso de registradores. Se o dado prestes
a ser lido ou escrito está fora do espaço do endereço do processo, uma falha de
segmentação é lançada.
O programa de computador é dividido em secções de tamanhos variados
e separados por tipos de dados onde cada uma delas ocupará um segmento da
memória. O sistema operativo que suporta este programa possuirá uma tabela
com os tamanhos e endereços de memória dos vários segmentos para saber onde
estão. Cada segmento possui um conjunto de permissões (leitura, escrita ou
execução).
Se o processo é autorizado pelas permissões e se o endereço está no
trecho do segmento a referência do endereço da memória é permitida, senão a
falha é lançada.
A MMU é responsável pela tradução de um segmento e um endereço
relativo no segmento em um endereço de memória, e também pela verificação da
permissão da referência e valor do endereço relativo.
Exemplo:

PARTE 2 – QUESTIONÁRIO SO PROCESSOS


9) Um processo pode ser descrito como a execução de um programa (sendo
que cada processo é associado a seu espaço de endereçamento), enquanto o
programa é um código executável formado por um conjunto de instruções
(algoritmo) e um código objeto.

10) O que identifica um processo é o seu PID, ou seja, seu process indetifier.
Este identificador corresponde a um valor inteiro que irá informar o número do
processo que está sendo executado ou manipulado, ou seja, qual o processo está
sendo manipulado.
Exemplo:
11) A primitiva fork() é uma chamada de sistema, ou seja, uma chamada ao
sistema operacional para que dentro de um processo provoque a criação de um
clone perfeito (tendo as mesmas variáveis, registros, descritores de arquivo, e
entre outros) deste mesmo processo para execução; assim os processos pai
(processo copiado) e filho (processo criado) possuem o mesmo código.

Para usar a chamada de sistema de criação de processos, simplesmente


escrevemos fork(). Fazendo isso, o sistema operacional se encarrega do resto e
retorna um número de identificação, Porém ao armazenar esse retorno da função
fork() numa variável de nome ‘pid’ do tipo ‘pid_t, vemos que esse número de pid
tem um comportamento especial:

 Dentro do processo filho, o pid tem valor 0;


 Dentro do processo pai, o pid tem o valor do processo filho;
 Retorna um valor negativo caso tenha ocorrido algum erro;
12)
Primeiro o processo original roda e cai no else, pois é o pai, e sua pid é
exibida. Em seguida a mensagem final a ambos os processos é exibida. Porém,
foi criado outro processo com a função fork(), então um processo idêntico vai ser
executado (e este é o processo filho) que cai na condição if. Por fim a mensagem
final a ambos é exibida e o programa é encerrado.

13) A primitiva exit() é a única que não retorna valor, pois todos os descritores
de arquivos abertos são automaticamente fechados.

Por convenção, um código de retorno exit() igual a 0 significa que o


processo terminou normalmente; enquanto que quando é retornado um valor não
nulo (em geral -1 ou 1), indicará a ocorrência de um erro de execução.

Um exemplo pode ser encontrado no próprio item anterior (questão 12),


em que: na linha 15 de código temos o retorno exit(1) indicando – caso acordado
- que algo de errado ocorreu na primitiva fork(); enquanto que na linha 30, temos
o retorno da finalização do programa completo por exit(0).

14) A primitiva wait() suspende a execução do processo até a morte de seu


filho. Se o filho já estiver morto no instante de chamada da primitiva (caso de um
processo zumbi), a função retorna imediatamente. O valor de retorno pode ser o
identificador do processo morto ou -1 em caso de erro.

Já, se status é não nulo (NULL), tanto a primitiva wait() quanto waitpid()
(abordada mais a frente) armazena a informação relativa a razão da morte do
processo filho, sendo apontada pelo ponteiro status. Este valor pode ser avaliado
com diversas macros que são listadas com o comando shell man 2 wait. O código
de retorno via status indica a morte do processo que pode ser devido:

 Uma chamada exit(), e neste caso, o byte a direita de status vale 0, e o byte a
esquerda é o parâmetro passado a exit() pelo filho;
 Uma recepção de um sinal fatal, e neste caso, o byte a direita de status é não
nulo. Os sete primeiros bits deste byte contêm o número do sinal que matou o
filho.
Exemplo:

15) A primitiva waitpid() suspende a execução do processo até que o filho


especificado pelo argumento mude de estado, ou seja, o processo pai espera até
que um processo filho específico seja suspenso.
Por padrão, esta chamada espera pelo término do processo filho, mas esse
comportamento pode ser modificado pelo argumento option; e caso ele já esteja
morto no momento da chamada, o comportamento é idêntico ao descrito
anteriormente na primitiva wait().

Os tópicos abaixo mostram os possíveis valores para o argumento pid:


 < -1: significando que o pai espera a morte de qualquer filho cujo o ID do grupo
é igual ao valor de pid;
 -1: significado que o pai espera a morte de qualquer filho;
 0: significado que o pai espera a morte de qualquer processo filho cujo ID do
grupo é igual ao do processo chamado;
 > 0: significado que o pai espera a morte de um processo filho com um valor de
ID exatamente igual a pid.
 Exemplo:

16) O comando fork() cria uma cópia idêntica ao seu processo pai retornando
o pid do processo criado enquanto a família de chamadas de sistema exec() ()
permitem o lançamento da execução de um programa externo ao processo. Não
existe a criação efetiva de um novo processo, mas simplesmente uma substituição
do programa de execução, ou seja, possui a capacidade de substituir
completamente a imagem de um processo vigente por uma nova imagem (sendo
que este arquivo executável pode ser tanto um arquivo binário quanto um script)
e em caso de sucesso, não há retorno desta chamada de sistema, isto devido à
substituição da imagem do processo. Já em casos de erro, a função retorna -1 e
a variável global errno (contida na biblioteca errno.h) é sinalizada de acordo com
a causa do erro.

17) Sim, a identificação do processo pai é sempre maior que a do processo


filho, pois este último sofre de uma hierarquia de processos.

18) Não, a sequência de pid pode ser aleatória; isto porque o tempo de
resposta da criação de um filho pode variar gerando números de pid variados.

19) A primitiva system() permite a execução de um comando shell dentro de


um programa. Esta primitiva executa um comando especificado por string,
chamando o programa /bin/sh/ -c string, retornando após o comando ter sido
executado ( mostrando no dispositivo de saída).

Durante a execução do comando, SIGCHLD será bloqueado e SIGINT e


SIGQUIT serão ignorados
O retorno dessa primitiva é o código de retorno do comando. Em caso de
erro, retorna 127 se a chamada da primitiva exec() falhar, ou -1 se algum outro
erro ocorrer.
Exemplo:
20) A chamadas de sistema exec() possui a capacidade de substituir
completamente a imagem de um processo vigente por uma nova imagem, ou seja,
permitem o lançamento da execução de um programa externo ao processo. Não
existe a criação efetiva de um novo processo, mas simplesmente uma substituição
do programa de execução assim como já mencionada na questão 16.

Em sua essência, as seis chamadas de sistema exec() possuem a mesma


funcionalidade e diferem apenas na quantidade e forma como os argumentos são
passados para o novo programa.

Estão listados abaixo significado de cada uma delas :

 L: lista de argumentos [execl(), execle() e execlp()]. Os argumentos que serão


recebidos em argv são listados um a um como parâmetros da função em forma
de string.
 V: vetor de argumentos [execv(), execve() e execvp()]. Os argumentos que serão
recebidos em argv são passados em um vetor do tipo char* que já contém todas
as strings previamente carregadas.
 E: variáveis de ambiente [execle() e execve()]. O último parâmetro destas
funções é um vetor para strings (char *) que será recebido pelo novo programa no
argumento envp contendo variáveis de ambiente pertinentes para sua execução.
Para as versões sem a letra “e“, o ambiente é adquirido a partir de uma variável
externa (extern char **environ) já declarada na biblioteca unistd.h.
 P: utilização da variável de ambiente PATH [execlp() e execvp()]. Esta função irá
buscar a nova imagem do processo nos diretórios contidos na variável PATH. Para
as versões sem a letra “p“, deverá ser passada uma string contendo o caminho
completo para o arquivo executável.

PARTE 3 – QUESTIONÁRIO ESPECÍFICO AO CÓDIGO ABAIXO:

21) O código acima resulta na execução de 3 processos:


 O processo original (processo pai);
 O processo filho (criado com o fork() da 15 linha),
 O processo neto (criado com o fork() da 21 linha);
Apesar do comando execl() também iniciar um novo processo, nesse exemplo,
não chegou a ser executado com sucesso.

22) No primeiro printf (“Quem sou eu?”), o processo em execução é o


processo neto (filho do processo filho), enquanto no segundo printf(“E eu, quem
sou?”), é encontrado o processo filho.

23) O comando wait() faz com que o processo pai espere a finalização do
processo filho para que continue sua execução comum. Geralmente o retorno
desta função é o número do pid do processo filho ou, em caso de erro, o retorno
é igual a -1.
24) A variável status, neste código serve para sabermos se o processo filho
encerrou suas atividades ok, isto porque, a variável armazena o retorno da
primitiva wait() (sendo 0 para ok e -1 para erro), o que pode ser testados nas linhas
de 35 à 42.

25) Não, o comando execl() é executado apenas uma vez pelo processo filho,
pois esta linha de código se encontra dentro do escopo, ou seja, área de execução
do processo filho e não a uma área comum de execução a todos os processos.

Vous aimerez peut-être aussi