• Nenhum resultado encontrado

Criação de checkpoints e recuperação do estado da aplicação

3.4 Pré-compilador e biblioteca de checkpointing

3.4.3 Criação de checkpoints e recuperação do estado da aplicação

Veremos agora o modo através do qual os dados de um processo são armazenados em um checkpoint e posteriormente recuperados numa arquitetura possivelmente distinta da original.

Criação do checkpoint

Um novo checkpoint é gerado sempre que a aplicação chama o método ckp_save_stack_data. Como já vimos, a biblioteca de checkpointing precisa substituir os endereços de memória por offsets. O processo de gerar o checkpoint é realizado em três fases:

1. Na primeira fase, é calculado o tamanho necessário para o buffer que irá armazenar o checkpoint temporariamente. Neste processo, endereços de blocos de memória alocados dinamicamente são inseridos na pilha de checkpointing. Definimos também uma pilha de ponteiros, onde empilhamos os índices da pilha de checkpointing onde estão localizados os endereços dos ponteiros e os dados referenciados por estes ponteiros. Nesta fase, ponteiros com múltiplos níveis de indireção são também resolvidos, incluindo matrizes multi-dimensionais alocadas dinamicamente. Neste caso, todos os ponteiros presentes na matriz são empilhados na pilha de ponteiros. Finalmente, no caso de ponteiros para estruturas, a função que armazena os membros da estrutura é chamada. Esta função, por sua vez, empilha novos itens na pilha de checkpointing, que serão tratados ainda nesta fase à medida em que a pilha for percorrida.

2. Na segunda fase, tipos de dados primitivos e blocos de memória alocados dinamicamente são copiados no buffer de checkpoint e suas localizações são inseridas em uma tabela, que contém as suas posições no buffer.

3. Na terceira fase, os endereços de memória contidos nos ponteiros são substituídos por offsets contidos na tabela de posições no buffer. Devemos notar que, ao utilizar estes offsets, referências para endereços repetidos e referências circulares são tratadas automaticamente, com os ponteiros referenciando a mesma posição no buffer.

Estas três fases requerem O(s+p) passos, onde s é o número de elementos na pilha de checkpointing antes da geração do checkpoint e p é o número de elementos na pilha de ponteiros no final da primeira fase. A biblioteca de checkpointing permite salvar os checkpoints diretamente no sistema de arquivos ou em repositórios remotos de checkpoints.

Recuperação do estado contido em um checkpoint

A recuperação dos dados de um checkpoint é realizada pela classe CkpRestoreData. Inicial- mente os dados do checkpoint são lidos para um buffer temporário. À medida em que a aplicação realiza chamadas ao método ckp_get_data, os dados são copiados do buffer para endereços na pilha de execução e para a memória dinâmica da aplicação, de modo a recuperar o estado armazenado no check- point. Para determinar o próximo dado que será lido do buffer, a classe CkpRestoreData mantém um ponteiro para a posição corrente no buffer.

O parâmetro contendo o tipo do dado a ser lido, enviado na chamada a ckp_get_data, determina como este dado será tratado. Se for um tipo primitivo, os bytes referentes àquele tipo serão copiados no endereço fornecido na chamada a ckp_get_data, realizando, quando a arquitetura onde o checkpoint foi gerado for diferente, a conversão apropriada na representação dos dados. No caso de estruturas, a biblioteca chama as funções fornecidas na chamada a ckp_get_data, de modo a reconstruir a estrutura, como descrito na Seção 3.4.2. Finalmente, no caso de ponteiros, o procedimento depende do tipo de ponteiro armazenado:

• Para ponteiros simples (com um nível de indireção) para endereços de memória alocada dinami-

camente, é preciso determinar primeiro se o bloco de memória referenciado já foi lido do buffer.

Em caso negativo, o tamanho do bloco é lido a partir do buffer e um novo bloco de memória

alocado dinamicamente. Os dados do buffer são então copiados para o bloco de memória recém alocado. Se a arquitetura onde o checkpoint está sendo restaurado for igual à que ele foi salvo, os dados são copiados diretamente para o endereço fornecido. Caso contrário, os dados do buffer são copiados em blocos do tamanho do tipo de dado apontado pelo ponteiro fornecido, sendo convertidos à medida em que são copiados. No final da cópia, o endereço do bloco de memória alocado é adicionado a uma tabela dataChunksRead, indexada pelo

offset do bloco que acabou de ser copiado do buffer.

Em caso afirmativo, é atribuído ao ponteiro fornecido o endereço de memória contido na

posição offset da tabela dataChunksRead.

• No caso de ponteiros com múltiplos níveis de indireção, o procedimento é similar ao caso de pon- teiros simples. Mas, ao realizar a cópia dos dados para o bloco de memória alocado, é necessário agora substituir os offsets contidos no bloco de memória do buffer por endereços de memória reais,

3.4 Pré-compilador e biblioteca de checkpointing 51 no espaço da aplicação. Para cada offset contido no bloco de memória, é realizada uma chamada a ckp_get_data, reduzindo o nível de indireção do ponteiro. Este processo é repetido recursiva- mente até o ponteiro se transformar em um ponteiro simples.

• No caso de ponteiros para endereços da pilha de execução o procedimento é diferente. Os en- dereços destes ponteiros são armazenados em uma tabela stackDataPointersList, junto com os offsets lidos do buffer temporário. À medida que os dados são lidos do buffer e copiados para a pilha de execução, os offsets e respectivos endereços na pilha de execução são adicionados a uma tabela stackDataAddresses, similar à tabela dataChunksRead. No final do pro- cesso de recuperação do estado da aplicação, após todos os dados do buffer serem lidos, a tabela stackDataPointersListé percorrida, atribuindo aos endereços de memória lá contidos os valores dos endereços de stackDataAddresses.

Existem ainda outros casos que são tratados pela biblioteca de checkpointing, como vetores alocados diretamente na pilha de execução e ponteiros para estrutura, e que não descreveremos neste texto8. O

procedimento para recuperar os dados nestes casos é similar aos demais, diferindo apenas em detalhes de implementação.