• Nenhum resultado encontrado

4 Modelagem Proposta

4.2 Fluxo de Desenvolvimento Proposto

4.5.1 Programação Concorrente

Para desenvolver uma aplicação paralela ou concorrente é preciso, du- rante a codificação em linguagem de programação, aplicar explicitamente construtores de execução concorrente. Este paradigma, conhecido como paralelismo em nível de thread ou Thread Level Parallelism (TLP) [41], difere do paradigma ILP que explora o paralelismo implícito entre as instruções. Quando bem projetado e adequado ao tipo de aplicação, a execução paralela das operações aumenta o desempenho do sistema, mas necessita da utilização de mecanismos de sincronismo e controle de concorrência para evitar condi- ções de corrida que podem comprometer o comportamento do sistema.

O desenvolvimento de software para plataformas multiprocessadas é su- portado neste trabalho através da interface nativa de programação POSIX [60], que é um padrão IEEE 1003 [62] amplamente adotado pelos principais sistemas operacionais. A escolha desta interface em particular não representa uma limitação, sendo possível a utilização de outras interfaces de programa- ção concorrente que estejam disponíveis nativamente ou na arquitetura alvo. Como foi visto nas seções anteriores deste capítulo, cada módulo de pro- cessador possui o suporte para interface POSIX e durante a inicialização do ambiente de simulação são criadas threads para cada instância da plata- forma. Esta organização simula em alto nível a concorrência existente entre os núcleos de processamento e permite um melhor aproveitamento dos recursos do sistema de desenvolvimento nativo, uma vez que o ambiente de simulação é implementado de forma paralela.

4.5.1.1 Definições da Plataforma

O acesso aos recursos da plataforma pelo software do sistema é feito atra- vés da criação de um cabeçalho da plataforma, contendo todas as inclusões de bibliotecas necessárias, definições e declaração de variáveis. A implemen- tação deste código de cabeçalho deve ser independente da aplicação, po- dendo ser utilizado por outras aplicações que executem nesta mesma plata- forma.

No código fonte 4.7 é fornecido um exemplo de como o código do cabe- çalho da plataforma pode ser implementado. Este arquivo é dividido em duas

partes: arquitetura alvo (linhas 1 a 11) e modelo de sistema nativo proposto (linhas 12 a 23). Em ambos casos são utilizados os mesmos mecanismos para execução multiprocessada, sendo baseada em registradores para identifica- ção do processador (GSMPID) e para controle de acesso de região crítica de memória através de mutex (GSMPMT).

Código Fonte 4.7: Exemplo de cabeçalho de definições da plataforma

1 / / Real Hardware header 2 # i f d e f __REAL_HW__

3 / / Standard I /O include 4 #include < s t d i o . h>

5 / / Shared Memory po int er 6 unsigned i n t ∗ PTR = NULL ; 7 / / SMP r e g i s t e r s

8 # define GSMPMT_BUS_ADDRESS (0 x F F F F F F F 4 ) 9 # define GSMPID_BUS_ADDRESS (0 x F F F F F F F 8 )

10 # define GSMPMT ( ∗ ( ( v o l a t i l e unsigned i n t ∗ ) (GSMPMT_BUS_ADDRESS ) )

11 # define GSMPID ( ∗ ( ( v o l a t i l e unsigned i n t ∗ ) ( GSMPID_BUS_ADDRESS ) )

12 / / HdSC Model header 13 # else

14 / / HdSC Software include 15 #include <software . hpp>

16 / / HdSC wrapping software header f o r code i ns t ru m en ta t io n 17 #include <hsc_wrapper . hpp>

18 / / Functions encapsulation

19 # define lock_item ( . . . ) __ENCAP__ ( lock_item , __VA_ARGS__ ) 20 # define unlock_item ( . . . ) __ENCAP__ ( unlock_item , __VA_ARGS__ ) 21 # define producer ( . . . ) __ENCAP__ ( producer , __VA_ARGS__ ) 22 # define consumer ( . . . ) __ENCAP__ ( consumer , __VA_ARGS__ ) 23 # e n d i f

A maioria das linhas é dedicada a incluir as bibliotecas padrões e da pla- taforma, entretanto a declaração do ponteiro PTR (linhas 5 e 6) é feita para permitir o acesso à região de memória compartilhada da plataforma através

de palavras de 32 bits com o tipo inteiro sem sinal.

Na seção 4.3.3, pode ser visto no exemplo do código fonte 4.5 a modela- gem de ponteiros, no qual foi criado um ponteiro de nome PTR. A motivação para esta modelagem de ponteiro é permitir que o acesso de posições de memória em um ambiente de simulação nativo sejam traduzidos em endere- ços do espaço de endereçamento da plataforma virtual, utilizando a mesma sintaxe utilizada pela linguagem de programação. Neste exemplo foi criado somente um ponteiro para acessar o dispositivo, mas a modelagem permite a criação de múltiplos ponteiros, caso seja necessário, sempre buscando aten- der aos requisitos da plataforma e não de uma aplicação em especial.

4.5.1.2 Código Fonte da Aplicação

Para ilustrar a execução multiprocessada da aplicação, foi implementada uma aplicação clássica de produtor e consumidor (ver capítulo 2 de conceitos básicos), onde as instâncias de processadores com números de identificação pares e ímpares são produtoras e consumidoras, respectivamente. As instân- cias produtoras acessam concorrentemente a uma variável de contagem de itens, que está armazenada na memória compartilhada, para incrementar o seu valor, enquanto que as instâncias consumidoras acessam concorrente- mente esta mesma variável para decrementar seu valor.

No exemplo de aplicação concorrente, visto no código fonte 4.8, é feita a inclusão do cabeçalho com as definições da aplicação (linhas 1 e 2), que já foi detalhado anteriormente, a declaração de ponteiro para a região de me- mória compartilhada (linhas 4 e 5). Na declaração do ponteiro é definido o endereço 0x80008000 para armazenamento da quantidade de itens que será incrementado pela tarefa produtora e decrementado pela tarefa consumi- dora.

Código Fonte 4.8: Exemplo de código fonte de programação concorrente

1 / / Consumer/ Producer include 2 #include <consumer_producer . h> 3

4 / / Item counter po int er

6

7 / / Lock item function

8 void lock_item ( unsigned i n t i d ) { 9 / / Attempting to lock mutex 10 do GSMPMT = ( i d << 16) + 1 ;

11 / / Repeat the lock u n t i l i t i s obtained 12 while ( (GSMPMT >> 16) ! = i d ) ;

13 }

14 / / Unlock item function

15 void unlock_item ( unsigned i n t i d ) { 16 / / Releasing mutex

17 GSMPMT = ( i d << 16) ; 18 }

19 / / Producer t a s k

20 void producer ( unsigned i n t i d ) { 21 / / Forever loop 22 while ( 1 ) { 23 / / Mutex lock 24 lock_item ( i d ) ; 25 / / R e t r i e v i n g item quantity 26 unsigned i n t quantity = ∗PTR ; 27 / / Producing item 28 i f ( quantity < 10) quantity ++; 29 / / Updating item quantity 30 ∗PTR = quantity ;

31 / / Outputting information

32 p r i n t f ( " [ Processor %u ] [ Producer ] %u item ( s ) \n " , id , quantity ) ; 33 / / Mutex unlock 34 unlock_item ( i d ) ; 35 } 36 } 37 / / Consumer t a s k

38 void consumer ( unsigned i n t i d ) { 39 / / Forever loop

40 while ( 1 ) { 41 / / Mutex lock 42 lock_item ( i d ) ; 43 / / R e t r i e v i n g item quantity 44 unsigned i n t quantity = ∗PTR ; 45 / / Consuming item 46 i f ( quantity > 0) quantity −−; 47 / / Updating item quantity 48 ∗PTR = quantity ;

49 / / Outputting information

50 p r i n t f ( " [ Processor %u ] [ Consumer ] %u item ( s ) \n " , id , quantity ) ; 51 / / Mutex unlock 52 unlock_item ( i d ) ; 53 } 54 } 55 / / Main function

56 i n t main ( i n t argc , char∗ argv [ ] ) { 57 / / S e t t i n g item po int er

58 PTR = item ;

59 / / Resetting item quantity 60 ∗PTR = 0 ; 61 / / R e t r i e v i n g processor ID 62 unsigned i n t i d = GSMPID ; 63 / / Processor t a s k a l l o c a t i o n 64 i f ( i d % 2 == 0) producer ( i d ) ; 65 else consumer ( i d ) ; 66 / / Returning success 67 r e t u r n 0 ; 68 }

Na função principal da aplicação (linhas 55 a 68) é feita a atribuição do endereço ao ponteiro PTR, a inicialização da quantidade de itens para o valor zero, a obtenção do número de identificação do processador e alocação da tarefa de produtor e de consumidor para os núcleos pares e ímpares, respec- tivamente.

Na tarefa de produtor (linhas 19 a 36) é executado em um laço infinito (linhas 21 e 35) que solicita o acesso exclusivo para acessar a memória com- partilhada da plataforma (linhas 23 e 24) e incrementar o valor da variável de itens, garantindo que não exceda o limite de 10 unidades (linhas 25 e 30). Após incrementar este contador de itens, são exibidos no terminal de saída as infor- mações sobre o processador, o nome da tarefa de produtor e a quantidade atual de itens disponíveis (linhas 31 e 32). Uma vez que as operações foram finalizadas, é feita a liberação da região crítica (linhas 33 e 34) para permitir que outros processadores sejam capazes de acessarem o contador de itens.

A tarefa de consumidor (linhas 37 a 54) também é executada em um laço infinito (linhas 39 e 53), repetindo o bloqueio para acessar a região crítica (li- nhas 41 e 42), acessando o endereço de memória que contém a variável de itens (linhas 43 e 44) e decrementando o valor do contador de itens, garan- tindo que não seja negativo (linhas 45 e 46). Antes de liberar a região crítica (linhas 51 e 52), o valor é atualizado na memória compartilhada (linhas 47 e 48) e a identificação do processador, o nome da tarefa de consumidor e o valor atualizado do contador de itens são exibidos (linhas 49 e 50).

Na figura 56 é exibida a saída da execução da aplicação de produtor e consumidor em uma plataforma com 4 processadores, com dois núcleos com funções de produção e dois núcleos com função de consumo. Os resultados observados são referentes as primeiras linhas geradas pela execução, uma vez que a simulação não possui critério de parada, executando indefinidamente.

[Processor 3][Consumer] 0 item(s) [Processor 3][Consumer] 0 item(s) [Processor 0][Producer] 1 item(s) [Processor 3][Consumer] 0 item(s) [Processor 1][Consumer] 0 item(s) [Processor 2][Producer] 1 item(s) [Processor 1][Consumer] 0 item(s) [Processor 2][Producer] 1 item(s) [Processor 0][Producer] 2 item(s) [Processor 3][Consumer] 1 item(s) [Processor 3][Consumer] 0 item(s) [Processor 3][Consumer] 0 item(s) [Processor 3][Consumer] 0 item(s) [Processor 1][Consumer] 0 item(s) [Processor 3][Consumer] 0 item(s) [Processor 0][Producer] 1 item(s) [Processor 1][Consumer] 0 item(s) [Processor 0][Producer] 1 item(s) [Processor 0][Producer] 2 item(s) [Processor 0][Producer] 3 item(s) [Processor 0][Producer] 4 item(s) [Processor 0][Producer] 5 item(s) [Processor 0][Producer] 6 item(s) [Processor 0][Producer] 7 item(s) [Processor 0][Producer] 8 item(s) [Processor 0][Producer] 9 item(s) [Processor 0][Producer] 10 item(s) [Processor 0][Producer] 10 item(s) [Processor 0][Producer] 10 item(s) [Processor 0][Producer] 10 item(s) [Processor 1][Consumer] 9 item(s) [Processor 0][Producer] 10 item(s) [Processor 1][Consumer] 9 item(s) [Processor 1][Consumer] 8 item(s) ...