# data[].attributes.body
Na alocação contígua simples a memória principal é subdividida em duas áreas: uma para o sistema operacional e outra para o programa do usuário

Nesse esquema, o usuário tem controle sobre toda a memória principal, podendo ter acesso a qualquer posição da memória, inclusive a área do sistema operacional.

Para proteger o sistema desse tipo de acesso, que pode ser intencional ou não, alguns sistemas implementam proteção por meio de um registrador que delimita as áreas do sistema operacional e do usuário.

Dessa forma, sempre que um programa faz referência a um endereço na memória, o sistema verifica se o endereço está dentro dos limites permitidos.

Caso não esteja, o programa é cancelado e uma mensagem de erro é gerada, indicando que houve uma violação no acesso à memória principal.

# data[].attributes.body
O swapping é uma técnica aplicada à gerência de memória para programas que esperam por memória livre para serem executados. Nessa situação, o sistema escolhe um processo residente que é transferido da memória principal para a memória secundária (swap out), geralmente o disco. Posteriormente, o processo é carregado de volta da memória secundária para a memória principal (swap in) e pode continuar sua execução como se nada tivesse ocorrido.

O algoritmo de escolha do processo a ser retirado da memória principal deve priorizar aquele com menor chance de ser executado, para evitar o swapping desnecessário de um processo que será executado logo em seguida. Os processos retirados da memória estão, geralmente, no estado de espera, não existindo a possibilidade de um processo no estado de pronto também ser selecionado. No primeiro caso, o processo é dito no estado de espera outswapped e, no segundo caso, no estado de pronto outswapped.

Para que a técnica de swapping seja implementada, é essencial que o sistema ofereça um loader que implemente a relocação dinâmica de programas.A relocação dinâmica é realizada por meio de um registrador especial denominado registrador de relocação. No momento em que o programa é carregado na memória, o registrador recebe o endereço inicial da posição de memória que o programa irá ocupar. Toda vez que ocorrer uma referência a algum endereço, o endereço contido na instrução será somado ao conteúdo do registrador, gerando, assim, o endereço físico. Dessa forma, um programa pode ser carregado em qualquer posição de memória.

# data[].attributes.body
Estática

Na alocação particionada estática ou fixa, a memória é dividida em pedaços de tamanho fixo, chamados partições. O tamanho das partições deve ser estabelecido na fase de iniciação do sistema, e definido em função do tamanho dos programas que executam no ambiente.

Sempre que for necessária a alteração do tamanho de uma partição, o sistema deverá ser desativado e reinicializado com uma nova configuração.

Dinâmica

Na alocação particionada dinâmica ou variável, cada programa utiliza um espaço necessário, tornando essa área sua partição.

Como os programas utilizam apenas o espaço de que necessitam, o problema da fragmentação interna não ocorre no esquema de alocação particionada dinâmica.

# data[].attributes.body
A seguir são apresentados os principais critérios que devem ser considerados em uma política de escalonamento.

Utilização do processador

Na maioria dos sistemas é desejável que o processador permaneça a maior parte do seu tempo ocupado. Uma utilização na faixa de 30% indica um sistema com uma carga de processamento baixa, enquanto na faixa de 90% indica um sistema bastante carregado, próximo da sua capacidade máxima.

Throughput

Throughput representa o número de processos executados em um determinado intervalo de tempo. Quanto maior o throughput, maior o número de tarefas executadas em função do tempo. A maximização do throughput é desejada na maioria dos sistemas.

Tempo de Processador / Tempo de UCP

Tempo de processador ou tempo de UCP é o tempo que um processo leva no estado de exe- cução durante seu processamento. As políticas de escalonamento não influenciam o tempo de processador de um processo, sendo este tempo função apenas do código da aplicação e da entrada de dados.

Tempo de Espera

Tempo de espera é o tempo total que u  processo permanece na fila de pronto durante seu processamento, aguardando para ser executado. A redução do tempo de espera dos processos é desejada pela maioria das políticas de escalonamento.

Tempo de Turnaround

Tempo de turnaround é o tempo que um processo leva desde a sua criação até seu término, levando em consideração todo o tempo gasto na espera para alocação de memória, espera na fila de pronto (tempo de espera), processamento na UCP (tempo de processador) e na fila de espera, como nas operações de E/S. As políticas de escalonamento buscam minimizar o tempo de turnaround.

Tempo de Resposta

Tempo de resposta é o tempo decorrido entre uma requisição ao sistema ou à aplicação e o instante em que a resposta é exibida. Em sistemas interativos, podemos entender como o tempo decorrido entre a última tecla digitada pelo usuário e o início da exibição do resultado no monitor. Em geral, o tempo de resposta não é limitado pela capacidade de processamento do sistema computacional, mas pela velocidade dos dispositivos de E/S. Em sistemas interativos, como aplicações on-line ou acesso à Web, os tempos de resposta devem ser da ordem de poucos segundos.

# data[].attributes.headline
Escalonamentos Não Preemptivos e Preemptivos

# data[].attributes.subject
Escalonamentos Não Preemptivos e Preemptivos

# data[].attributes.body
O escalonamento não preemptivo foi o primeiro tipo de escalonamento implementado nos sis- temas multiprogramáveis, onde predominava tipicamente o processamento batch. Nesse tipo de escalonamento, quando um processo está em execução nenhum evento externo pode ocasionar a perda do uso do processador. O processo somente sai do estado de execução caso termine seu processamento ou execute instruções do próprio código que ocasionem uma mudança para o estado de espera.

No escalonamento preemptivo, o sistema operacional pode interromper um processo em execu- ção e passá-lo para o estado de pronto, com o objetivo de alocar outro processo na UCP. Com o uso da preempção, é possível ao sistema priorizar a execução de processos, como no caso de aplicações de tempo real, em que o fator tempo é crítico. Outro benefício é a possibilidade de implementar políticas de escalonamento que compartilhem o processador de uma maneira mais uniforme, distribuindo de forma balanceada o uso da UCP entre os processos.

# data[].attributes.body
No escalonamento first-in-first-out (FIFO scheduling), também conhecido como first-come-first- served (FCFS scheduling), o processo que chegar primeiro ao estado de pronto é o selecionado para execução. Este algoritmo é bastante simples, sendo necessária apenas uma fila, onde os processos que passam para o estado de pronto entram no seu final e são escalonados quando chegam ao seu início. Quando um processo vai para o estado de espera, o primeiro processo da fila de pronto é escalonado. Todos os processos quando saem do estado de espera entram no final da fila de pronto.

O principal problema é a impossibilidade de prever-se quando um processo terá sua execução iniciada, já que isso varia em função do tempo de execução dos demais processos posicionados à sua frente na fila de pronto. Outro problema neste tipo de escalonamento é que processos CPU-bound levam vantagem no uso do processador sobre processos I/O-bound. No caso de existirem processos I/O-bound mais importantes do que os CPU-bound, não é possível tratar este tipo de diferença.

# data[].attributes.body
No escalonamento shortest-job-first (SJF scheduling), também conhecido como shortest-process- next (SPN scheduling), o algoritmo de escalonamento seleciona o processo que tiver o menor tempo de processador ainda por executar. Dessa forma, o processo em estado de pronto que necessitar de menos tempo de UCP para terminar seu processamento é selecionado para execução.

Um problema existente nesta implementação é não ser possível ao sistema operacional saber quanto tempo um processo permanecerá utilizando a UCP na próxima vez em que for escalonado, contudo é possível prever o tempo com base no seu comportamento passado.

# data[].attributes.body
Um programa no ambiente de memória virtual não faz referência a endereços físicos de memória (endereços reais), mas apenas a endereços virtuais. No momento da execução de uma instrução, o endereço virtual referenciado é traduzido para um endereço físico, pois o processador manipula apenas posições da memória principal. O mecanismo de tradução do endereço virtual para endereço físico é denominado mapeamento.

# data[].attributes.body
O escalonamento cooperativo é uma implementação que busca aumentar o grau de multiprogra- mação em políticas de escalonamentos que não possuam mecanismos de preempção, como o FIFO e o SJF não preemptivo. Neste caso, um processo em execução pode voluntariamente liberar o processador, retornando à fila de pronto e possibilitando que um novo processo seja escalonado, permitindo assim uma melhor distribuição no uso do processador.

A principal característica do escalonamento cooperativo está no fato de a liberação do processador ser uma tarefa realizada exclusivamente pelo processo em execução, que de uma maneira cooperativa libera a UCP para um outro processo. Neste mecanismo, o processo em execução verifica periodicamente uma fila de mensagens para determinar se existem outros processos na fila de pronto.

# data[].attributes.body
O escalonamento circular (round robin scheduling) é um escalonamento do tipo preemptivo, pro- jetado especialmente para sistemas de tempo compartilhado. Esse algoritmo é bastante semelhante ao FIFO, porém quando um processo passa para o estado de execução existe um tempo-limite para o uso contínuo do processador denominado fatia de tempo (time-slice) ou quantum.

No escalonamento circular, toda vez que um processo é escalonado para execução uma nova fatia de tempo é concedida. Caso a fatia de tempo expire, o sistema operacional interrompe o processo em execução, salva seu contexto e direciona-o para o final da fila de pronto. Esse mecanismo é conhecido como preempção por tempo.

O escalonamento é realizado alocando a UCP ao primeiro processo da fila de pronto. O processo permanecerá no estado de execução até que termine seu processamento, voluntariamente passe para o estado de espera ou que sua fatia de tempo expire, sofrendo, neste caso, uma preempção pelo sistema operacional.

# data[].attributes.body
Esse mecanismo, conhecido por mapeamento, permite traduzir um endereço localizado no espaço virtual para um associado no espaço real. Como consequência do mapeamento, um programa não mais precisa estar necessariamente em endereços contíguos na memória principal para ser executado.

A tarefa de tradução de endereços virtuais é realizada por hardware juntamente com o sistema operacional, de forma a não comprometer seu desempenho e torná-lo transparente aos usuários e suas aplicações.

O dispositivo de hardware responsável por esta tradução é conhecido como unidade de gerência de memória (Memory Management Unit - MMU), sendo acionado sempre que se faz referência a um endereço virtual. Depois de traduzido, o endereço real pode ser utilizado pelo processador para o acesso à memória principal.

O mecanismo de tradução se encarrega, então, de manter tabelas de mapeamento exclusivas para cada processo, relacionando os endereços virtuais do processo às suas posições na memória real.

# data[].attributes.body
O escalonamento por prioridades é um escalonamento do tipo preemptivo realizado com base em um valor associado a cada processo denominado prioridade de execução. O processo com maior prioridade no estado de pronto é sempre o escolhido para execução, e processos com valores iguais são escalonados seguindo o critério de FIFO. Neste escalonamento, o conceito de fatia de tempo não existe, consequentemente um processo em execução não pode sofrer preempção por tempo.

No escalonamento por prioridades, a perda do uso do processador só ocorrerá no caso de uma mudança voluntária para o estado de espera ou quando um processo de prioridade maior passa para o estado de pronto. Neste caso, o sistema operacional deverá interromper o processo corrente, salvar seu contexto e colocá-lo no estado de pronto. Esse mecanismo é conhecido como preempção por prioridade. Após isso, o processo de maior prioridade é escalonado.

A preempção por prioridade é implementada através de uma interrupção de clock, gerada em determinados intervalos de tempo, para que a rotina de escalonamento reavalie as prioridades dos processos no estado de pronto. Caso haja processos na fila de pronto com maior prioridade do que o processo em execução, o sistema operacional realiza a preempção.

# data[].attributes.body
O escalonamento circular com prioridades implementa o conceito de fatia de tempo e de prioridade de execução associada a cada processo. Neste tipo de escalonamento, um processo permanece no estado de execução até que termine seu processamento, voluntariamente passe para o estado de espera ou sofra uma preempção por tempo ou prioridade.

O escalonamento circular com prioridades possui duas variações, dependendo se a prioridade é do tipo estática ou dinâmica. No escalonamento circular com prioridades estáticas, a prioridade definida no contexto de software de cada processo permanece inalterada ao longo da sua existência.

No caso do escalonamento circular com prioridades dinâmicas, é possível que a prioridade de um processo seja alterada dinamicamente pelo administrador do sistema ou, em algumas políticas, pelo próprio sistema operacional. O ajuste dinâmico das prioridades dos processos pelo sistema operacional com base no seu comportamento é outro exemplo de um mecanismo adaptativo.

# data[].attributes.body
A memória virtual por paginação é a técnica de gerência de memória em que o espaço de endereçamento virtual e o espaço de endereçamento real são divididos em blocos de mesmo tamanho chamados de páginas. As páginas no espaço virtual são denominadas páginas virtuais, enquanto as páginas no espaço real são chamadas de páginas reais ou frames.

Quando um programa é executado, as páginas virtuais são transferidas da memória secundária para a memória principal e colocadas nos frames. Sempre que um programa fizer referência a um endereço virtual, o mecanismo de mapeamento localizará na ETP da tabela do processo o endereço físico do frame no qual se encontra o endereço real correspondente.

Nessa técnica, o endereço virtual é formado pelo número da página virtual (NPV) e por um deslocamento. O NPV identifica unicamente a página virtual que contém o endereço, funcionando como um índice na tabela de páginas. O deslocamento indica a posição do endereço virtual em relação ao início da página na qual se encontra. O endereço físico é obtido, então, combinando-se o endereço do frame, localizado na tabela de páginas, com o deslocamento, contido no endereço virtual.

# data[].attributes.body
No escalonamento por múltiplas filas (multilevel queue scheduling) existem diversas filas de processos no estado de pronto, cada qual com uma prioridade específica. Os processos são asso- ciados às filas em função de características próprias, como importância para a aplicação, tipo de processamento ou área de mémória necessária.

Como processos possuem características de processamento distintas, é difícil que um único mecanismo de escalonamento seja adequado a todos. A principal vantagem de múltiplas filas é a possibilidade da convivência de mecanismos de escalonamento distintos em um mesmo sistema operacional. Cada fila possui um mecanismo próprio, permitindo que alguns processos sejam escalonados pelo mecanismo FIFO, enquanto outros pelo circular.

Neste mecanismo, o processo não possui prioridade, ficando esta característica associada à fila. O processo em execução sofre preempção caso um outro processo entre em uma fila de maior prioridade. O sistema operacional só pode escalonar processos de uma determinada fila caso todas as outras filas de maior prioridade estejam vazias. Uma boa prática é classificar os processos em função do tipo de processamento realizado e associá-los adequadamente às respectivas filas.

# data[].attributes.body
O mecanismo de memória virtual permite a execução de um programa sem que seu código esteja completamente residido na memória principal. A política de busca de páginas determina quando uma página deve ser carregada para a memória. Basicamente, existem duas estratégias para este propósito: paginação por demanda e paginação antecipada.

Na paginação por demanda (demand paging), as páginas dos processos são transferidas da memória secundária para a principal apenas quando são referenciadas. Este mecanismo é conveniente, na medida em que leva para a memória principal apenas as páginas realmente necessárias à execução do programa. Desse modo, é possível que partes não executadas do programa, como rotinas de tratamento de erros, nunca sejam carregadas para a memória.

Na paginação antecipada (antecipatory paging ou prepaging), o sistema carrega para a memória principal, além da página referenciada, outras páginas que podem ou não ser necessárias ao processo ao longo do seu processamento. Se imaginarmos que o programa está armazenado sequencialmente no disco, existe uma grande economia de tempo em levar um conjunto de páginas da memória secundária, ao contrário de carregar uma de cada vez. Por outro lado, caso o processo não precise das páginas carregadas antecipadamente, o sistema terá perdido tempo e ocupado a memória principal desnecessariamente.

# data[].attributes.headline
Escalonamento por Múltiplas Filas com Realimentação

# data[].attributes.subject
Escalonamento por Múltiplas Filas com Realimentação

# data[].attributes.body
O escalonamento por múltiplas filas com realimentação (multilevel feedback queues scheduling) é semelhante ao escalonamento por múltiplas filas, porém os processos podem trocar de filas durante seu processamento. Sua grande vantagem é permitir ao sistema operacional identificar dinamicamente o comportamento de cada processo, direcionando-o para filas com prioridade de execução e mecanismo de escalonamento mais adequados ao longo de seu processamento.

Esse esquema permite que os processos sejam redirecionados entre as diversas filas, fazendo com que o sistema operacional implemente um mecanismo adaptativo. Os processos não são previamente associados às filas de pronto, e, sim, direcionados pelo sistema para as filas existentes com base no seu comportamento. Um processo, quando criado, entra no final da fila de maior prioridade, porém durante sua execução, a cada preempção por tempo, o processo é redirecionado para uma fila de menor prioridade.

# data[].attributes.headline
Política de Escalonamento em Sistemas de Tempo Compartilhado

# data[].attributes.subject
Política de Escalonamento em Sistemas de Tempo Compartilhado

# data[].attributes.body
Sistemas de tempo compartilhado os usuários interagem com os sistemas interativamente, através de processos interativos, exigindo tempos de resposta baixos. A política de escalonamento para este tipo de sistema deve considerar o compartilhamento dos recursos de forma equitativa para possibilitar o uso balanceado da UCP entre os processos.

Em um processo em que todos os processos são tratados igualmente, processos CPU-bound levam vantagem sobre processos I-O bound no uso do processador. No escalonamento circular com prioridades é possível associar prioridades maiores aos processos I/O-bound, a fim de compensar o excessivo tempo gasto por este tipo de processo no estado de espera.

# data[].attributes.headline
A comunicação entre aplicações concorrentes

# data[].attributes.subject
A comunicação entre aplicações concorrentes

# data[].attributes.body
É comum que os processos de uma aplicação concorrente precisem se comunicar para troca de informações. Esta comunicação pode ser implementada de várias maneiras. Por exemplo, com o uso de memória compartilhada entre os processos ou através da troca de mensagens entre processos em execução.

# data[].attributes.body
Em um ambiente monothread, um processo suporta apenas um programa no seu espaço de endereçamento. Neste ambiente, aplicações concorrentes são implementadas apenas com o uso de múltiplos processos independentes ou subprocessos.

# data[].attributes.body
Um processo é dividido em três partes: contexto de hardware, contexto de software e espaço de endereçamento.

Contexto de Hardware

Armazena o conteúdo dos registradores gerais da UCP, além dos registradores de uso específico, como o PC (Program Counter), o SP (Stack Pointer) e o registrador de status.

Contexto de Software

São especificados limites e características dos recursos que podem ser alocados pelo processo. O contexto de software é composto por três grupos de informações sobre o processo: identificação, quotas e privilégios.

Identificação:  Cada processo criado pelo sistema recebe uma identificação única (PID - Process Identification) representada por um número.  O processo também possui a identificação do usuário ou processo que o criou (Owner).  Cada usuário possui uma identificação única no sistema (UID - User Identification), atribuída ao processo no momento de sua criação.

Quotas: São os limites de cada recurso do sistema que um processo pode alocar.

Privilégio: É o total de recursos destinados a um determinado processo.

Espaço de Endereçamento: É a área de memória pertencente ao processo onde instruções e dados do programa são armazenados para execução.

# data[].attributes.body
Execução

Um processo é dito no estado de execução quando está sendo processado pela UCP.

Pronto

Um processo está no estado de pronto quando aguarda apenas para ser executado.

Espera

Um processo no estado de espera aguarda por algum evento externo ou por algum recurso para prosseguir seu processamento.

# data[].attributes.body
A criação de um processo ocorre a partir do momento em que o sistema operacional adiciona um novo PCB à sua estrutura e aloca um espaço de endereçamento na memória para uso. A partir da criação do PCB, o sistema operacional já reconhece a existência do processo, podendo gerenciá-lo e associar programas ao seu contexto para serem executados. No caso da eliminação de um processo, todos os recursos associados ao processo são desalocados e o PCB eliminado pelo sistema operacional.

# data[].attributes.body
Um processo é definido como CPU-bound (ligado à UCP) quando passa a maior parte do tempo no estado de execução, utilizando o processador, ou pronto.

Um processo é classificado como I/O-bound (ligado à E/S) quando passa a maior parte do tempo no estado de espera, pois realiza um elevado número de operações de E/S.

# data[].attributes.body
Um processo foreground é aquele que permite a comunicação direta do usuário com o processo durante o seu processamento.

Um processo background é aquele onde não existe a comunicação com o usuário durante o seu processamento.

# data[].attributes.body
Processos Independentes são processos onde não existe vínculo do processo criado com o seu criador. A criação de um processo independente exige a alocação de um PCB.

Subprocessos são processos criados dentro de uma estrutura hierárquica. Neste modo, o processo criador é denominado processo-pai, enquanto o novo processo é chamado de subprocesso ou processo-filho. O subprocesso, por sua vez, pode criar outras estruturas de subprocessos.

# data[].attributes.body
Sinais é um mecanismo que permite notificar processos de eventos gerados pelo sistema operacional ou por outros processos. O uso de sinais é fundamental para a gerência de processos, além de possibilitar a comunicação e sincronização entre processos.

A geração de um sinal ocorre quando o sistema operacional, a partir da ocorrência de eventos síncronos ou assíncronos, notifica ao processo através de bits de sinalização localizados no seu PCB. Um processo não responde instantaneamente a um sinal. Os sinais ficam pendentes até que o processo seja escalonado, quando então serão tratados.

O tratamento de um sinal é muito semelhante ao mecanismo de interrupções. Quando um sinal é tratado, o contexto do processo é salvo e a execução desviada para um código de tratamento de sinal (signal handler), geralmente no núcleo do sistema. Após a execução do tratador de sinais, o programa pode voltar a ser processado do ponto onde foi interrompido.

# data[].attributes.headline
A comunicação por compartilhamento de memória

# data[].attributes.subject
A comunicação por compartilhamento de memória

# data[].attributes.body
O Compartilhamento de Memória é um mecanismo de comunicação entre processos que usa uma área de memória, um buffer que é compartilhado entre os vários processos de uma aplicação concorrente. Os processos compartilham informações em operações de escrita e leitura através do buffer de memória.

# data[].attributes.body
Em um ambiente multithread, ou seja, com múltiplos threads, não existe a ideia de programas associados a processos, mas, sim, a threads. O processo, neste ambiente, tem pelo menos um thread de execução, mas pode compartilhar o seu espaço de endereçamento com inúmeros outros threads.

# data[].attributes.headline
Política de Escalonamento em Sistemas de Tempo Real

# data[].attributes.subject
Política de Escalonamento em Sistemas de Tempo Real

# data[].attributes.body
O escalonamento em sistemas de tempo real deve levar em consideração a importância relativa de cada tarefa na aplicação. Em função disso, o escalonamento por prioridades é o mais adequado, já que para cada processo uma prioridade é associada em função da importância do processo dentro da aplicação. No escalonamento para sistemas de tempo real não deve existir o conceito de fatia de tempo, e a prioridade de cada processo deve ser estática.

O sistema OpenVMS possui uma faixa de valores de prioridade de 0 a 31, sendo que os valores entre 16 e 31 devem ser utilizados exclusivamente para processos de aplicações em tempo real. Nesta subfaixa, o escalonamento é realizado com base apenas no valor da prioridade do processo, não havendo preempção por tempo.

# data[].attributes.body
A política de alocação de páginas determina quantos frames cada processo pode manter na memória principal. Existem, basicamente, duas alternativas: alocação fixa e alocação variável.

Na política de alocação fixa, cada processo tem um número máximo de frames que pode ser utilizado durante a execução do programa. Caso o número de páginas reais seja insuficiente, uma página do processo deve ser descartada para que uma nova seja carregada. O limite de páginas reais pode ser igual para todos os processos ou definido individualmente. Apesar de parecer justo, alocar o mesmo número de páginas para todos os processos pode não ser uma boa opção, pois a necessidade de memória de cada processo raramente é a mesma. O limite de páginas deve ser definido no momento da criação do processo, com base no tipo da aplicação que será executada. Essa informação faz parte do contexto de software do processo.

Na política de alocação variável, o número máximo de páginas alocadas ao processo pode variar durante sua execução em função de sua taxa de paginação e da ocupação da memória principal. Nesse modelo, processos com elevadas taxas de paginação podem ampliar o limite máximo de frames, a fim de reduzir o número de page faults. Da mesma forma, processos com baixas taxas de paginação podem ter páginas realocadas para outros processos. Este mecanismo, apesar de mais flexível, exige que o sistema operacional monitore constantemente o comportamento dos processos, gerando maior overhead.

# data[].attributes.body
Threads em modo usuário (TMU) são implementados pela aplicação e não pelo sistema operacional. Para isso, deve existir uma biblioteca de rotinas que possibilite à aplicação realizar tarefas como criação/eliminação de threads, troca de mensagens entre threads e uma política de escalonamento. Neste modo, o sistema operacional não sabe da existência de múltiplos threads, sendo responsabilidade exclusiva da aplicação gerenciar e sincronizar os diversos threads existentes.

# data[].attributes.body
Threads em modo kernel (TMK) são implementados diretamente pelo núcleo do sistema operacional, através de chamadas de rotinas do sistema que oferecem todas as funções de gerenciamento e sincronização (Fig. 6.9). O sistema operacional sabe da existência de cada thread e pode escaloná-los individualmente. No caso de múltiplos processadores, os threads de um mesmo processo podem ser executados simultaneamente.

# data[].attributes.body
A arquitetura de threads em modo híbrido combina as vantagens de threads implementados em modo usuário (TMU) e modo kernel (TMK). Um processo pode ter vários TMKs e, por sua vez, um TMK pode ter vários TMUs. O núcleo do sistema reconhece os TMKs e pode escaloná-los individualmente. Um TMU pode ser executado em um TMK, em um determinado momento, e no instante seguinte ser executado em outro.

# data[].attributes.body
A estrutura de dados chamada scheduler activations combina o melhor das arquiteturas modo kernel e modo usuário, realizando a troca de informações do núcleo do sistema com a biblioteca de threads.

# data[].attributes.body
Quando várias threads são executadas em um processo ao mesmo tempo, temos o multithreading. Este modelo de execução de aplicações possibilita que as threads sejam criadas e executadas de forma independente nos processos, mas compartilhando recursos de forma simultânea. No fim, isso garante ainda mais performance para os sistemas.

# data[].attributes.headline
Problemas de compartilhamento de recursos

# data[].attributes.subject
Problemas de compartilhamento de recursos

# data[].attributes.body
Para demonstrar como a sincronização entre processos concorrentes é fundamental para a confiabilidade dos sistemas multiprogramados são apresentados alguns problemas de compartilhamento de recursos.

A primeira situação é o compartilhamento de um arquivo em disco para atualização do saldo de conta corrente.

Como garantir que os processos em execução leia e atualizem o mesmo arquivo de forma correta para evitar dados inconsistentes?

A segunda situação é o compartilhamento de uma variável de memória por dois processos concorrentes.

Como garantir que um processo não altere o valor da variável utilizada pelo outro processo em execução?

Em ambos os casos, em toda a situação em que houver dois ou mais processos compartilhando um recurso, seja um arquivo ou uma área de memória, deve haver um mecanismo de controle para evitar estes conflitos de concorrência. Este mecanismo é conhecido por condição de corrida.

# data[].attributes.body
Sincronização condicional é uma situação em que o acesso ao recurso compartilhado exige a sincronização de processos vinculada a uma condição de acesso. Um recurso pode não se encontrar pronto para uso devido a uma condição específica. Nesse caso, o processo que deseja acessá-lo deverá permanecer bloqueado até que o recurso fique disponível.

# data[].attributes.body
A exclusão mútua é o mecanismo mais simples para evitar compartilhamentos problemáticos. A ideia é impedir que dois ou mais processos acessem um mesmo recurso simultaneamente.

Neste caso, quando um processo estiver usando um recurso, todos os demais processos devem ser colocados no estado em espera. Este acesso exclusivo do recurso pelo processo é chamado exclusão mútua.

A exclusão mútua afeta apenas os processos concorrentes quando eles fazem acesso a um recurso compartilhado. O trecho de código que faz acesso ao recurso compartilhado é chamado região crítica.

# data[].attributes.body
Semáforo é um mecanismo que indica se algum recurso está sendo utilizado. Dessa forma, qualquer tentativa de acessar aquele recurso só estará liberada depois que o semáforo sinalizar. Duas operações básicas podem ser feitas sobre estes semáforos: P (aquisição) e V (liberação).

# data[].attributes.body
O monitor é um mecanismo de mais alto nível cujo objetivo é impedir o acesso concorrente inadequado, obrigando uma thread esperar por outra para terminar o que está fazendo. Ele é um objeto específico que controla esse acesso a um outro objeto principal que precisa da proteção de concorrência.

# data[].attributes.body
Deadlock é a situação em que um processo aguarda por um recurso que nunca estará disponível ou um evento que não ocorrerá. Essa situação é consequência, na maioria das vezes, do compartilhamento de recursos, como dispositivos, arquivos e registros, entre processos concorrentes em que a exclusão mútua é exigida. O problema do deadlock torna-se cada vez mais frequente e crítico na medida em que os sistemas operacionais evoluem no sentido de implementar o paralelismo de forma intensiva e permitir a alocação dinâmica de um número ainda maior de recursos.

# data[].attributes.body
Este mecanismo é chamado de política de substituição de páginas. Uma página real, quando liberada por um processo, está livre para ser utilizada por qualquer outro processo. A partir dessa situação, qualquer estratégia de substituição de páginas deve considerar se uma página foi ou não modificada antes de liberá-la; caso contrário, os dados armazenados na página podem ser perdidos.

A política de substituição de páginas pode ser classificada conforme seu escopo, ou seja, dentre os processos residentes na memória principal quais são candidatos a ter páginas realocadas. Em função desse escopo, a política de substituição pode ser definida como local ou global.

Na política de substituição local, apenas as páginas do processo que gerou o page fault são candidatas a realocação. Nesse modelo, sempre que um processo precisar de uma nova página o sistema deverá selecionar, dentre os frames alocados pelo processo, a página a ser substituída. Os frames dos demais processos não são avaliados para substituição.

Já na política de substituição global, todas as páginas alocadas na memória principal são candidatas à substituição, independente do processo que gerou o page fault. Como qualquer processo pode ser escolhido, é possível que o processo selecionado sofra um aumento na sua taxa de paginação, em função da redução do número de páginas alocadas na memória. Na verdade, nem todas as páginas podem ser candidatas à substituição. Algumas páginas, como as do núcleo do sistema, são marcadas como bloqueadas e não podem ser realocadas.

# data[].attributes.body
Memória virtual por segmentação é a técnica de gerência de memória onde o espaço de endereçamento virtual é dividido em blocos de tamanhos diferentes chamados segmentos. Na técnica de segmentação, um programa é dividido logicamente em sub-rotinas e estruturas de dados, que são alocadas em segmentos na memória principal.

Normalmente, a definição dos segmentos é realizada pelo compilador, a partir do código-fonte do programa, e cada segmento pode representar um procedimento, função, vetor ou pilha. O espaço de endereçamento virtual de um processo possui um número máximo de segmentos que podem existir, onde cada segmento pode variar de tamanho dentro de um limite. O tamanho do segmento pode ser alterado durante a execução do programa, facilitando a implementação de estruturas de dados dinâmicas.

Os segmentos são mapeados através de tabelas de mapeamento de segmentos (TMS), e os endereços são compostos pelo número do segmento virtual (NSV) e por um deslocamento. O NSV identifica unicamente o segmento virtual que contém o endereço, funcionando como um índice na TMS. O deslocamento indica a posição do endereço virtual em relação ao início do segmento no qual se encontra. O endereço físico é obtido, então, combinando-se o endereço do segmento, localizado na TMS, com o deslocamento, contido no endereço virtual.

# data[].attributes.body
Thrashing pode ser definido como a excessiva transferência de páginas/segmentos entre a memória principal e a memória secundária. Esse problema está presente em sistemas que implementam tanto paginação como segmentação.

O thrashing no sistema ocorre quando existem mais processos competindo por memória principal que espaço disponível. Nesse caso, o primeiro passo é a redução do número de páginas/segmentos de cada processo na memória; porém, como já analisamos, esse mecanismo leva ao thrashing do processo. Caso a redução não seja suficiente, o sistema inicia o swapping, retirando processos da memória principal para a memória secundária. Se esse mecanismo for levado ao extremo, o sistema passará mais tempo realizando swapping que atendendo aos processos.