Você está na página 1de 10

Exercícios Aula 3

Processos
O livro base para as responder aos exercícios é:
SILBERSCHATZ, Abraham; Peter Baer Galvin, Greg Gagne. Fundamentos de
Sistemas Operacionais. 9 ed. Rio de Janeiro: LTC, 2015.
O livro se encontra disponível na Biblioteca do UNICEUB com acesso online.
1. Usando o programa mostrado a seguir, explique qual será a saída na LINHA A.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int value = 5;
int main()
{
pid_t pid;
pid = fork();
if (pid == 0) { /* processo-filho */
value += 15;
return 0;
}
else if (pid > 0) { /* processo-pai */
wait(NULL);// A função wait suspende a execução do processo
até //a morte de seu filho. Se o filho já estiver morto no
instante da //chamada da primitiva (caso de um processo zumbi), a
função retorna //imediatamente.
printf(“PARENT: value = %d”, value); /* LINHA A */
return 0;
}
}

O resultado continua 5, pois o filho não atualiza sua cópia de value, pois não
entrou no if. Quando o controle retornar ao pai, sua variável value continuará
sendo 5.
2. Faça um comentário a respeito do seguinte código relacionados aos processos
pai e filho.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
int i;
char c = 'a';
pid_t pid;

if ((pid = fork()) < 0)


{
perror("fork");
exit(1);
}
if (pid == 0)
{
printf("Caractere e Endereco: %c - %p (filho)\n",c,&c);
}
else
{
c='b';
printf("Caractere e Endereco: %c - %p (pai)\n",c,&c);
}

scanf("%d", &i);
exit(0);
}

O resultado é o seguinte:

O código mostra que se pid < 0, não foi criado o processo-filho direito, ocorrendo o
erro. Quando é pid == 0, mostra o endereço e o dado que contém no processo-filho. Se
não for nenhuma das alternativas anteriores é quando o processo pai que está sendo
executado mostrando seu endereço e seu dado armazenado.

3. Incluindo o processo-pai inicial, quantos processos são criados pelo programa


mostrado (lembrando que cada filho com x=n vai criar ainda netos (com
x=n+1 ... ?)( 2^n), e estes bisnetos, etc).
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
/* cria um processo-filho */
fork();
/* cria outro processo-filho */
fork();
/* e cria um outro */
fork();
return 0;
}
São criados 16 processos nesse código, pois tem o processo-pai, cria o processo-filho
quando cria outro processo-filho acaba criando 2 processos, no segundo cria 4
processos e no ultimo 8 processos tendo um total de 16 processos.

4. Quando um processo cria um novo processo usando a operação fork(), qual dos
estados a seguir é compartilhado entre o processo-pai e o processo-filho?
a) Pilha
b) Heap
c) Segmentos de memória compartilhada
Somente os segmentos de memória compartilhada são compartilhados entre
processo-pai e processo-filho recém-criado por fork(). São feitas da pilha e do heap
para o processo recém-criado.
5. Incluindo o processo-pai inicial, quantos processos são criados pelo programa
mostrado no programa a seguir?
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
{
int i;
for (i = 0; i < 4; i++)
fork();
return 0;
}
Serão criados 32 processos.
6. Usando o programa mostrado na Figura 3.35, explique qual será a saída nas
linhas X e Y.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define SIZE 5
int nums[SIZE] = {0,1,2,3,4};
int main(){
int i;
pid_t pid;
pid = fork();
if (pid == 0) {
for (i = 0; i < SIZE; i++) {
nums[i] *= -i;
printf(“CHILD: %d “,nums[i]); /* LINHA X */
}
}
else if (pid > 0) {
wait(NULL);
for (i = 0; i < SIZE; i++)
printf(“PARENT: %d “,nums[i]); /* LINHA Y */
}
return 0;
}
7. Quais são os benefícios e as desvantagens de cada uma das situações a seguir?
Considere tanto o nível de sistema quanto o nível de programador.

a. Comunicação síncrona, e assíncrona


b. Armazenamento em buffer automático e explícito
c. Envio por cópia e envio por referência
d. Mensagens de tamanho fixo e de tamanho variável
a) comunicação síncrona seria com bloqueio e as mensagens emitidas por uma pessoa
são imediatamente recebidas e respondidas por outras pessoas. Como a ligação
telefônica.
A comunicação assíncrona é sem bloqueio, ela está desconectada do tempo e do
espaço. Ou seja, o comunicador e o receptor podem manter relacionamento na
medida em que tenham tempo disponível. Como no WhatsApp, e-mail.
b) independentemente de a comunicação ser direta ou indireta, as mensagens
trocadas por processos em comunicação localizam-se em uma fila temporária.
c)
d) A mensagem com tamanho fixo, podem ser enviadas, a implementação no nível do
sistema é simples. Essas restrições tornam a tarefa de programar mais difícil. As
mensagens de tamanho variável requerem uma implementação mais complexa no
nível do sistema, mas a tarefa de programar se torna mais fácil.
8. Faça um resumo (Mínimo 5 laudas) de todo o capítulo 3 do livro. O resumo
deve ser escrito neste arquivo (não criar um arquivo à parte).

PROCESSOS
Em um sistema operacional moderno, processos de usuários e processos de kernel
estão concorrentemente disputando o recurso do processador, logo uma boa gerência
sobre esses processos deve ser implementada no sistema operacional para que o
processador seja usado com mais eficiência, evitando que ele fique ocioso.
Um processo nada mais é que um programa em execução, entretanto um programa
por si só não é um processo. Um programa é um arquivo com uma lista de instruções
geralmente armazenado no disco rígido, porém não está sendo executado sem estar
carregado na memória principal, entretanto ele recebe o nome de arquivo executável
(.exe). Já o processo, é conjunto de instruções sendo executadas que possui um
contador para a próxima instrução que será executada. Para o programa ser um
processo, ao inicializar um arquivo executável ele será carregado na memória principal
e estará pronto para ser executado.
Um processo possui estado, novo, em execução, em espera, pronto e concluído.
Quando é executado pode haver alternância entre os estados. Então, quando um
processo está pronto para ser executado ele fica no estado “pronto”, quando o
processador estiver sendo processado o estado muda para “em execução”, caso haja
uma interrupção, e o processador precise mudar para outro processo, ele volta para o
estado “pronto” novamente. Entretanto, apenas um processo pode estar em execução
a cada instante em um processador, mas muitos processos podem estar nos outros
estados.
No sistema operacional o processo é representado por um bloco de controle de
processos onde são guardadas todas as informações referentes ao processo, como o
estado do mesmo. O contador do programa indica qual a próxima instrução a ser
executada, registradores da CPU, informações de scheduling da CPU, informações de
gerenciamento da memória, informações de contabilização e informações de status de
E/S. Com as informações guardadas no bloco de controle de processo, o sistema
operacional consegue gerenciar de forma eficaz os processos, pois possui todas as
informações necessárias.
THREADS
Os modelos de processos que vimos até então executam apenas um thread, tal como
quando um processo executa um processamento de texto, ele está com um único
thread de instruções sendo executado.
Os sistemas operacionais mais modernos já permitem que o processo tenha vários
threads em execução, logo desempenha mais de uma tarefa por vez. O PCB (process
control block) é expandido de tal forma a incluir informações para cada thread.
SCHEDULING DE PROCESSOS
A função do scheduling de processos é apontar um processo que esteja com o estado
pronto e disponibilizá-lo ao processador para que possa ser executado. Ou seja, o
escalonador manterá o processador sempre ocupado com algum processo, para que
ele não fique ocioso.
Os processos que estão no estado pronto são armazenados em uma lista chamada fila
de “prontos”. Essa fila tem uma estrutura de lista encadeada onde possui um
cabeçalho que aponta para o primeiro e para o último da fila de “prontos”, e a partir do
primeiro bloco mantém-se um apontador para o próximo PCB a ser executado.
Há também outros tipos de filas, como a fila do dispositivo que armazena os processos
que necessitam dos recursos de algum dispositivo de E/S, mas caso o dispositivo esteja
ocupado, executando outra tarefa, o processo fica aguardando o dispositivo nessa fila.
Pode-se enfatizar que, como pode haver vários dispositivos conectados ao computador,
cada dispositivo tem a sua própria fila de processos. Após o dispositivo executar o
processo que estava na sua fila, ele volta para a fila de “prontos” do processador onde
terá que esperar mais uma vez o processador ficar disponível para poder executar o
processo novamente. E esse ciclo permanece até terminar todas as instruções do
processo onde, depois de finalizado, ele é removido de todas as filas onde estava
presente e tem seu PCB e recursos removidos da memória.
MUDANÇA DE CONTEXTO
Quando o CPU sofre uma interrupção é preciso salvar as informações do processo que
estão em execução para que depois ele possa retomar a execução. Para isso, o sistema
operacional precisa salvar o contexto corrente do processo. Esse contexto é salvo no
PCB do processo, onde são guardados o valor dos registradores da CPU, o estado do
processo, além das informações de gerenciamento de memória.
Após salvar o contexto do processo que estava em execução, o CPU precisará carregar
o contexto do novo processo a ser executado. Essa tarefa é chamada de mudança de
contexto, e o tempo para fazer essa operação depende muito do hardware, pois com
mais registradores conclui-se a tarefa mais rapidamente.
OPERAÇÕES SOBRE PROCESSOS
Um processo pode criar vários outros processos e assim sucessivamente, criando uma
estrutura hierárquica com os parâmetros de uma árvore. O processo criador é
chamado de processo pai e os processos criados são chamados de processos filhos.
Esses processos na maioria dos sistemas operacionais recebem um identificador de
processo (pid) que é exclusivo e geralmente é um valor inteiro.
No sistema operacional Linux, primeiro processo a ser criado é o unit, no qual possui o
pid = 1. Portanto, os outros processos a serem criados no sistema serão filhos do
processo unit e consequentemente terão um pid maior que 1.
Quando um processo filho é criado, ele precisa de recursos para que as tarefas sejam
executadas como tempo de CPU, memória, arquivos, dispositivos de E/S etc. podendo
obter esses recursos diretamente do sistema operacional ou o processo pai poderá
dividir seus recursos com os processos filhos. Quando o processo pai divide os
recursos, de certa forma impedirá que o processo filho crie muitos outros processos
filhos, fazendo com o sistema não sobrecarregue. Além de fornecer diversos recursos
físicos e lógicos, o processo pai pode passar, na inicialização, dados (entradas) ao
processo filho.
Para criar um processo fazendo uma cópia do processo pai, é usado o comando fork()
no Unix e o comando createProcess() no Windows.
PARA ENCERRAR UM PROCESSO
Um processo é encerrado quando termina a execução do seu último comando,
utilizando a chamada de sistema exit() e ele pode retornar ao processo pai um valor de
status por meio da chamada de sistema wait(). Depois de encerrado, todos os recursos
do processo são desalocados pelo sistema operacional.
O processo pai também pode encerrar um processo filho por meio da chamada de
sistema TerminateProcess() no Windows, e para isso é importante que o processo pai
saiba corretamente o pid do seu processo filho. Existem circunstâncias que o processo
pai precisa encerrar o processo filho, como o filho que excedeu os recursos ou o pai
que está sendo encerrado e os processos filhos não podem continuar em execução.
Nesses exemplos o pai deverá encerrar o processo filho.
Em sistemas que não permitem que processos filhos existam quando o processo pai for
encerrado, um fenômeno chamado encerramento em cascata é iniciado pelo sistema
operacional onde todos os processos filhos do processo pai são encerrados também.
Outro comando importante é o wait(), pois com esse comando o pai tem que esperar
os processos filhos serem encerrados para só depois ser encerrado. Com isso,
processos zumbis, que são processos filhos sem um processo pai, não existirão.
COMUNICAÇÃO INTER PROCESSOS
Os processos que são executados concorrentemente no sistema operacional podem
ser processos independentes ou processos cooperativos. Um processo é independente
quando não pode afetar outros processos em execução no sistema, nem ser afetado
por eles. Qualquer processo que não compartilhe dados com outros processos é
independente. Um processo é cooperativo quando pode afetar outros processos em
execução no sistema ou pode ser afetado por eles. É claro que qualquer processo que
compartilhe dados com outros processos é um processo cooperativo.
Existem muitas razões para o fornecimento de um ambiente que permita a cooperação
entre processos, tais como o compartilhamento de informações, aumento da
velocidade de computação, modularidade, conveniência.
Como vários podem estar interessados em um mesmo bloco de informações deve-se
fornecer um ambiente que permita o acesso concorrente a tais informações.
Processos cooperativos requerem um mecanismo de comunicação entre processos
(IPC) que permita a troca de dados e informações. Existem dois modelos básicos de
comunicação entre processos: memória compartilhada e transmissão de mensagens.
No modelo de memória compartilhada, é estabelecida uma região da memória que é
compartilhada por processos cooperativos.
Na transmissão de mensagens, a comunicação acontece através de mensagens
trocadas entre os sistemas cooperativos.
SISTEMA DE MEMÓRIA COMPARTILHADA
A memória compartilhada exige que os processos em comunicação estabeleçam uma
região de memória compartilhada. Geralmente, essa região se localiza no espaço de
endereçamento do processo que cria o segmento de memória compartilhada. Para que
outros processos se comuniquem usando esse segmento, é exigido que esse processo
anexe o segmento de memória compartilhada ao seu espaço de endereçamento.
Podemos usar dois tipos de buffers; o ilimitado e o limitado. O ilimitado não determina
um limite ao seu tamanho. O consumidor pode ter que esperar por novos itens, porém
o produtor pode sempre produzir novos itens. Diferentemente do buffer ilimitado o
buffer limitado mantém um tamanho fixo. Assim, o consumidor deve aguardar até que
o buffer esteja vazio, e o produtor deve esperar até que o buffer esteja cheio.
Para termos um melhor entendimento sobre os processos cooperativos, podemos
considerar um problema produtor-consumidor que é muito usado em processos
cooperativos. Um processo produtos cria informações que são consumidas por um
processo consumidor. Digamos que um compilador produz um código de montagem
que é consumido por um montador. O montador, pode produzir código de montagem
que será consumido pelo carregador. O problema do produtor-consumidor também
existe uma metáfora útil para o paradigma cliente-servidor. Geralmente, consideramos
um servidor como produtor e um cliente como consumidor.
A solução desse problema produtor-consumidor seria o uso da memória
compartilhada. Para possibilitar que os processos produtores e consumidores sejam
executados concorrentemente, um buffer deve estar disponível para que possa ser
preenchido pelo produtor e esvaziado pelo consumidor.
SISTEMAS DE TRANSMISSÃO DE MENSAGENS
A transmissão de mensagens proporciona um método para permitir que os processos
se comuniquem e sincronizem suas ações sem ter que compartilhar o mesmo espaço
de endereçamento como na memória compartilhada.
É mais conveniente para um ambiente de trabalho, no qual existem computadores em
comunicação, todos conectados por uma rede.
Existem vários métodos para implementar logicamente um link e as operações send() e
receive(), tais como, comunicação direta ou indireta, comunicação síncrona e
assíncrona.
Na comunicação direta, cada processo que quer se comunicar deve nomear
explicitamente o receptor ou o emissor da comunicação. Na comunicação indireta, as
mensagens são enviadas para e recebidas de caixas postais, ou portas. Uma caixa
postal pode ser considerada abstratamente como um objeto no qual mensagens
podem ser inseridas por processos e do qual mensagens podem ser removidas. Cada
caixa postal tem uma identificação exclusiva. Por exemplo, filas de mensagens POSIX
usam um valor inteiro para identificar uma caixa postal. Um processo pode se
comunicar com outro processo por meio de várias caixas postais diferentes, mas dois
processos só podem se comunicar se tiverem uma caixa postal compartilhada.
O armazenamento em buffer independe de a comunicação ser direta ou indireta, as
informações trocadas pelo processo de comunicação residem em uma fila temporária.
Temos três maneiras; capacidade zero, capacidade limitada e ilimitada.
Na capacidade zero, a fila terá um tamanho zero, portanto o link não pode ter
nenhuma mensagem em espera, assim o emissor não pode criar novos itens até que o
consumidor receba a mensagem que estava na fila.
Na capacidade limitada, a fila terá um tamanho “n”, portanto no máximo poderão ter
“n” mensagens na fila. Se a fila estiver cheia quando uma nova mensagem for enviada,
a mensagem é inserida na fila para que o emissor possa continuar a execução sem
interrupções. Porém se o link estiver cheio, o emissor deverá ser bloqueado até que
haja espaço disponível na fila.
Já na capacidade ilimitada como, como o nome indica, a fila tem tamanho infinito,
assim pode ter qualquer número de mensagens em espera. O emissor nunca será
bloqueado.
SINCRONIZAÇÃO
A comunicação entre processos tem lugar por meio de chamadas às primitivas send ( )
e receive ( ). Há diferentes opções de projeto para a implementação de cada primitiva.
Para a transmissão de mensagens podem ser, o envio com bloqueio onde o processo
emissor é bloqueado até que a mensagem seja recebida pela caixa postal. O envio sem
bloqueio, onde o emissor envia a mensagem e continua a operação. O recebimento
com bloqueio onde o receptor é bloqueado até que a mensagem fique disponível, e o
recebimento sem bloqueio onde o receptor recupera uma mensagem válida ou
mensagem nula.
Diferentes combinações de send() e receive() são possíveis. Quando send() e receive()
são com bloqueio, temos um ponto de encontro entre o emissor e o receptor. A
solução para o problema do produtor-consumidor torna-se trivial quando usamos
comandos send ) e receive() com bloqueio. O produtor, simplesmente, invoca a
chamada send() com bloqueio e espera até que a mensagem seja distribuída para o
receptor ou a caixa postal
SISTEMAS IPC
WINDOWS
O Windows é um exemplo de projeto moderno que utiliza a modularidade para
aumentar a funcionalidade e diminuir o tempo necessário à implementação de novos
recursos. O Windows dá suporte para subsistemas. Os programas de aplicação se
comunicam com esses subsistemas por um método de transmissão de mensagens.
O tipo de transmissão de mensagens do Windows é chamado de (advanced pocedure
call – APLC) ele é usado na comunicação entre dois processos em um mesmo sistema
computacional. É análogo ao método chamado de (RPC – remote procedure call) que é
vastamente utilizada, contudo melhorado e específico para Windows.
COMUNICAÇÃO EM SISTEMAS CONSUMIDOR-SERVIDOR
As técnicas de memória compartilhada e transmissão de mensagens podem ser
utilizadas para a comunicação em sistemas consumidor-servidor. O servidor espera
receber a solicitação do consumidor ouvindo uma porta especificada. Depois de que a
solicitação foi recebida o servidor aceita uma conexão que socket do consumidor
forneceu para completá-la. Servidores que geram serviços específicos (como telnet,
FTP e HTTP) ouvem em portas bem conhecidas (um servidor telnet ouve na porta 23,
um servidor FTP ouve na porta 21, e um servidor web, ou HTTP, ouve na porta 80).
Todas as portas abaixo de 1024 são consideradas bem conhecidas, e podemos utilizá-
las para estabelecer serviços-padrão.
Quando um processo consumidor inicia um pedido de conexão, uma porta é destinada
a ele pelo computador hospedeiro. Essa porta tem algum número aleatório maior do
que 1024.
A comunicação com o uso de sockets, apesar de ser comum e eficiente, é considerada
uma forma de comunicação de baixo nível entre processos distribuídos. É de
responsabilidade da aplicação consumidor estabelecer uma estrutura de dados.
PIPES
Os pipes fornecem maneiras relativamente simples para que os processos se
comuniquem uns com os outros. Os pipes comuns permitem a comunicação entre
processos-pais e processos-filhos, enquanto os pipes nomeados permitem que
processos não relacionados se comuniquem.
Pipes comuns possibilitam que dois processos se comuniquem na forma produtor-
consumidor, o produtor grava em uma extremidade do pipe e o consumidor lê a outra
extremidade. Como consequência, os pipes comuns são unidirecionais, possibilitando apenas a
comunicação em uma direção.
Pipes nomeados proveem uma ferramenta de comunicação extremamente mais
poderosa do que os pipes comuns. Com a comunicação podendo ser bidirecional, e
nenhum relacionamento pai-filho exigido. Após o pipe nomeado ser imposto, vários
processos podem utilizá-lo para comunicação.
Os pipes nomeados nos sistemas Windows fornecem um método de comunicação mais rico do
que seus equivalentes no UNIX. A comunicação full duplex é possibilitada e os processos em
comunicação podem ser localizados na mesma máquina ou em máquinas diferentes.
Complementarmente, apenas dados orientados a bytes podem ser transmitidos por uma FIFO
UNIX, enquanto os sistemas Windows permitem tanto dados orientados a bytes quanto dados
orientados a mensagens.

Você também pode gostar