Sistemas Operacionais: Concorrência

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.

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.