PROCESSOS EM
WINDOWS E UNIX
Dirlene [Link]
Eduardo Bruno Silva
Resumo
Este artigo tem por objetivo descrever processos, estados e mudanças de
processos, threads, escalonamento de processos, além do tipo de
processamento realizado, com base em sistemas operacionais bem
conhecidos como Windows e Unix.
Abstract
The objective of this article is to describe process, states and process’
changes, threads, process scheduling and even types of process realized,
based in well-known operational systems like Windows and Unix.
Introdução
A finalidade deste trabalho é aprofundar nossos conhecimentos de acordo com o
funcionamento do sistema operacional sob a visão de processos, e comparar a gestão particular
de processos dos sistemas mais conhecidos e utilizados (Windows e Unix).
Este contexto está presente no dia-a-dia da maioria das pessoas, porém poucos
percebem a importância deste acontecimento, embora qualquer pessoa que trabalhe com
computadores sabe que existe algo chamado sistema operacional que controla o equipamento.
Isto se aplica às mais diversas tarefas, desde o computador utilizado em casa ao computador de
grande porte utilizado por bancos e outras grandes empresas.
Para termos uma visão mais periférica deste assunto, trataremos aqui questões
pertinentes ao estudo interno do funcionamento da gerência de processos do sistema
operacional, suas características teóricas e funcionais, além da exemplificação de como isto
ocorre.
Conteúdo do Artigo
1. Processos e Threads
Tratando-se do estudo de Sistemas Operacionais nenhum conceito é tão importante
quanto à definição de processo, porém é fundamental termos a noção da diferenciação entre
processo e programa. O próprio conceito de processo é abstrato e causa divergências, pois não
existe uma definição precisa aceita por todos. Sendo assim, definiremos aqui processo como
uma instância de um programa em execução e programa como conjunto de instruções
necessárias à execução das operações desejadas. Desse modo, o programa é passivo dentro do
sistema, ou seja, ele não muda seu estado, diferentemente do processo que é um elemento ativo
e altera seus estados à medida que executa um programa.
Um processo reúne uma série de atributos (espaço de endereçamento, permissões de
acesso, quotas e etc.) e áreas (de código, dados e pilha de execução). Para cada processo temos
também ao menos um fluxo de execução, também chamado de thread. Nos sistemas
operacionais modernos existe a idéia de multithreading, ou seja, vários fluxos de execução
associados internamente a um único processo, compartilhando entre elas os recursos do
processo. Devido a essa característica a gerência das threads é muito mais simples tornando o
próprio processamento mais leve, pois o chaveamento entre as threads de um mesmo processo
é muito mais rápido do que o chaveamento entre processos diferentes.
2. Estados de Processos
Cada processo passa por diferentes momentos. A partir destes momentos podemos
estabelecer diferentes possíveis estados para um processo, que mudam de acordo com as
chamadas do sistema. Os estados em que um processo pode se encontrar variam de acordo com
cada sistema, mas de forma geral são classificados em pronto, executando e bloqueado.
Quando um processo é criado ele está pronto para ser executado pelo processador, no
entanto, existe uma fila de processos, chamada “fila de aptos” (ready queue), esperando para
serem executados, pois o processador executa apenas um processo por vez. Como cada
processo utiliza o processador por apenas alguns milisegundos, têm-se a impressão de que o
processador executa vários processos ao mesmo tempo.
A mudança de qualquer estado de qualquer processo é iniciada por um evento. Esse
evento aciona o sistema operacional que então altera os estados de um ou mais processos.
Quando um processo que está no estado executando sofre uma mudança de estado o
processador fica livre e o sistema operacional seleciona um processo da lista de aptos para
assumir o processador. O processo selecionado passa do estado apto para o estado executando.
A parte do sistema operacional que faz essa seleção é chamada de escalonador.
Durante o estado executando, um processo pode fazer chamadas de sistema. Até a
chamada de sistema ser atendida, o processo não pode continuar sua execução. A chamada de
sistema é um evento disparado pelo próprio processo. Neste momento o processo sofre outra
mudança de estado, passando do estado executando para o estado bloqueado. Ele só volta ao
estado pronto após a conclusão da chamada. Alguns processos que estão no estado bloqueado
podem estar esperando por interrupções de hardware. Geralmente elas informam o término de
uma operação de E/S, isso significa que um processo bloqueado será liberado.
3. Gerenciamento de Processos
Em todos os sistemas multiprogramados, vários processos disputam o processador. A
cada momento em que o processador for desocupado, um dos processos que estão na lista de
prontos irá utilizar o processador. A atividade de tomar a decisão de qual será o próximo
processo que utilizará o processador é chamada escalonamento. É necessário dividir o tempo
do processador entre os processos do sistema. Isso é feito de duas maneiras diferentes:
escalonamento a curto prazo e escalonamento a longo prazo.
No escalonamento a curto prazo é decidido qual o processo que deverá ser executado a
seguir, assim que o processador ficar livre. Este escalonador é executado com muita
freqüência, portanto, deve ser rápido.
Em alguns sistemas, os processos nem sempre são criados no momento da solicitação.
Em ambientes tipo batch, a criação de um processo pode ser adiada caso a máquina esteja
sobrecarregada. Cabe ao escalonador a longo prazo decidir quando um processo solicitado
deve ser criado.
É possível acontecer um esgotamento dos recursos de memória disponíveis, ou seja, a
memória necessária para os processos em execução torna-se maior do que a memória
disponível. Para solucionar este problema, criou-se uma técnica chamada swapping. São duas
as operações de swap: swap-in e swap-out. Na operação de swap-out, a execução de um
processo é suspensa, e o seu código é copiado para o disco rígido. A operação swap-in faz o
contrário, ou seja, copia o processo de volta do disco para a memória e sua execução é
retornada do ponto em que havia sido suspensa. O escalonador que faz as decisões de swap é
chamado de escalonador de médio prazo.
Uma complicação enfrentada pelos escalonadores é a de que cada processo tem um
comportamento único e imprevisível. Quando o escalonador coloca um processo para rodar, ele
nunca sabe ao certo quanto tempo o processo vai levar até ser bloqueado. Para assegurar que
nenhum processo rode por um tempo muito grande, os sistemas operacionais modernos se
utilizam de um relógio interno, que periodicamente gera um sinal de interrupção (interrupção
de tempo). A cada interrupção destas o escalonador decide se o processo corrente deve
continuar executando ou ceder o lugar a um outro processo. A esta estratégia de permitir a
suspensão temporária de processos que poderiam continuar executando é dado o nome de
escalonamento preemptivo, que contrasta com a antiga estratégia de rodar até o fim do
processo, presentes nos primeiros sistemas operacionais, conhecida como escalonamento não-
preemptivo.
Para a tomada de decisão, todos os escalonadores possuem um determinado algoritmo
que devem seguir. Aqui apresentaremos alguns destes algoritmos:
Ordem de Chegada (First-in first-out): É o algoritmo mais simples. A fila do
processador é uma fila simples e os processos são executados na mesma ordem
em que chegaram na fila.
Processo mais curto primeiro (Shortest Job First): Este algoritmo de
escalonamento selecionará para a execução o processo que, entre os elementos
da fila, venha a ocupar o processador pelo menor período de tempo antes de ser
novamente bloqueado ou terminado.
Prioridade: No tipo de escalonamento baseado em prioridade, para cada
processo é associado um número inteiro que representa seu nível de prioridade.
Quando for necessária a seleção de um processo, será selecionado o processo
que tiver o maior nível de prioridade.
Algoritmo Circular (Round Robin): Neste algoritmo, os processos prontos para
rodar tem seus blocos de controle armazenados em uma lista circular, sendo
selecionado para execução o primeiro processo da lista. A partir daí o processo é
executado até que seja bloqueado ou termine o seu tempo de execução, e a partir
daí o ciclo continua no próximo processo.
Múltiplas Listas: Em um mesmo sistema existem vários tipos de processo.
Neste tipo de algoritmo, existe uma lista para cada tipo de processo. Cada lista
segue seu próprio algoritmo, de acordo com a característica dos processos.
Estes são apenas alguns dos tipos de escalonamento existentes. Cada escalonador
possui suas próprias características definidas.
4. Windows
O Windows possui duas classes de sistemas operacionais distintas: aquelas versões
destinadas a servidores e as versões dedicadas aos usuários finais. As versões dedicadas aos
usuários finais não são exatamente multiprogramadas como o UNIX, ou seja, não podem
suportar diversos processos independentes ao mesmo tempo, embora não sejam
monoprogramadas na acepção da palavra. Portanto, iremos nos focar na versão Windows NT,
pois esta é dedicada a servidores, facilitando a comparação com o UNIX.
Um processo neste sistema operacional envolve um endereçamento virtual, recursos de
sistema, e pelo menos uma thread. Existe um gerente de objeto que é o responsável pela
criação e destruição dos processos. A execução de cada processo é controlada individualmente
pelo conteúdo armazenado pelo thread. Multithreads são utilizados para a implementação de
servidores, onde os threads ocupam o mesmo espaço de endereçamento virtual e todos os
recursos do processo, processando concorrentemente as solicitações de múltiplos clientes.
O escalonador deste sistema escalona apenas threads para execução, logo cada processo
deve ter pelo menos uma thread antes de ser executado. Assim como os processos, no
Windows NT as threads são implementadas como objetos, criadas e destruídas pelo
gerenciador de objetos.
4.1.1. Estados de processos
No Windows, durante a existência de um processo, ele passa por uma série de
mudanças, caracterizando vários estados específicos do sistema:
Estado de Pronto: Um processo está neste estado quando está aguardando uma
vaga no processador para ser executado. Quando os processos são criados eles
estão neste estado, e passam novamente a este estado quando perdem o
processador por time-out ou quando precisam ser bloqueados para esperar uma
operação de E/S, por exemplo.
Estado de Standby: Quando um processo se encontra neste estado ele já foi
selecionado para a execução, mas ainda precisa esperar uma troca de contexto
para ganhar o espaço do processador.
Estado de Execução: É quando o processo já está em fase de execução no
processador.
Estado de Espera: Neste estado o processo depende de algum evento para
continuar a ser executado e voltar para o estado de pronto, como por exemplo,
E/S.
Estado de Transição: É necessário algum recurso do sistema para que este
processo, que se encontra em um estado semelhante ao de espera, volte ao
estado de pronto.
Estado de Terminado: É o estado final de um processo ou thread. Quando
terminada sua execução, o objeto pode ser excluído ou não.
4.1.2. Gerenciamento de processos
Como sabemos, a unidade de execução do Windows é a thread. Cada processo do
Windows pode ser formado por um conjunto de threads, mas ele sempre tem pelo menos uma
thread, chamada de thread primária, que é criada quando o processo é carregado para a
execução.
No Windows, assim como no UNIX, o escalonador utiliza-se de múltiplas filas, os
processos interativos (I/O bound) possuem prioridades sobre os CPU bound e o escalonamento
é baseado no sistema de prioridades. Até então os dois sistemas se equivalem. No entanto, seus
sistemas de prioridades são completamente diferentes. No Windows, cada thread possui uma
prioridade que varia de 0 a 31, sendo a prioridade 31 como a mais alta.
A prioridade 0 é atribuída a uma thread especial, chamada zero thread, que é
responsável por zerar as páginas livres no sistema. Esta é a única thread capaz de receber tal
prioridade. As outras prioridades definem duas classes de threads: as threads em tempo real (de
forma semelhante aos processos de tempo real discutidos em breve no UNIX), que recebem as
prioridades de 16 a 31, e as threads normais, que recebem as prioridades de 1 a 15. Existe uma
outra classe de threads chamada idle, que possui a mais baixa prioridade, e só são executadas
quando não existem outras threads aptas.
O escalonador por sua vez, sempre escolhe a thread com a maior prioridade. As threads
em tempo real sempre são executadas até terminarem sua execução ou serem bloqueadas. As
outras threads, normais, recebem fatias de tempo, que não passam de algumas dezenas de
milisegundos. Cada thread recebe uma prioridade base ao ser criada, e para as threads em
tempo real a prioridade jamais se altera. Processos com prioridade entre 0 e 15 têm sua
prioridade ajustada dinamicamente em tempo de execução. Processos que retornam de
operações de E/S recebem um aumento na prioridade que varia de acordo com o periférico
utilizado. Após operações de sincronização as threads também podem receber aumento de
prioridade.
5. UNIX
O UNIX utiliza processos como abstrações de programas em execução. É o objeto pelo
qual a utilização de memória, recursos de E/S e tempo de processador podem ser monitorados.
No UNIX os processos são criados através da chamada de sistema conhecida como Fork, cuja
execução cria um processo idêntico àquele que o chamou. Após o Fork, o processo pai
continua rodando em paralelo com o filho. Tanto os processos pais quanto filhos podem
executar outras chamadas Fork formando uma árvore de processos. Embora exista um
relacionamento entre os processos, cada sub-processo (processos filhos) possui seu próprio
espaço de endereçamento, ou seja, as variáveis do processo pai não são visíveis por seus filhos.
Cada processo do sistema possui uma série de identificações. São elas:
PID (Process Identification): número único que identifica um processo.
PPID (Parent Process Identification): número de identificação do processo pai.
UID (User Identification): número de identificação do usuário que criou o
processo.
GID (Group Identification): número do grupo do dono do processo.
O processo 0 (zero) é o único processo que não possui pai, sendo criado pelo próprio
sistema na sua inicialização. O processo 1 é criado pelo processo 0 e é chamado init. Este
último é o ancestral de todos os outros processos do sistema. Cada processo recebe uma
prioridade ajustada dinamicamente pelo kernel escalonados de acordo com a sua prioridade.
5.1.1. Estados de processos
Estado Executável: É quando o processo está pronto para ser executado pelo
processador. Neste estado ele já tem todos os recursos de que necessita e
aguarda para processar seus dados.
Estado Dormente: Quando neste estado, os processos ficam essencialmente
bloqueados, pois estão esperando que ocorra um evento específico, como uma
solicitação para ler parte de um arquivo. A menos que recebam um sinal, não
obterão qualquer tempo do processador.
Estado Zumbi: Se um programa cria um processo e demora para consultar seu
resultado após seu término, o processo permanece como "zumbi". Geralmente, é
um bug do programa pai do processo. Se existirem muitos processos, pode ser
necessário terminar o programa pai para desocupar a tabela de processos do
kernel [VILELA/TOMAZ].
Estado Parado: Processos parados recebem uma ordem administrativa proibindo
sua execução. Este estado ocorre quando um processo recebe um sinal STOP ou
STPT e são reiniciados com um sinal CONT. É semelhante ao Estado
Dormente, mas ele só volta a ativa quando um outro processo o desperta. Ele
pode ser eliminado neste estado aqui.
5.1.2. Gerenciamento de processos
Processos são divididos em três classes: processos interativos, processos batch e
processos em tempo real. Em cada um, os processos podem ainda ser divididos em I/O bound e
CPU bound de acordo com o tempo que ficam esperando por operações E/S ou utilizando o
processador. O escalonador UNIX não distingue processos interativos de processos batch,
diferenciando-os apenas dos processos em tempo real, além de privilegiar os processos I/O
bound em relação aos CPU bound de forma a oferecer um melhor tempo de resposta para as
aplicações interativas.
No UNIX, o escalonador é baseado em time-sharing, ou seja, o tempo do processador é
dividido em fatias de tempo denominadas quantum, as quais são alocadas determinados
processos. Quando o quantum de um processo é esgotado, ele sofre uma interrupção por
tempo, como já vimos o que confere ao UNIX o escalonamento preemptivo.
O processamento do UNIX é dividido em “épocas”. Quando é criado um processo, o
mesmo recebe uma determinada quantidade de quantum calculado no início de uma época.
Processos diferentes tendem a ter diferentes valores de quantum.
O escalonador UNIX tem uma outra característica interessante. O escalonador possui
um esquema de atribuição de prioridades dinâmicas. Ele monitora o comportamento dos
processos e ajusta dinamicamente suas prioridades, buscando uma igualdade de utilização do
processador pelos processos. Os processos que utilizaram o processador por um “longo”
período têm sua prioridade reduzida enquanto os processos que estão a muito tempo sem
utiliza-lo recebem um aumento na prioridade.
Porém, este tipo de prioridade só é utilizado pelos processos interativos e batch. Os
processos em tempo real possuem prioridade estática, variando de 1 a 99. Neste caso, a
prioridade estática é definida por usuários privilegiados do sistema e não é modificada pelo
escalonador. O escalonador executa os processos de prioridade dinâmica apenas quando não há
processos em tempo real para executar, ou seja, a prioridade estática é sempre maior do que a
dinâmica. Existem três políticas para seleção de um processo para execução, que explicaremos
resumidamente:
SCHED_FIFO: Esta política só diz respeito aos processos em tempo real.
Quando é criado, o processo é colocado no fim da fila correspondente à sua
prioridade. Quando um processo deste tipo começa a ser executado pelo
processador, só existem três maneiras para parar de executar o processo. Ou um
processo com prioridade superior torna-se apto a executar, ou ele libera o
processador espontaneamente para um processo com prioridade igual, ou ele é
terminado ou bloqueado por uma operação qualquer de E/S, por exemplo.
SCHED_RR: Esta política é semelhante à anterior, visto que também só diz
respeito aos processos em tempo real, e o processo, no momento de sua criação
é inserido no final da sua fila de prioridade. A diferença diz respeito ao
momento em que os processos são executados. São quatro as situações que
devem ocorrer para parar a execução de um processo. A primeira é quando o
seu quantum termina e neste caso ele volta ao final da sua fila prioridade. As
outras três são idênticas às razões descritas na política acima.
SCHED_OTHER: É a política destinada aos processos interativos e batch. É um
esquema de filas multinível de prioridades dinâmicas com time-sharing.
Como foi dito anteriormente, a criação dos processos no UNIX se dá através de duas
operações, Fork e Exec. Um processo cria uma cópia sua (Fork) e em seguida substitui seu
código por um outro (Exec). No momento da criação, o processo pai (que fez o Fork) cede
metade do seu quantum restante ao processo filho. Isto acontece para evitar que um usuário
crie processos filhos que executem o mesmo código que o processo pai. Sem isto, a cada
criação de um processo filho receberia um quantum integral.
O escalonador UNIX é executado de duas formas. A primeira é a forma direta através
de uma chamada à rotina que implementa o escalonador. A segunda forma, chamada de lazy,
ocorre em três situações.
A primeira destas situações é a interrupção por tempo que faz uma atualização dos
temporizadores e realiza uma contabilização de tempo por processo. A segunda é quando um
processo de prioridade mais alta é desbloqueado pela ocorrência de um evento esperado. A
terceira e última forma de execução do lazy é quando um processo invoca o escalonador
através de uma chamada de sistema do tipo yield. Essa chamada permite que um processo
passe a sua vez a outro processo, o que se faz necessária a execução do escalonador.
Conclusão
Segundo o que foi analisado neste artigo, podemos perceber a grande distinção que há
entre os sistemas operacionais mais utilizados hoje em dia. Mais do que inseridos no dia-a-dia
da maioria das pessoas, os sistemas operacionais, aliados às mais diversas tecnologias
modernas, facilitam e transformam o cotidiano. O propósito desta análise baseou-se em
aprofundamos nossos conhecimentos estabelecendo como via de regra o estudo dos processos e
suas características, de acordo com dois dos sistemas operacionais mais conhecidos e utilizados
atualmente.
Windows e UNIX possuem características comuns se comparados rapidamente, de
forma geral. Porém, pesquisando mais a fundo, podemos perceber tais diferenças como as
encontradas nesse estudo. Essas diferenças vão desde os processos que são administrados de
forma diferente, estados e chamadas dos processos que variam de acordo com o tipo de
execução, e até mesmo o gerenciamento do próprio sistema operacional.
Essas informações são encontradas facilmente em livros e sites direcionados à tais
pesquisas, de forma que, torna-se possível um estudo detalhado do assunto. Entretanto,
algumas questões tornam-se um pouco difíceis por serem complexas em sua própria essência,
como por exemplo, o estudo do gerenciamento dos processos em ambos sistemas operacionais.
Apesar de certas dificuldades de acordo com a compreensão de alguns assuntos, foi
possível esclarecer dúvidas e curiosidades até então pouco discutidas ou pesquisadas, além de
descobertas que anteriormente não haviam sido notadas. Evidentemente, ainda existem dados e
questões a serem discutidos futuramente, mas pode-se concluir, devido à pesquisa de livros não
muito modernos, que este é um assunto que não mudou muito nos últimos tempos, talvez sendo
necessária uma nova maneira de gerenciar processos.
Referências Bibliográficas:
TANENBAUM, Andrew S. Sistemas Operacionais Modernos. Amsterdã, Holanda -
Universidade de Vrige, 1992.
OLIVEIRA, Rômulo Silva de. CARISSIMI, Alexandre da Silva. TOSCANI, Simão Sirineo.
Sistemas Operacionais. 1ª Ed. Porto Alegre/RS – Instituto de Informática da UFRGS, Série
Livros Didáticos número 11, 2001.
ALBUQUERQUE, Fernando. Projeto de Sistemas Operacionais em Linguagem C.
Brasília/DF – Universidade de Brasília. Editora EBRAS, 1990.
GUIMARÃES, Célio Cardoso. Princípios de Sistemas Operacionais. Departamento de
Ciências da Computação – UNICAMP. Editora Campos.
VILELA, Awdrey Vieira. TOMAZ, Webert. Administração de Redes Linux.
[Link]/[Link]?id_ac=1168
[Link]