Capitulo 8 Principios Básicos de Sincronização

 

Objectivos

8.1 Interacção de processos

    8.1.1 Secção critica

    8.1.2   Deadlock

8.2 Coordenando processos

8.3 Semaforos

   8.3.1 Principios de operação

     8.3.2 Considerações práticas

            8.3.2.1 Implemento de semáforos

        8.3.2.2 Implementação de semaforos activos e passivos

8.4 Multiprocessadores com memória partilhada

 

 

 

 

Objectivos:

 

 

          A multiprogramação, partilha de tarefas e processamento paralelo tem feito a  cooperação de grupos sequênciais de processos possivel. Operações concorrentes tal como um grupo de processos relacionados permite uma aplicação simples para  aproveitar as vantagens de paralelismo entre o cpu e  dispositivos I/O num uniprocessador mas também em cpu´s multiprocessador. Contudo, a cooperação entre multiplos processos introduz potênciais novos problemas na implementação de software, incluindo congelamento, a existência de secções criticas e a possibilidade de indeterminação na computação.

Estes problemas aumentam quando dois ou mais processos pretendem competir ou partilhar recursos (incluindo informação).

É  importante compreender  o súbito potêncial da interação entre processos antes de tentar desenhar mecanismos para suportar essas interações.

Por isso a primeira parte deste capitulo discute a natureza geral dessas interacções.

O resto foca as ferramentas classicas de sincronização, o semáforo, incluindo técnicas de implementação.

A sincronização é necessária em uniprocessadores , multiprocessadores e redes. Contudo o problema é suficientemente mais complexo do que é primeiramente estudado para o ambiente uniprocessador. Nesta discusão os resultados são generalizados para multiprocessadores

(no final do capitulo) e para redes (no capitulo 17).

As edições de sincronização são dirigidas na ordem em que foram derivadas.

Fazendo isto ajuda a explicar porque existe uma certa esquisitice em alguns aspectos das soluções.

Numa exposição clássica, às vezes uma solução errada é explicada antes de uma solução correcta ser dada para que possamos perceber porque técnicas obvias provam ser inadequadas.

No capitulo 9, a discussão da sincronização é estendida a um elevado nivel  de mecanismos.

 

 

8.1 Interacção de processos

 

Os programas de computador são o conjunto de uma série de algoritmos – procedimentos passo a passo para alcançar algumas tarefas de processamento de informação.

O uso de vários algoritmos para várias computações especificas é denominado de programação, já desde os ultimos 500 anos.

Enquanto linguagens de programação populares tais como o C e C++ tendem geralmente a desprezar a cooperação, a tecnologia do hardware básico foi sistemáticamente em frente no uso de máquinas de computação distribuidas e paralelas.

A pressão económica e os requerimentos das aplicações forçou o uso de hardware paralelo e distribuido.

Isto torna-se visivel em, por exemplo, sistemas contemporânios de administração de informação, ambientes de computação de escritorios e aplicações numéricas. Para ver esta aproximação no trabalho, veja “ In The Cockpit ” : resolvendo um sistema de equações lineares.

As principais barreiras para o uso da cooperação para aplicações deriva do software, como se mostra á frente:

Ø      Não existem linguagens de programação, usadas por programadores de aplicações, que concordem amplamente. Enquanto a ADA ( Linguagem de programação usada pelo Ministério da Defesa dos EUA ) suporta cooperação, o C e o C++ não.

Ø      A tecnologia  dos sistemas de software não convergiram para uma programação padrão para programas cooperantes. A aproximação usada no exemplo em “ In the Cockpit “ é único para este exemplo.

Ø      Os sistemas operativos, normalmente, apenas fornecem os mecanismos minimos para a cooperação, visto que há tantas maneiras de implementá -la e nenhuma delas domina a área.

 

As técnicas aceitáveis para caracterizar  algoritmos cooperantes tem provado ser um sério impedimento para a tecnologia de programação cooperante. O que torna  isto tão dificil para que não possamos formar um modelo de aplicação para uma cooperação simples e proveitosa?

 

 

Resolvendo um sistema de equações lineares          In The Cockpit

 

Os sistemas de equações lineares são muito usados para servir de modelo a problemas desde negócios, economia , investigação de operações, fisica, engenharias entre outras disciplinas.Se nós representarmos um problema com três incognitas, X1 X2  e X3 , conseguimos  determinar valores para X1 X2 e X3 derivando três equações lineares que expliquem relacionamentos entre as três variáveis como mostramos a seguir:

 

 

 

a11x1+a12x2+a13x3=b1

a21x1+a22x2+a23x3=b2

a31x1+a32x2+a33x3=b3

 

Dadas as três equações, podemos usar vários métodos numéricos para encontrar as raizes de x1, x2 e x3. Visto que o nosso objectivo é ilustrar  como a cooperação pode ser aplicada a estes problemas, podemos reescrever as equações da seguinte maneira:

x1=(b1-a12x2-a13x3)/a11

x2=(b2-a21x1-a23x3)/a21

x3=(b3-a31x1-a32x2)/a31

 

De seguida, usamos uma estimativa inicial de x1(1),x2(1) e x3(1) para computar uma segunda estimativa dos três valores x1(2),x2(2) e x3(2). Nós fazemos isto resolvendo as três equações ao mesmo tempo usando a estimativa inicial de x1, x2 e x3. Isto pode ser conseguido criando três processos para resolver equações e começando os três processos ao mesmo tempo. Depois de terminarem os três processos, usando x1(i-1), x2(i-2) e x3(i-3) para produzir novos valores x1i, x2i e x3i, eles tem que sincronizar-se ums com os outros para ver se a computação convergiu para uma estimativa aceitavel. Depois da sincronização, um processo vai substituir os valores nas equações para confirmar o resultado. Se os valores corresponderem a computação está terminada .

 

________________________________________________________________________________________________________________ 

 

 

Ø      Desenvolver um conjunto de processos para que possam implementar cooperativamente uma aplicação é surpreendentemente complexo. Uma reação é a variedade de paradigmas para implementar a cooperação. Outro é  a dificuldade de lidar com as interacções e dependências.

Ø      É especialmente dificil desenvolver software para manejar eficientemente com a confusão dessas interacções  na rede de workstations e paralelo de processadores.

Ø      Mesmo com os mecanismos de alto nivel disponiveis é dificil criar novos programas sem criar também novos problemas de sincronização.

 

Uma computação paralela é feita de multiplas partes independentes que conseguem ser executadas simultâniamente usando uma parte por processo. Em algums casos as diferentes partes são definidas por um programa, mas em maior parte dos casos são definidos por multiplos programas.A cooperação é conseguida através de logica de partilha de memória que é usada  para partilhar informação e para sincronizar os processos que a partilham. O sistema operativo deve fornecer pelo menos a nivel base de mecanismos para suportar a partilha  e a sincronização entre um grupo de processos.

Em systemas de multiprogramação batch, ficheiros dentro do sistema de ficheiros são partilhados. Um processo pode partilhar informação com outro, escrevendo a informação num ficheiro que o outro pode abrir e ler. Muitos estilos de operações simultâneas dos dois processos são quase impossiveis de atingir com este “granulado“ de informação partilhada. O processo tem que conseguir sincronizar usando um mecanismo mais simples que o de partilha de ficheiros.

Os sistemas de partilha de tempo estimulam a necessidade de uma partilha menos “granulada”, visto que um simples utilizador pode ser capaz de criar dois processos e redireccionar a saida do primeiro para a entrada do segundo. Os pipes do Unix implementam este tipo de partilha. Esta capacidade de partilha menos “granulada  assume a existência simultânea de dois processos em comunicação.

Programas cooperativos tem que ser construidos de forma a que os processos possam partilhar informação, no entanto não interferirem um com o outro enquanto estão na parte critica das suas respectivas execuções. Este requesito introduz um problema de sincronização chamada de problema da secção critica.

Um segundo problema é a ocorrência de deadlock´s durante a cooperação de processos. Isto é, dois ou mais processos podem meter-se em situações em que esperam por um recurso que outro processo necessita e que nenhum deles pode prosseguir sem que um deles desista do recurso. Os problemas da secção critica  e dos deadlock´s são discutidos seguidamente.

 

 

8.1.1 Secção critica

 

Suponha uma aplicação que é escrita de forma a que dois processos, p1 e p2, cooperam para aceder a uma variavel interira comum, x. Por exemplo, o processo p1, pode trabalhar com créditos para uma conta, enquanto p2 trabalha com débitos. O proximo esquema de codigo mostra como os processos colocam a variavel balance partilhada:

 

Shared float balance;    /* variavel partilhada */

 

Esquema de código para p1             Esquema de codigo para p2

...                            ...

balance = balance + amount;      balance = balance – amount;

...                            ...

 

Estas instruções de linguagem de alto nivel vão ser compiladas em poucas instruções de máquina, como as que vemos a seguir:

 

load R1,balance                load R1,balance

load R2,amount                  load R2,amount

add R1,R2                       sub R1,R2

store R1,balance                store R1,balance

 

O problema da secção critica pode ocorrer quando os segmentos de código são executados. Suponha que p1 está a correr num uniprocessador quando o intervalo do timer acaba. Em particular, supondo que o processo p1 está a executar a instrução

 

load R2,amount    .

 

Se o escalonador (scheduler) selecionar em seguida o processo p2 para correr e este executar o segmento de instrucção “balance  = balance – amount; “ antes de p1 voltar a correr, então vai existir “ condição de corrida “ entre p1 e p2. Se p1 ganhar a corrida, os valores de R1 e R2 serão somados e a variavel balance não sobre qualquer erro. Se p2 ganhara corrida, vai ler o resultado original de balance, efectuar a subtracção ( balance – amount ) e guardar o valor em balance. Por sua vez p1 que ainda tem o valor antigo de balance vai executar a soma e sobrepôr o resultado ao anterior ficando a operação de subtracção perdida.

O programa que contêm p1 e p2 tem uma secção critica quando há necessidade de partilhar a variável balance. Para p1 a secção critica é quando soma amount a balance. Para p2 é quando subtrai amount a balance. A execução cooperetiva de dois processos não é garantidamente determinada, visto que duas execuções diferentes do mesmo programa pode produzir valores diferentes. Na primeira execução p1 pode ganhar a corrida e na próxima execução pode ser p2 a ganhar, resultando valores diferentes nas variaveis partilhadas. Não é possivel detectar problemas apenas considerando o processo ( ou programa ) p1 ou p2. Os problemas ocorrem devido á partilha, não devido a um erro no código do programa. O problema da secção critica pode ser evitado fazendo com que ambos os processos possam entrar na secção critica desde que o outro não se encontre tambem na sua secção critica.

Como é que dois processos podem cooperar para entrar na secção critica? Num sistema multiprogramado, as interupções causam os problemas, visto que pode fazer com que o escalonador funcione. Por causa das operações gravadas nos estados dos registos implícito com troca de contexto, um processo pode resumir a execução carregando informação inconsistente nos registos. Se o programador se aperceber que a ocorrência de uma interrupção pode levar a resultados errados, ele ou ela, podem fazer com que o programa desligue as interrupções, enquanto se está a processar a secção critica do processo.

A figura 8.1 mostra como, por exemplo, a secção critica pode ser programada

 

 

Figura 8.1

Desligar as interrupções para implementar a secção critica.

 

Share double balance;                          /*  variáveis partilhadas */

 

Programa p1                                       Programa p2

 

disableinterrupts();                              disableinterrupts();

balance=balance+amount;                  balance=balance-amount;

enableinterrupts();                              enableimterrups();

 

 

usando as instruções enableinterrupt e o disableinterrupt para evitar que ambos os processos sejam iniciados ao mesmo tempo nas secções criticas. As interrupções são desligadas quando os processos entram na secção critica  de forma a não serem interrompidos e voltam a ser ligados depois de esta ser executada. Claro que, esta técnica pode provocar erros nos dispositivos de entrada/saida (i/o) visto que podem vir a ser desligados durante muito tempo, consoante os programas. Em particular, imagine que o programa entra num ciclo infinito na sua secção critica. As interrupções iam ficar desligadas permanentemente.

          Uma alternativa para a solução da figura 8.1 não necessita que as interrupções sejam desactivadas, dessa forma o problema de grande/infinitos intervalos de computação é evitado. A ideia é fazer com que os dois processos coordenem as suas acções sobre as variaveis partilhadas para sincronizar os seus progressos. A figura 8.2 usa uma flag (bandeira) partilhada, lock, de forma a que p1 e p2 possam coordenar aos seus acessos a balance. Quando p1 entra na secção critica altera a variavel partilhada lock de forma a prevenir que p2 entre tambem na sua secção critica. O mesmo acontece quando p2 entra na secção critica.

 

Figura 8.2

Secção critica usando lock

 

Shared boolean lock=FALSE;                            /* variáveis partilhadas */

Shares float balance;

 

Programa para p1                                            Programa para p2

 

...                                                                        ...   

/* adquirir o cadeado (lock ) */                       /* adquirir o cadeado (lock ) */

while (lock) (NULL;);                                        while (lock) (NULL;);

lock=TRUE;                                                      lock=TRUE;

/* executar a secção critica */                         /* executar a secção critica */

balance=balance+amount;                              balance=balance-amount;

/* abrir o cadeado (lock) */                             /* abrir o cadeado (lock) */

lock=FALSE;                                                   lock=FALSE;

                                                                     

 

 

          A figura 8.3 ilustra um modelo de execução para dois processos. Suponha que p1 é interrompido durante a execução da frase

 

balance = balance + amount;

 

depois de ter alterado lock para TRUE e p2 começar a ser executado. Então p2 vai esperar num ciclo while enquanto p1 termina e muda lock para FALSE.

 

Figura 8.3

O modelo de execução

 

 

Quando p1 mudar lock para FALSE indicou que p2 já pode continuar, quando chegar a interrupção do relógio e  ser a sua vez de executar.

          Embora o uso de lock para entrar na secção critica, estamos a acrecentar uma nova secção critica relacionada com o teste da variavel lock. O problema pode ocorrer quando acabamos de testar a variavel lock e nos preparamos para alterá-la. Imaginemos que eramos interrompidos depois de testarmos a variável lock. O outo processo ainda ia ver lock=FALSE e entrava na seção critica. Quado fosse a vez do primeiro voltar a correr iria mudar lock=TRUE mas o outro já lá estava dentro. Para evitar isso podemos usar as interrupções mas desta vez por pouco tempo, apenas para correr uma instrução como é mostrado na figura 8.4.

 

Figura 8.4

Manipulação de lock como secção critica

 

enter(lock){                                                               exit(lock){

disableinterrupts();                                                  disableinterrupts();

/* loop enquanto lock=TRUE */                               lock=FALSE;

while(lock){                                                               enableinterrupts();

/* deixa ocorrerem interrupcções */                        }

enableinterrupts();

disableinterrupts();

}

lock=TRUE;

enableinterrupts();

}

 

As interrupções são desligadas por pouco tempo.

 

 

8.1.2   Deadlock

 

Numa situação de deadlock, dois ou mais processos entrão num estado em que ambos seguram um recurso que o outro está a requisitar. Visto que uma operação de requisição bloqueia a ligação até o recurso estar disponivel, nenhum dos processos vai ter o recurso disponivel e vão ficar bloqueados infinitamente.Na sincronização de processos, nós focamos os casos em que o recurso é secção critica ou lock. O capitulo 10 considera o problema geral em que o recurso pode ser qualquer recurso no sistema.

          Considere um caso em que dois processos p1 e p2 manipulam uma lista comum. Uma tarefa na manipulação da lista é o processo acrescentar ou apagar entradas, outra é actualizar uma discrição do programa com o comprimento da lista. Quando uma operação de apagar ou acrescentar ocorre, o comprimento tem que ser actualizado. Podemos tentar a aproximação na figura 8.5 mas vamos ver mais tarde que não chega para garantir o bom funcionamento do programa.

 

Figura 8.5

Multiplas variaveis partilhadas com sa interrupções desligadas

 

boolean lock1=FALSE                                     /* variaveis partilhadas */

boolean lock2=FALSE

shared list L;

 

Programa para p1                                    Programa para p2

 

...                                                                        ...

/* Entrar na secção critica para                       /* Entrar na secção critica para

apagar um elemento da lista */                       actualizar o comprimento */

enter(lock1);                                                     enter(lock2);

<Apagar elemento>;                                         <Actualizar comprimento>;

/* Sair da secção critica  */                              /* Sair da secção critica */

exit(lock1);                                                        exit(lock2);

<Computação intermédia>;                               <Computação intermédia>;

/* Entrar na secção critica para                         /*Entrar na secção critica para

actualizar o comprimento */                              acrescentrar um elemento à lista*/

enter(lock2);                                                      enter(lock1);

<Actualizar comprimento>;                               <Adicionar elemento>;

/* Sair da secção critica  */                               /* Sair da secção critica */

exit(lock2);                                                         exit(lock1);

                                                                      

 

 

          Se os processos p1 e p2 são executados ao mesmo tempo num systema multiprogramado, uma interrupção do relógio pode ocorrer depois de p1 apagar um elemento mas antes de ter actualizado o comprimento, então os dados na descrição não são iguais aos dados reais. O processo deve executar as duas operações ou então nenhuma. Para não ocorrerem estes erros vamos modificar o nosso programa, como mostra a figura 8.6.

           Quando p1 entra na secção critica para manipular a lista  muda lock1 o que previne p2 de entrar na sua secção critica. Depois de executada a secção critica volta a mudar lock1 para o seu valor inicial. O mesmo acontece se for p2 a entrar primeiro na secção critica.

 

Figura 8.6

Garantindo que os valores relatados são correctos

 

boolean lock1=FALSE                                     /* variaveis partilhadas */

boolean lock2=FALSE

shared list i,j;

 

Programa para p1                                            Programa para p2

 

...                                                                      ...

/* Entrar na secção critica                              /* Entrar na secção critica

da lista */                                                       da descrição */

enter(lock1);                                                   enter(lock2);

<Apagar elemento>;                                       <Actualizar comprimento>;

<Computação intermédia>;                            <Computação intermédia>;

/* Entrar na secçãp critica                            /*Entrar na secção critica

da descrição */                                              para i */

enter(lock2);                                                   enter(lock1);

<Actualizar comprimento>;                            <Adicionar elemento>;

/* Sair das secções criticas  */                     /* Sair das secções criticas */

exit(lock1);                                                     exit(lock2);

exit(lock2);                                                     exit(lock1);

                                                                  

 

 

8.2 Coordenando processos

 

A necessidade de interacções entre processos leva à necessidade de sincronização, e a sincronização leva a problemas com secções criticas e deadlocks. FORK, JOIN e QUIT são introduzidos no capitulo 6, como mecanismos para criar e eliminar processos. Também são usados para sincronizar partes de uma computação cooperativa.

 

 

Figura 8.7

Sincronismo usando fork, join e quit

 

Figura 8.8

Exemplo de processos paralelos

 

shared double x,y;                                       /* variaveis partilhadas */

 

proc_A {                                                       proc_B{

while(TRUE){                                               while(TRUE){

<computa A1>;                                            read(x);     /* consome x */

write(x);    /* produz x */                             <computa B1>;

<computa A2>;                                             write(y);   /* produz y */

read(y);    /* consome y */                            <compute B2>;

}                                                                      }

}                                                                     }

 

 

 

 

 

 

 

8.3 Semaforos

 

 

Um semaforo é um tipo de sistema operativo de informação abstrata que executa operações similares ás funções enter e exit descritas na figura 8.4 . Antes de discutir os principios de funcionamento dos semaforos,esta secção descreve as pretensões dos obstáculos do comportamento do semaforo .

          Como já foi discutido, um processo não pode entrar numa secção critica se outro já se encontra na sua secção critica. Quando um processo está prestes a entrar na sua secção critica, primeiro vai determinar se outro processo já se encontra na sua secção critica. Se já se encontrar algum na secção critica, o segundo processo sincroniza a sua operação  bloqueando-se até que o primeiro processo saia da sua secção critica. Uma solução aceitavel para o problema da secção critica é requerida para encontrar os seguintes obstáculos:

Ø      Apenas um processo pode entrar na sua secção critica de cada vez (exclusão mútua).

Ø      Supondo que a secção critica está livre. Se um grupo de processos indica que quer entrar, apenas esse grupo de processos vai entrar na zona de seleção.

Ø      Quando um processo tenta entrar na sua secção critica, não pode ser adiado indefenidamente mesmo que não exista outro processo na secção critica.

Ø      Depois de um processo requerer entrar na secção critica, apenas um número limitado de processos é permitido entrar na suas secções criticas  antes do processo original entrar na sua secção critica.

 

O problema da secção critica é um problema de software na sua formulação tradicional. Para propósito de discussão, esta secção assume tambem como pontos altos outros aspectos importantes do problema considerando o esqueleto de dois processos mostrados abaixo na figira 8.9. 

 

 

 

 

Figura 8.9

Cooperação de processos

 

proc_0(){                                                                           proc_1(){

while(TRUE) {                                                                   while(TRUE) {

<secção de computação>;                                                <secção de computação>;

<secção critica>;                                                              <secção critica>;

           }                                                                                        }

}                                                                                        }

 

<declarações globais partilhadas>

<processamento inicial>

fork(proc_0, 0);

fork(proc_1, 0);

 

 

          As seguintes pretensões são feitas sobre o esquema do software na figura:

 

1-    Escrever e ler uma celula de memória comum a dois processos é uma operação indivisivel. Qualquer tentativa de dois processos executarem simultâneamente operações de escrita ou leitura resulta em vários ordenamentos desconhecidos, mas as operações não vão ocurrer ao mesmo tempo.

2-    Não é suposto haver prioridades entre os processos no caso de dois ou mais processos quererem entrar na secçáo critica.

3-    As velocidades relativas dos processos são desconhecidas, por isso o mesmo programa pode demorar intervalos de tempo a executar .

4-    Como é indicado na figura 8.9, os processos são supostos serem sequenciais e ciclicos.

 

 

 

 

8.3.1 Principios de operação

 

Um semaforo, s, é uma variavel inteira não negada que só é alterada ou testada por uma de duas rotinas de acesso:

          V(s) : [s=s+1]

          P(s) : [while (s=0) (wait) ; s=s+1]

          Os parêntises rectos que cercam as expressões nas rotinas de acesso indicam que as operações são indivisiveis ou atómicas. Na operação V, o processo que executa a rotina não pode ser interrompido até completar a rotina. A operação P é mais complexa. Se s é maior que 0, é testado e decrementado como uma operação indivisivel. Por outro lado se s é igual a 0, o processo que executa a operação P pode ser interrompido

 

 

 

 

 

 

 

Figura 8.10

Uso de semáforos nas secções criticas

 

 

proc_0() {                                                     proc_1() {

while(TRUE){                                               while(TRUE){

<secção de computação>;                          <secção de computação>;

P(mutex);                                                    P(mutex);

<secção critica>;                                         <secção critica>;

V(mutex);                                                    V(mutex);

}                                                                   }

}                                                                  }

 

semaphore mutex = 1;

fork(proc_0, 0);

fork(proc_1, 0);

 

 

8.3.2 Considerações práticas

 

 

          Existem algumas considerações práticas relacionadas com a implementação do semáforo. O resto desta secção lida com como implementar semáforos e como ver os semaforos como recursos.

 

 

 

8.3.2.1 Implemento de semáforos

 

A figura 8.4 mostra como as interrupções podem ser desligadas para manipular a variavel lock. A mesma técnica pode ser usada para implemtar operações V e P. Visto que P e V são implementados como funções do sistema operativo, o modelo de uma solução assume que os processos do utilizador podem ter um ponteiro para o semáforo, mas o ponteiro é para um objecto no sistema operativo.

          Uma implementação que siga este modelo desliga as interrupções, mas apenas por pequenos periodos de tempo (ver Figura 8.17). Quando um processo bloqueia num semaforo, as interrupções estão ligadas; As interrupções estão desligadas apenas quando é manipulado o valor do semaforo. Isto tem dois efeitos importantes:

Ø      Há efeitos minimos no sistema I/O.

Ø      Enquanto um processo segura um semáforo, previne apenas a entrada de outros processos na secção crítica. Todos os outros processos não são afectados pelo facto do processo estar na secção crítica.

 

Os semáforos podem ser implementados sem desligar as interrupções se o hardware fornecer algumas condições especiais. Uma implementação directa  das instrucções P e V é razoavelmente dificil devido á necessidade de acomodar o ciclo wait na operação P. Em vez disso, uma implementação uniprocessador geralmente quer interagir com o escalonador para a implementação de P, logo uma parte da instrucção vai ser implementada em hardware e outra em software. O sistema operativo pode criar recursos abstratos para cada semáforo. Depois usa um recurso de gestão bloqueia processos que executam operações P como se tivessem executado uma operação de requerimento num recurso convencional. A garantia vem como o recurso de gestão do semaforo consegue implementar simultameamente e correctamente os acessos ao semáforo sem usar as interupções.

          As instrucções TS (test and set) são o caminho dominante para conseguir os efeitos P e V. TS é uma instrução simples que pode de incluido no repertório da máquina sem muito esforço. Mas isto pode fazer a implementação de semaforos muito simples. A local de memória TS, m, ou TS(m), é carregado para um registo do CPU, e na memória é escrito o valor TRUE.

          Para um processo entrar na secção crítica vai ler a memória. Ao comparar o valor de m o processo para  ou segue.

 

 

8.3.2.2 Implementação de semaforos activos e passivos

 

 

Um semaforo pode ser visto como um recurso de consumo. Um processo bloqueia se o valor requisitado a um semaforo não está disponivel. Um processo move-se de um estado de execução para um estado bloqueado executando uma operação P num semaforo de valor zero. O processo passa do estado bloqueado para executavel quando é lido um valor positivo no semaforo; ele decrementa o semaforo enquanto muda de estado. Quando um processo larga um recurso, neste caso executa uma operação V, o alocador de recursos move o primeiro processo de bloqueado para pronto.

 

 

8.4 Multiprocessadores com memória partilhada

 

As secções 8.1 e 8.3 descrevam uma técnica para implementação de semaforos desligando interrupções. Num multiprocessador com memória partilhada, isto é insuficiente, visto que desligando as interrupções num CPU não afecta o comportamento dos outros processadores. Por isso multiprocessadores com memória partilhada comercializados usam instrucções especificas  como TS para implementar semaforos. Em multiprocessadores com partilha de memória, a memória pode ser projectada para suportar TS.