Prévia do material em texto
Lista de SO: 1. Analise as diferenças e semelhanças entre processos, threads de usuário e threads de núcleo. Processos: é todo programa em execução, quando a gente vai executar esse programa ele é dividido em vários processos. Ele é dividido em vários processos para poder aproveitar os diversos recursos que a máquina tem, como por exemplo: memoria, processador, disco rígido, impressora e outros. Os processos precisam se comunicar para saber quem está utilizando o recurso, quando ele vai ser liberado e com isso os processos vão competir por esses recursos. Comparação com a Thread: o processo é mais pesado que a thread, pois ele tem todo o contexto do próprio processo em si para no BCP, além de que seu chaveamento de processo leva mais tempo que a Thread, pois tem muita mais informação para ser salva no BCP antes de fazer a troca. Threads: É uma entidade básica de CPU; Também chamado de processo leve; Elas podem executar mais de uma tarefa de uma vez, ou seja, em um único processo elas processam parte desse processo e com isso ganha velocidade no processamento do processo como um todo; A CPU alternas entre as execuções dos threads dando a impressão que estão sendo executadas em paralelo; Threads possuem pilha de execução aonde armazenam todo o estado antes de trocar de thread; No processo de troca de contexto os threads gravam menos informações no BCP isso ajuda na sua execução; Os threads compartilham o mesmo espaço de endereçamento, sendo assim podem ler ou apagar a pilha de outro thread; As Threads são sempre de um mesmo usuário, elas sempre pertencem ao mesmo processo; Threads de usuário: o processo que faz o gerenciamento dos threads eu não consigo executar um thread do processo 1 e chavear para outro thread do processo 2, pois a lista de cada thread está dentro dos processos. Threads em modo kernel: quem gerencia é o próprio núcleo, ele pode chaviar entre um thread de um processo e chaviar para outro thread de outro processo, visto que a lista dos threads está dentro kernel e com isso eu consigo executar um thread do processo 1 e chaviar para a thread do processo 2. 2. Defina o que é uma aplicação CPU-bound, uma aplicação I/O-bound e uma aplicação memorybound. Comente como as características inerentes a esses tipos de aplicação podem influenciar na implementação e no gerenciamento dos processos e threads. Como essas características podem ser utilizadas para melhorar o desempenho do sistema? CPU-bound: CPU Bound: é quando o processo vai fazer o domínio da CPU, então o seu recurso primordial é a CPU. Aplicação I/O-bound: é um processo que vai dominar I/O, ou seja, é um processo que vai estar fazendo muita entrada e saída e com isso a utilização da CPU é pouca, a utilização de memória também vai ser pouca. Operação que consegue inserir informações ou tirar informação, como por exemplo: gravar ou retirar dados no disco, mandar informações pelo teclado, receber informações pelo monitor. Essa aplicação vai dominar as E/S. Aplicação memorybound: é um processo que vai usar pouca CPU, mas, no entanto, vai utilizar muita memória. Ora ele vai alterar para o uso da CPU (3 a 4%) e a outra porcentagem restante ele vai estar utilizando a memória. Outro indicador que o processo é memorybound é quando o processo esgota o recurso de memória e começa a utilizar o SWAP, visto que ele está sem memória e está escrevendo no disco para fazer essa parte de troca da memória principal com essa participação criada no disco. Quando o processo está utilizando o SWAP a máquina fica lenta e isso é devido ao processo de escrita no disco, pois é uma operação custosa, é um processo lento. Se eu tenho um processo de CPU Bound: e eu tenho outros processos para serem executados, o que vai acontecer com essa fila de processos? Vai ficar parado pois a CPU está ocupada. Um detalhe que devemos observar é que depende do tipo de algoritmo que esta escalonado, se ele for preemptivo ele vai parar o processo e fazer a troca de contexto e com isso muita troca de contexto. Se eu tenho um processo de MemoryBound: nesse caso a CPU vai estar livre, pois são processos que dependem mais de memória, mas teremos quase o mesmo problema da CPU Bound, pois haverá troca, porém, não de contexto e sim troca de informações da memória e se a memória estiver cheia vai começar a fazer SWAP e aí o sistema acaba ficando mais lento em termos de resposta. Se eu tenho processos de I/O Bound na fila: a CPU está livre, a memória está livre, mas, no entanto, eu vou ter diversas interrupções, pois quem vai controlar a E/S é a controladora de disco, a controladora de memória, são os drivers do sistema operacional e assim sempre que tiver uma I/O para ser feita eu vou ter que interromper a CPU para fazer aquela operação de I/O. Ou seja, além de interromper a CPU eu vou te que fazer troca de contexto e vamos ter que parar o processamento toda hora para atender uma requisição de I/O. 3. Considere uma aplicação que possua vários processos, sendo um processo pai e diversos processos filhos. Discuta em que situações esse arranjo possibilita a obtenção de um melhor desempenho. Considere em sua análise dois fatores Diferentes tipos de aplicações: CPU bound, I/O bound e Memory-bound; Utilização de diferentes tipos de sistemas computacionais: uma única CPU com único núcleo, uma CPU com diversos núcleos, diversas CPUs Analise como a combinação entre esses fatores pode influenciar no desempenho da aplicação. CPU Bound, uma única CPU e processos filhos: o que podemos interferir é no escalonamento, podemos contabilizar e organizar a fila em ordem de tamanho crescente. Dessa forma nós conseguimos executar os processos de forma mais rápida. Agora se trocarmos o algoritmo para um preemptivo vai ocorrer muita mudança de contexto e com isso vai levar mais tempo parar que os processos terminem. CPU Bound com mais núcleos: agora eu posso pegar os processos e cada núcleo possui suas determinadas threads desta forma, colocar esses threads desse mesmo processo para ser executada em cada núcleo dessa forma processando mais rapidamente cada processo. Memory Bound com mais núcleos: não vai ajudar muito, pois o problema agora é memória e não processador. Dessa forma quando esse processo ocupar a memória e acabar o tempo dele executar e vamos trocar para outro processo, quando o processo b entrar para processar, antes disso as informações que estão na memória vão para o disco e depois a memória puxa o processo b para executar. Com isso vai sobrecarregando o disco. Se um processo c entrar para executar vai te que gravar as informações da memória e jogar para o disco, depois o processo c vai para a memória, mas dessa vez a memória vai estar cheia e vai começar a fazer SWAP, com esse processo de SWAP vai começar a escrever memorias no disco e desta forma vai sobrecarregando mais o sistema operacional. OBS: se a gente intercalar um processo de memorybound com processo de cpu bound, podemos aliviar o sistema e facilitar no processamento. 4. Considerando a questão 3, inclua agora em sua análise, um estudo de como as diferentes formas de escalonamento poderão influenciar no desempenho. Podemos intercalar com processos de memorybound com processos de CPU bound. Com isso podemos otimizar o processamento de sistema. Outra observação que devemos apontar que é os processos mais demorados, como os de I/O, devemos deixar para o final, pois vão levar mais tempo para serem executados. Se aplicarmos prioridades a prioridade vai ser sempre mais baixa para os processos de I/O, agora se utilizamos quantum cada processo vai receber o mesmo tempo, vai executar e vai acabar. Quando tivermos mais troca de contexto o processo que mais vai acabar sendo prejudicado é de CPU bound, pois é o processo que mais trabalha a CPU em sua execução. 5. Dois modos de operação para o processador podem ser considerados: modo usuário e modo kernel ou supervisor. Discuta o que é possível e o que não é possível fazer em cada um desses modos de operação e porque é necessáriaa existência desses dois modos. MODO USUÁRIO: linguagem de mais alto nível e está do lado do aplicativo, é possível apenas trabalhar com o conjunto de instruções que está do modo usuário, trabalhando com aplicativos que o usuário utiliza, como word, media play, etc. Neste modo não executamos instruções privilegiadas, quando o usuário tenta executar alguma instrução não privilegiada em modo usuário o sistema operacional não deixa executar. MODO KERNEL: linguagem de mais baixo nível na qual o SO tem controle e responsabilidade de gerenciar os recursos do sistema. O modo kernel é a ponte entre o hardware e os softwares, qualquer coisa feita sem o devido cuidado pode provocar um grande problema e ferindo a integridade do sistema. É no modo kernel que conseguimos instalar os drivers dos dispositivos, por exemplo. No modo kernel temos acesso irrestrito ao conjunto de instruções da máquina. É importante a separação dos dois modos para uma melhor proteção do sistema como um todo, mantendo a integridade do sistema. Apenas no modo kernel podemos fazer acessos irrestritos que se não tivermos o devido cuidado podermos afetar o funcionamento da máquina. Cite algumas instruções em que são necessárias trocas do modo usuário para kernel e discuta o porquê essas trocas foram necessárias. Instalação de drive de algum dispositivo, como por exemplo VGA. Nesse exemplo é necessário a troca de usuário pois vamos fazer acesso a ponte entre o hardware e o software e esse contato direto é feito apenas em modo kernel, pois é uma forma do SO proteger melhor o funcionamento do sistema contra qualquer mal-uso do usuário. 6. Liste e discuta os eventos que podem levar um processo a mudar seu estado de: Bloqueado -> pronto: Quando um processo recebe todos recursos necessários para ficar em estado de Pronto. Pronto -> executando: Quando processo já possui todos recursos necessários e requisitos que não o impedem de seguir para Execução. Executando -> bloqueado: O processo está sendo executado, porém é interrompido devido o processo não possuir todos os recursos necessários para sua execução. Com isto, o mesmo vai para Bloqueado até receber todos os recursos necessários para ficar em estado de Pronto. Executando -> pronto: O processo está sendo executado, porém o seu quantum termina e outro processo entra em execução, com isso o primeiro processo volta para a fila de prontos. Outra possibilidade é do processo estar em execução e ser interrompido, devido à chegada de outro processo com maior prioridade, desta forma o primeiro processo vai para fila de prontos e aguarda sua prioridade. 7. Discuta as questões envolvidas com a definição do valor do quantum em um sistema operacional que utiliza Round Robin para escalonamento de seus processos. Round Robin é um algoritmo antigo e preemptivo, entretanto é simples de ser utilizado e programado. Em sua dinâmica, cada processo recebe um tempo de processo, chamado de quantum, em que todos os processos ganham o mesmo valor de quantum para rodarem na CPU. E ao final desse tempo de execução o processo é suspenso e outro processo é colocado em execução. Se o quantum não for suficiente para terminar o processo, o mesmo é realocado para o final da fila de prontos, e aguarda sua vez, e então continuará a partir do momento em que foi preemptado até que todo o processo seja executado. A problemática desse algoritmo é determinar o quantum ideal, pois, se o quantum for pequeno demais ocorrem muitas mudanças de contextos, e com isso acaba diminuindo a eficiência da CPU e se o quantum for muito longo o tempo de resposta é comprometido, sendo que processos mais curtos ficam em execução até o quantum terminar para ocorrer a troca de processos. 8. Como o tipo de utilização do sistema em questão pode influenciar na resposta da questão anterior? Por exemplo, se o sistema for altamente interativo ou se o sistema executar majoritariamente programas CPU-bound a discussão sobre o quantum mais adequado pode ser alterada? Cite outras características que poderiam influenciar na definição do quantum ideal. Altamente interativo: muita mudança de contexto. O quantum mais adequado pode ser alterado, se colocarmos um quantum menor, teremos mais processos sendo executados, porém aumentaremos as trocas de contextos. Será melhor trocarmos mais contextos e atender mais processos, já que o sistema é muito interativo e ele precisa alterar de processo com muita velocidade. Para encontrarmos o quantum ideal podemos ajustar o quantum para o menor tempo necessário para executar por completo um processo. 9. Diferentes algoritmos de escalonamento podem apresentar diferentes objetivos, dentre eles: a. Minimizar tempo de resposta b. Maximizar vazão c. Justiça. Discuta o que significa cada um desses objetivos e como eles podem ser atingidos em um algoritmo de escalonamento. Minimizar tempo de resposta: colocar os processos menores para serem executados primeiro e os maiores lá para o final. Por exemplo, suponha que nós temos processos: maiores, médios e pequenos; dessa forma podemos criar múltiplas threads para esse processo e com isso nós conseguimos minimizar o tempo de resposta. Maximizar vazão: colocar os processos menores para serem executados primeiro e os maiores lá para o final. Justiça: se for um algoritmo baseado em tempo, tem que ser o mesmo tempo de execução para todos os processos que vão executar. Se for a CPU, tem que ser a mesma para todo mundo. Se for espaço de memória, tem que ser igual para todo mundo. É um algoritmo difícil de encaixar, ele não vai conseguir ser justo em tudo e alguém vai acabar sendo prejudicado. 10. Três tipos de estrutura podem ser considerados para o projeto e implementação de um sistema operacional: kernel monolítico, em camadas e cliente-servidor. Analise as três opções e comente as vantagens e desvantagens de cada uma delas. Comente para que tipo de aplicação cada uma delas é adequada. Kernel Monolítico: A arquitetura monolítica pode ser comparada com uma aplicação formada por vários módulos que são compilados separadamente e depois linkados, formando um grande e único programa executável, onde os módulos podem interagir livremente. Em Camadas: Na arquitetura de camadas, o sistema é dividido em níveis sobrepostos. Cada camada oferece um conjunto de funções que podem ser utilizadas apenas pelas camadas superiores. A vantagem da estruturação em camadas é isolar as funções do sistema operacional, facilitando sua manutenção e depuração, além de criar uma hierarquia de níveis de modos de acesso, protegendo as camadas mais internas. Uma desvantagem para o modelo de camadas é o desempenho. Cada nova camada implica em uma mudança no modo de acesso. Cliente-servidor: Sempre que uma aplicação deseja algum serviço, é realizada uma solicitação ao processo responsável. Neste caso, a aplicação que solicita o serviço é chamada de cliente, enquanto o processo que responde à solicitação é chamado de servidor. Um cliente, que pode ser uma aplicação de um usuário ou um outro componente do sistema operacional, solicita um serviço enviando uma mensagem para o servidor. O servidor responde ao cliente através de uma outra mensagem. A utilização deste modelo permite que os servidores executem em modo usuário, ou seja, não tenham acesso direto a certos componentes do sistema. Apenas o núcleo do sistema, responsável pela comunicação entre clientes e servidores, executa no modo kernel. Como conseqüência, se ocorrer um erro em um servidor, este poderá parar, mas o sistema não ficará inteiramente comprometido, aumentando assim a sua disponibilidade. Outra vantagem é que a arquitetura microkernel permite isolar as funções do sistema operacional por diversos processos servidores pequenos e dedicados a serviços específicos, tornado o núcleo menor, mais fácil de depurar e, conseqüentemente, aumentando sua confiabilidade. Na arquitetura microkernel, o sistema operacional passa a ser de mais fácil manutenção, flexível e de maior portabilidade. Apesar de todasas vantagens deste modelo, sua implementação, na prática, é muito difícil. Primeiro existe o problema de desempenho, devido a necessidade de mudança de modo de acesso a cada comunicação entre clientes e servidores. Outro problema é que certas funções do sistema operacional exigem acesso direto ao hardware, como operações de E/S. 11. Pesquise e descreva qual a estrutura utilizada nos sistemas operacionais Linux e Windows. Ambos sistemas operacionais usam um sistema “genérico”, ou seja, nenhum deles foi construído pensando apenas em processos de memorybound, cpu bound ou I/O. Eles foram construídos para conseguirem executar ambos os tipos de processos, porém, os sistemas não conseguem rodar tudo perfeitamente, pois como o próprio nome sugere, é genérico. Ele acaba funcionando na média. A arquitetura do Windowns é fortemente baseada no princípio do micronúcleo, assim cada funcionalidades do sistema é oferecido e gerenciada por um único componente do sistema operacional. Entretanto, ele não é puramente micronúcleo, pois módulos foram do núcleo executam operações em modo kernel. A maioria dos sistemas operacionais UNIX tem seu núcleo organizado seguindo uma estrutura monolítica, que executa uma parte no modo kernel e a outra parte é baseada em micronúcleo, e tem funções reduzidas para poder executar as primitivas de sincronização, em escalonador simples e um mecanismo de comunicação de processo. 12. Pense em uma forma de representar o escalonamento dos processos em um sistema operacional por meio de uma dinâmica realizada pelos alunos da turma. Considere os seguintes casos: a. Todos os processos são apenas CPU bound e não existe preempção. b. Todos os processos são apenas CPU bound e existe preempção. c. Os processos realizam algumas operações de E/S e existe preempção. 13. Explique e diferencie os conceitos: Race Condition, Região Crítica e Exclusão Mútua. Race Condition: é quando os processos acessam os recursos compartilhados concorrentemente, como memórias, arquivos, impressoras, discos e variados. Região Crítica: é a área de um código ou recurso compartilhado que depende expressivamente que o acesso à mesma seja realizado de maneira sequencial. Porém, sabido da possibilidade de condição de corrida, essa região deverá ser tratada de maneira especial de maneira a evitar condições de corrida entre processos. Exclusão Mútua: consiste em métodos que podem ser criados (semáforos, monitores) a fim de evitar o problema de condição de corrida e, de certa maneira, “excluir” a possibilidade de outros processos invadirem uma região crítica, recebendo assim o nome de Exclusão Mútua. 14. Defina um problema que precise de comunicação e sincronismo entre vários processos e proponha uma solução utilizando semáforos, monitores ou passagem de mensagens. Semáforos: é uma variável inteira, não negativa, que só pode ser manipulada por duas instruções (DOWN E UP). Um SEMÁFORO pode ter um valor de 0 quando não há sinal armazenado e um valor positivo referente ao número de sinais armazenados e no final ele faz uma comparação se os sinais armazenados são o mesmo número dos sinais que saíram. Semáforos podem ser usados para resolver o problema na perda dos sinais enviados de sleep e Wake up. Todas as ações feitas no semáforo são ações atômicas. Ex: problemática nos processos de produtor e consumidor. São criados 3 semáforos para resolver o problema: Full: tanto o o consumidor quando o produtor vai utilizado para saber se há espaço para produzir/consumir mais dados. Vazio: Se o processo que consome vê que não tem mais nada pra consumir e vai te que aguardar novos dados para poder consumir ele manda um down no semáforo e acaba dormindo. Já o produtor manda um up e começa a produzir. Mutex: é um semáforo binário na qual apenas um único processo consegue acessar a região critica por vez. Monitores: mecanismos de sincronização de alto nível que torna mais simples o desenvolvimento de aplicações concorrentes é constituído por um Conjunto de procedimentos, variáveis e estruturas de dados agrupados em um único módulo ou pacote; Somente um processo pode estar ativo dentro do monitor em um mesmo instante; outros processos ficam bloqueados até que possam estar ativos no monitor. Diferentemente de um Semáforo em que é trabalhado apenas com um dos dois estados (ou ele está ativo (up) ou inativo (down)). A solução Monitor agrega outras primitivas e algumas estruturas de dados para poder funcionar em conjunto para poder garantir esta exclusão mútua, que ou um processo naquele momento está executando e o outro estará esperando para poder utilizar este recurso. Ou seja, um Monitor é semelhante a um Semáforo, porém o Monitor possui mais recursos do que apenas os estados de Up e Down dos Semáforos Sleep e Wake up. Ou seja, há algumas estruturas para garantir um bom funcionamento daqueles recursos, e também algumas garantias que quando um ou mais processos tentaram acessar aquele recurso, apenas um será o detentor do recurso. E isto é feito por meio de um Monitor. Se já existe um processo que garantiu o acesso ao Monitor, nenhum outro processo terá acesso aquele Monitor até que o mesmo esteja liberado. Na construção dos Monitores (que também dependem da linguagem de programação), é o Compilador que garante a exclusão mútua. Diferente de quando é feita as especificações de um Semáforo, que irá fazer a manipulação da memória de forma direta. Em Monitores (assim como em linguagens como Python e Java) os compiladores também possuem algoritmos que garantem que não haja conflitos em armazenamentos e troca de contextos de variáveis. Sendo assim, quando um Monitor é especificado em uma linguagem de programação, será o compilador responsável pela exclusão mútua dos processos. O código é escrito e determinado o Monitor, porém que irá garantir que o trecho inserido o monitor seja executado na ordem e que dois processos não concorram de maneira simultânea pelo mesmo recurso, será o compilador. Todos os recursos compartilhados entre processos devem estar implementados dentro do Monitor. Ou seja, se é determinado que o processador será um recurso compartilhado, quem fará o controle de como a CPU será acessada e o controle do drive de um periférico como a impressora será o Monitor. Ou seja, todos os recursos que são compartilhados entre os processos estejam dentro do Monitor. Sendo assim, no momento em que dois ou mais processos vão acessar aquele o recurso, o Monitor irá barrar o acesso destes processos, pois já haverá outro processo acessando o recurso. Este procedimento é feito pelo compilador e não pelo programador, que apenas especificou quais são os recursos que serão compartilhados. Limitações de semáforos e monitores: • Ambos são boas soluções somente para CPUs com memória compartilhada. Não são boas soluções para sistemas distribuídos (pois há velocidade de comunicação diferente, então nos sistemas distribuídos, você pode ter um sistema distribuído na mesma rede ou em sistema distribuído em redes separadas. Mas a velocidade de comunicação deste sistema também vai depender também do estado da rede. Então pode haver um processo que irá conseguir enviar um sinal de maneira mais rápida que o outro, e então nestes processos de verificação de quem está usando e de quem não está usando, um simples atraso pode atrapalhar tudo. Então não são uma boa solução para sistemas distribuídos, exatamente por conta do problema de sincronização de informação ou de sinal do momento dos processamentos); • Nenhuma das soluções provê troca de informações entre processo que estão em diferentes máquinas (mais um motivo pelo qual os Semáforos e Monitores não funcionam para sistemas distribuídos, pois podemos ter uma parte do sistema que está executando nesta máquina e parte do sistema sendo executado na máquina de lá, ambas estão na mesma rede, porém não há troca de informações entre estes dois processos para saber o que este está manipulando e o processo que está em outra maquina está manipulando);• Monitores dependem de uma linguagem de programação – poucas linguagens suportam Monitores (Java tem Monitores, há primitivas que estão implementadas nas próprias API da linguagem, consegue utilizar de forma mais tranquila e especificar o que é preciso na construção de Monitores para sistemas operacionais); Passagem: troca de mensagens entre processos rodando em máquinas diferentes. Utiliza-se duas primitivas de chamadas sistemas send e receive Diferente dos monitores e diferente dos semáforos que não apresentam boas soluções para sistemas distribuídos ou para sistemas que rodam em máquinas diferentes. A troca de mensagens ela já é uma boa solução para esse tipo de sistema pois sempre vai vir atrelado a um par de chamadas que é uma mensagem de envio (send) e o receive que é para confirmar que foi recebido a mensagem que foi enviada. Os problemas dessas Soluções: Mensagens são enviadas para/por máquinas conectadas em rede; assim mensagens podem se perder ao longo da transmissão; não tem rede 100%. Em redes existe um conceito que é chamado de pacote de dados, quando as informações são trafegadas na rede entendam de forma grosseira que eles são pacotes, mas existe uma subdivisão sobre isso. Quando você envia um pacote pela rede, este pacote pode ser perdido, e em alguns casos pode ser necessário reenvio desse pacote, ou seja, mandar esse pacote novamente. Em outras situações esse pacote simplesmente ser ignorado, ele não vai fazer falta se ele não chegar. Não é o caso da passagem de mensagens, pois estamos utilizando esse conceito para promover comunicação entre processos que estão rodando em máquinas separadas, portanto irá fazer falta. Um pacote que irá conter um send ou receive ele não pode ser simplesmente descartado, no caso de ser perdido ou ser corrompido. Esse pacote tem que ser retransmitido para exatamente não gerar nenhum conflito nem para quem enviou ou para quem esperava receber. Uma mensagem especial chamada acknowledgement, o procedimento receive envia um acknowledgement para o procedimento send. Se esse acknowledgement não chega, ao procedimento send, esse procedimento retransmite a mensagem já enviada. Momento em que uma máquina vai tentar estabelecer uma comunicação com a outra, ela faz uma apresentação que são os acknowledgements ou ACK. Os sinais via ACK. são sinais de reconhecimento, é uma forma do processo ser checado para ver se pode receber a mensagem de send ou receive. Caso não possa, esse pacote de informação não será perdido, ou caso haja necessidade de retransmissão. Só que se for perdida e não conseguir estabelecer nenhuma comunicação a quem deseja se enviar a mensagem, todas as mensagens que foram enviadas vão continuar sendo perdidas. Para evitar esse problema é necessário apresentação e enviado um sinal de acknowledgement ou ACK e é solicitado ao processo se pode ser enviado uma mensagem ao outro. Pode ser enviado a mensagem quando o ACK for enviado novamente, e nesse momento o processo pode enviar o send ou receive, Para cada par de mensagens enviadas também tem-se uma confirmação, para saber se essa mensagem chegou ou não. Se é enviado um receive e é enviado um acknowledgement para confirmar esse receive e não obtiver acknowledgement de confirmação daquele receive, significa que é preciso retransmitir aquele receive ou aquele send se o ACK não confirmar. Antes de enviar um send ou um receive, a primeira coisa a ser enviada é um ACK, é preciso saber se a fonte com que irá se comunicar ou o destino que vai se comunicar, pode aceitar aquele pacote que vai ser enviado. Se não há confirmação, não tem motivo para ficar reenviando aquela mensagem. Retransmissão de pacote sobrecarrega a rede, quanto menos retransmissão tiver que ser feita, é melhor para a rede. Retransmitir não é uma coisa boa, deve-se sempre enviar informações novas, que não trafegam ainda pela rede. Se for transmitido um sinal de ACK, não será retransmitido sinal até receber a resposta. Será enviado ACK até receber a resposta do ACK enviado inicialmente. Problemas: A mensagem é recebida corretamente, mas o acknowledgement se perde. Quem deveria receber a mensagem, recebeu a mensagem de forma correta mas, no entanto, o sinal de confirmação de que aquela mensagem foi recebida foi perdido. Como é feito para saber se realmente a mensagem foi recebida? Haverá ACK duplicados na rede também. Se o primeiro ACK foi enviado e foi perdido, haverá a necessidade de ser enviado outro para poder confirmar que aquela mensagem foi recebida. O receiver deve ter uma maneira de saber se uma mensagem recebida é uma retransmissão, então cada mensagem enviada pelo send possui uma identificação (sequência de números); assim ao receber uma nova mensagem, o receive verifica essa identificação, se ela for semelhante a de alguma mensagem já recebida, o receive descarta a mensagem! Recebeu a mensagem, mas o ACK não foi confirmado (enviou de volta), vai haver retransmissão. Se houver retransmissão, a mensagem vai estar duplicada, se essa mensagem estiver duplicada, tem que eliminar a cópia/duplicada. Um forma de descobrir se aquela mensagem foi recebida ou não, é exatamente através da sequência de números. Todo pacote que é trafegado na rede, tem uma sequência. Desempenho: copiar mensagens de um processo para o outro é mais lento do que operações com semáforos e monitores; Pois tem outro meio que envolve a troca de informações, tem um rede, tem um meio de transmissão que não é mais local. Os semáforos e monitores, eles são executados e processados de forma local. O fato de eu enviar um recieve ou o fato de eu enviar um send, significa que é uma mensagem que vai trafegar pela rede, e essa mensagem tem que chegar do outro lado e tem que chegar novamente para quem enviou com a confirmação, de quem mandou e quem recebeu. Além disso, tem-se que garantir que não haja roubos dessas mensagens, que essas mensagens não sejam alteradas por outros processos. Tem que autenticar e tem que ter uma segurança de que o processo 1 está enviando para o processo 2 não vai ser modificado e nem vai ser alterada e ou capturada por qualquer outro processo. Mensagens: 15. Suponha que duas threads executam o seguinte código C concorrentemente, acessando as variáveis compartilhadas a, b e c: Inicialização: int a = 4; int b = 0; int c = 0; Thread 1 if (a < 0) { c = b - a; } else { c = b + a; } Thread 2 b = 10; a = -3; Quais são os possíveis valores de c após as duas threads terem finalizado? Assuma que as operações de escrita e leitura das variáveis são atômicas, e que a ordem de execução dos comandos de cada thread é mantida durante o processo de geração do código pelo compilador C.