Concorrência em Sistemas Operacionais

Desde a década de 1970, os computadores começaram a permitir que os desenvolvedores e usuários executassem diversos programas de forma simultânea. Essa capacidade gerou a crença popular de que “tudo é executado ao mesmo tempo”, como se todas as centenas de programas pudessem ser acessadas e processadas literalmente ao mesmo tempo.

Apesar de existirem diversos núcleos nos atuais chips de CPU (dual-core, quad-core, octa-core etc.), ainda é não possível que centenas de tarefas sejam literalmente processadas na CPU ao mesmo tempo. Essa ilusão, contudo, originou-se da possibilidade de competição entre os programas pelos recursos do hardware. Essa disputa é conhecida como concorrência.

O que é a concorrência em sistemas operacionais?

O conceito de concorrência pode ser compreendido com a execução simultânea de tarefas em um ambiente de computação. De acordo com Machado e Maia (2014), a concorrência em computadores é o princípio básico para o projeto e a implementação de sistemas multiprogramáveis.

A concorrência é viabilizada pela capacidade do processador de executar instruções ao mesmo tempo em que outras operações, como as de Entrada/Saída (E/S), estão em andamento no sistema computacional.

Nos antigos sistemas monoprogramáveis, os recursos computacionais (processador, memória e E/S) eram utilizados de forma pouco eficiente. O processador permanecia dedicado exclusivamente a uma única tarefa. Quando essa tarefa realizava uma operação de E/S – como uma leitura em disco, por exemplo -, o processador ficava ocioso, resultando em subutilização da capacidade de processamento por longos períodos.

A multiprogramação superou essa limitação ao permitir que vários programas estivessem residentes na memória simultaneamente. Dessa forma, quando um programa solicitava uma operação de E/S e passava para o estado de espera, outros programas podiam utilizar o processador. Isso permite que a Unidade Central de Processamento (UCP) permaneça menos tempo ociosa e que a memória principal seja usada de forma mais eficiente. Surge então a concorrência em sistemas operacionais, pois agora os recursos de CPU, memória e dispositivos de E/S são disputados pelos programas que desejam utilizá-los.

Para exemplificar o principal benefício dos sistemas multiprogramáveis, a Figura 1 mostra dois gráficos bidimensionais que demonstram o que acontece em sistemas monoprogramáveis e multiprogramáveis no cenário onde se deseja executar dois programas.

Figura 1 – Comparação entre o funcionamento de sistemas monoprogramáveis e multiprogramáveis.
Fonte: Machado e Maia (2014, p. 61).

Para gerenciar essa disputa pelos recursos, o sistema utiliza mecanismos que desviam o fluxo normal de execução do programa na CPU. Estes mecanismos são as interrupções e as exceções.

Mecanismos de controle do fluxo: Interrupções e Exceções

Esses mecanismos são implementados como sinais elétricos ou instruções de código que avisam o núcleo de processamento sobre a necessidade de mudança no fluxo normal de execução e a troca de tarefas.

As interrupções são eventos inesperados, geralmente causados por sinais de dispositivos de hardware externos ao processador. Ao final da execução de cada instrução, a Unidade de Controle verifica a ocorrência de alguma interrupção. Se houver, o programa é interrompido, e o controle é desviado para uma rotina de tratamento de interrupção. É essencial que, nesse momento, o sistema preserve o conteúdo de registradores (como o Program Counter – PC, e o Registrador de Status), para que o programa possa ser restaurado e continuar seu processamento posteriormente.

Por outro lado, as exceções são eventos previsíveis que ocorrem em função da execução de instruções do próprio programa (também chamados de eventos síncronos). Pode-se citar como exemplos de exceções os erros em operações aritméticas, como divisão por zero, ou as falhas de página (page fault) em memória virtual. O mecanismo de tratamento de exceções pode, frequentemente, ser escrito pelo programador para evitar o encerramento inesperado do programa pelo próprio sistema operacional.

A Figura 2 apresenta a operação do sistema operacional no caso onde ocorre uma interrupção ou uma exceção. Essa mudança de fluxo obriga o sistema a realizar uma troca de contexto para executar uma rotina de tratamento específica para a interrupção acionada pelo hardware ou pelo próprio programa.

Figura 2 – Exemplificação do que acontece com o sistema operacional quando ocorre uma interrupção ou exceção.
Fonte: Machado e Maia (2014, p. 63).

Otimização das operações de Entrada/Saída

O desempenho do sistema operacional e de todo o computador depende fortemente da eficiência das operações de E/S. Com o advento dos mecanismos de controle de fluxo, ao longo dos anos os SOs evoluíram para otimizar as operações de E/S, de forma a liberar a CPU para realizar outras tarefas e efetivamente permitir a concorrência.

Inicialmente, a comunicação entre a CPU e os periféricos era “controlada por um conjunto de instruções especiais, denominadas instruções de entrada/saída, executadas pelo próprio processador. Essas instruções continham detalhes específicos de cada periférico.” (Machado e Maia, 2014, p. 66). Esse modelo de funcionamento criava uma dependência muito forte entre o processador e os dispositivos de E/S.

Portanto, uma das primeiras modificações foi a simplificação das instruções de E/S por meio dos controladores (interfaces). Estas interfaces liberam o processador da necessidade de se comunicar detalhadamente com os periféricos, pois as especificidades ficam por conta da comunicação entre controlador e hardware, conforme pode-se verificar na Figura 3.

Figura 3 – Os controladores (ou interfaces) servem de comunicação entre a CPU, a memória e os dispositivos de E/S.
Fonte: Machado e Maia (2014, p. 66).

O próximo passo evolutivo era substituir a ineficiente comunicação controlada por programa (busy wait), na qual a CPU ficava testando periodicamente o estado do periférico. Com as interrupções, o controlador avisa o processador sobre o término da operação por meio de interrupções, liberando o processador para processar outras tarefas enquanto isso não acontece.

Segundo Machado e Maia (2014, p. 67), “a operação de E/S controlada por interrupção é muito mais eficiente que a controlada por programa, já que elimina a necessidade de o processador esperar pelo término da operação, além de permitir que várias operações de E/S sejam executadas simultaneamente.” Porém, com o tempo, o grande volume de dados transferidos exigia muitas intervenções do processador, o que reduzia sua eficiência. Para solucionar o problema, foi implementada uma técnica de transferência de dados denominada Direct Memory Access (DMA).

O DMA permite que a transferência de dados entre o controlador do dispositivo e a memória principal ocorra sem a intervenção do processador a todo momento, que fica livre para realizar outras tarefas novamente. Utilizando DMA, um bloco de dados pode ser transferido entre a memória principal e
dispositivos de E/S sem a intervenção do processador, exceto no início e no final da transferência. A Figura 4 apresenta uma imagem didática do diagrama de bloco de conexões dos componentes computacionais.

Figura 4 – Diagrama de Bloco para o controlador DMA.
Fonte: Geeks for Geeks (2025).

A extensão do conceito do DMA possibilitou o surgimento do canal de entrada/saída. O conceito foi introduzido pela IBM no Sistema 7094 e basicamente é um processador com capacidade de executar programas de E/S, permitindo controle total sobre essas operações e mínima intervenção da CPU principal. Na Figura 5 pode-se observar como funciona o canal de E/S.

As instruções de E/S são armazenadas na memória principal pelo processador, porém o canal é responsável pela sua execução. Assim, o processador apenas realiza uma operação de E/S, instruindo o canal para executar um programa localizado na memória (programa de canal).

Figura 5 – Esquema de conexão dos componentes considerando o canal de E/S entre o barramento da CPU, memória e os controladores de E/S.
Fonte: Machado e Maia (2014, p. 68).

Aprimorando a concorrência por meio de técnicas de software

Do ponto de vista do software, três técnicas importantes de gerência de recursos são detalhadas por Machado e Maia (2014), dada a sua contribuição à concorrência. São elas o buffering, o spooling e a reentrância.

A técnica de buffering consiste no uso de uma área na memória principal, denominada de buffer, para
a transferência de dados entre os dispositivos de E/S e a memória, conforme pode-se verificar na Figura 6.

Ela permite que uma operação de leitura transfira o dado primeiramente para a área do buffer, liberando imediatamente o dispositivo de entrada para realizar uma nova leitura. Dessa forma, o buffering permite minimizar o problema da discrepância entre a velocidade do processador e dos dispositivos de E/S. Segundo Machado e Maia (2014, p. 68), “o objetivo principal desta técnica é manter, na maior parte do tempo, processador e dispositivos de E/S ocupados.”

Figura 6 – Desenho esquemático de funcionamento da técnica de buffering.
Fonte: Machado e Maia (2014, p. 69).

A segunda opção é o spooling, que é semelhante à técnica de buffering – armazenamento temporário de dados. Porém, a diferença é que no spooling utiliza-se uma área em disco como um grande buffer para o sistema computacional.

Criada em meados dos anos 50, essa técnica evoluiu das antigas fitas magnéticas para os modernos discos. Ela é amplamente utilizada no gerenciamento de impressão, onde as informações são gravadas em um arquivo de spool no disco, liberando imediatamente o programa para outras atividades. Isso desvincula o programa do dispositivo de impressão e garante um uso eficiente e organizado da impressora pelo sistema operacional. Na Figura 7 pode-se verificar que o arquivo de spool é acessado pelo sistema operacional para manipular dados de impressão

Figura 7 – Desenho esquemático de funcionamento da técnica de spooling.
Fonte: Machado e Maia (2014, p. 70).

Por fim, a terceira técnica é chamada de reentrância e é a capacidade de um código executável ser compartilhado por diversos usuários simultaneamente, exigindo que apenas uma cópia do programa esteja residente na memória principal. Essa funcionalidade é crucial em sistemas multiprogramáveis para utilitários comuns, como editores de texto e compiladores, proporcionando uma utilização mais eficiente da memória.

Na Figura 8 é possível verificar que o código do programa construído de forma reentrante possui apenas uma cópia carregada na memória principal para todos os usuários do sistema. Entretanto, é importante destacar que a área de dados não é compartilhada entre os usuários, de forma que apenas as instruções de execução são partilhadas.

Figura 8 – Desenho esquemático de funcionamento da técnica de reentrância.
Fonte: Machado e Maia (2014, p. 71).

Considerações finais

A ideia da concorrência é muito importante para o funcionamento dos sistemas operacionais modernos, especialmente por conta de sua característica multitarefa e multiprocessadores. Com a evolução da tecnologia e o aumento da demanda por desempenho, a importância de técnicas eficientes de concorrência tende a crescer, tanto na indústria quanto na área acadêmica.

Aproveite o vídeo abaixo para complementar sua leitura. Bons estudos.

Referências

MACHADO, Francis Berenger; MAIA, Luiz Paulo. Arquitetura de sistemas operacionais. 5ª ed. Rio de Janeiro: LTC, 2014.

GEEKSFORGEEKS. Direct Memory Access (DMA) Controller in Computer Architecture. Última atualização: 23 jul. 2025. Disponível em: https://www.geeksforgeeks.org/computer-organization-architecture/direct-memory-access-dma-controller-in-computer-architecture/. Acesso em: 04 out. 2025.