Próxima revisão | Revisão anterior |
pua:gerencia_de_memoria [2008/09/20 11:58] – created maziero | pua:gerencia_de_memoria [2015/10/24 10:47] (atual) – maziero |
---|
| ====== Gerência de memória ====== |
| |
| O espaço de endereços de um processo é dividido em várias áreas distintas. As mais importantes são: |
| |
| * //Text area//: contém o código do programa e suas constantes. Esta área é alocada durante a chamada ''exec'' e permanece do mesmo tamanho durante toda a vida do processo. |
| * //Data area//: é a memória de trabalho do processo, onde ele armazena suas variáveis globais e estáticas. Tem tamanho fixo ao longo da execução do processo. |
| * //Stack area//: contém a pilha de execução, onde são armazenadas os parâmetros, endereços de retorno e variáveis locais de funções. Pode variar de tamanho durante a execução do processo. |
| * //Heap area//: contém áreas de memória alocadas a pedido do processo, durante sua execução. Varia de tamanho durante a vida do processo. |
| |
| {{ :pua:areas-memoria.png?600 |}} |
| |
| Um programa em C suporta três tipos de alocação de memória: |
| |
| * A **alocação estática** ocorre quando são declaradas variáveis globais ou estáticas; geralmente usa a área //Data//. |
| * A **alocação automática** ocorre quando são declaradas variáveis locais e parâmetros de funções. O espaço para a alocação dessas variáveis é reservado quando a função é invocada, e liberado quando a função termina. Geralmente é usada a pilha (//stack//). |
| * A **alocação dinâmica**, quando o processo requisita explicitamente um bloco de memória para armazenar dados; o controle das áreas alocadas dinamicamente é manual ou semi-automático: o programador é responsável por liberar as áreas alocadas dinamicamente. A alocação dinâmica geralmente usa a área de //heap//. |
| |
| ===== Alocação dinâmica de memória ===== |
| |
| <code c> |
| #include <stdlib.h> |
| void * malloc (size_t size) |
| </code> |
| |
| Esta função aloca uma nova região com ''size'' bytes de tamanho e retorna um pointeiro para o início da mesma (ou 0 em caso de erro). O conteúdo da nova área é indefinido. Eis um exemplo de uso: |
| |
| <code c> |
| struct mystruct *ptr; |
| ... |
| ptr = (struct mystruct *) malloc (sizeof (struct mystruct)); |
| if (ptr == 0) abort (); |
| </code> |
| |
| <code c> |
| #include <stdlib.h> |
| void free (void *ptr) |
| </code> |
| |
| Esta função libera um bloco de memória previamente alocado, apontado por ''ptr''. |
| |
| <code c> |
| #include <stdlib.h> |
| void * realloc (void *ptr, size_t newsize) |
| </code> |
| |
| Esta função redimensiona o bloco previamente alocado apontado por ''ptr'' para o novo tamanho ''newsize''. Retorna o novo endereço do bloco, que pode ser diferente do anterior, caso tenha sido necessário mudá-lo de lugar (o conteúdo original do bloco é preservado nesse caso ou em caso de erro). |
| |
| <code c> |
| #include <stdlib.h> |
| void * calloc (size_t count, size_t eltsize) |
| </code> |
| |
| Esta função aloca um bloco de tamanho suficiente para conter um vetor com ''count'' elementos de tamanho ''eltsize'' cada um. O conteúdo do bloco alocado é preenchido por zeros. |
| |
| <code c> |
| #include <stdlib.h> |
| void * alloca (size_t size) |
| </code> |
| |
| Esta função provê um mecanismo de alocação dinâmica semi-automática: o bloco alocado será liberado automaticamente ao encerrar a função onde o bloco foi alocado. O valor de retorno da chamada é o endereço de um bloco de tamanho ''size'' bytes, alocado na pilha da função atual (como se fosse uma variável local). |
| |
| Na GLibC, os blocos alocados pelas funções acima sempre iniciam em um endereço múltiplo de 8 em plataformas de 32 bits. Caso seja necessário obter blocos iniciando em múltiplos de 16, 32, 64, etc, as funções a seguir estão disponíveis: |
| |
| <code c> |
| #include <malloc.h> |
| void * memalign (size_t boundary, size_t size) |
| </code> |
| |
| Aloca um bloco de tamanho ''size'' cujo endereço inicial é um múltiplo de ''boundary'' (que deve ser 2<sup>n</sup>). Retorna o endereço do bloco alocado, que pode ser liberado mais tarde através da função ''free''. Ver também a função ''posix_memalign''. |
| |
| ===== Arquivos mapeados em memória ===== |
| |
| Sistemas operacionais modernos permitem mapear um arquivo em uma região de memória. Isso torna possível acessar o arquivo como se fosse um vetor de bytes em memória. Esse procedimento é mais eficiente que os tradicionais ''read''/''write'', pois somente as regiões acessadas do arquivo são carregadas em memória. Como o mecanismo de mapeamento de arquivo faz uso dos mecanismos de memória virtual, é possivel mapear arquivos muito grandes na memória (o limite é o espaço de endereçamento). |
| |
| <code c> |
| #include <sys/mman.h> |
| void * mmap (void *address, size_t length,int protect, int flags, int filedes, off_t offset) |
| </code> |
| |
| Cria um novo mapeamento em memória relacionado aos bytes ''offset'' ... ''offset+length-1'' no arquivo aberto indicado pelo descritor ''filedes''. O mapeamento não é removido ao fechar o descritor, ele deve ser desfeito explicitamente. |
| |
| O campo ''address'' indica um endereço preferencial para mapear o arquivo na memória (ou NULL). O campo ''protect'' contém flags indicando o tipo de acesso permitido àquela região: leitura (''PROT_READ''), escrita (''PROT_WRITE'') ou execução (''PROT_EXEC''). Acessos inválidos resultarão em um sinal ''SIGSEGV''. Essas proteções podem ou não ser suportadas pelo hardware. |
| |
| O campo ''flags'' contém flags que controlam o tipo de mapeamento: |
| |
| * ''MAP_PRIVATE'': escritas na área mapeada em memória não devem ser escritas de volta no disco, ou seja, o mapeamento é privado do processo que o fez. |
| * ''MAP_SHARED'': escritas na área mapeada em memória serão imediatamente visíveis por outros processos mapeando esse mesmo arquivo em memória (caso seja necessário atualizar o conteúdo físico em disco rapidamente, deve-se utilizar a chamada ''msync''). |
| * ''MAP_FIXED'': força o sistema a usar o endereço especificado em address (ou falhar se não for possível). |
| * ''MAP_ANONYMOUS'': cria um mapeamento anônimo, ou seja, não conectado a nenhum arquivo. Pode ser útil para comunicação entre processos sem a necessidade de se criar um arquivo em disco. |
| |
| Deve-se observar que nem todos os descritores de arquivo podem ser mapeados em memória. Normalmente, somente arquivos normais e dispositivos orientados a blocos podem ser mapeados em memória. Além disso, essa funcionalidade não está disponível em sistemas mais antigos. |
| |
| <code c> |
| #include <sys/mman.h> |
| int munmap (void *addr, size_t length) |
| </code> |
| |
| Remove mapeamentos efetuados entre (''addr'') e (''addr + length''), onde ''length'' deve ser o tamanho do mapeamento. Mais de um mapeamento pode ser removido em uma só operação. |
| |
| <code c> |
| #include <unistd.h> |
| #include <sys/mman.h> |
| int msync (void *address, size_t length, int flags) |
| </code> |
| |
| Permite sincronizar um mapeamento de arquivo em memória com a imagem do arquivo em disco. A região é indicada pelos campos ''address'' e ''length''. O campo ''flags'' pode conter as seguintes opções: |
| |
| * ''MS_SYNC'' : força os dados a serem efetivamente escritos no disco (a operação default apenas garante que outros processo com o arquivo aberto de forma convencional verão as mudanças recentes). |
| * ''MS_ASYNC'' : indica a ''msync'' para iniciar a sincronização, mas não aguardar sua conclusão. |
| |
| ===== Proteção de páginas em memória ===== |
| |
| É possível informar ao sistema operacional que certas páginas de memória virtual nunca devem ser enviadas para o disco (//swapping// ou //paging//). Isso pode ser necessário em algumas circunstâncias: |
| |
| * //Velocidade//: buscar uma página faltante em disco toma muito mais tempo que acessar a mesma página em memória. Esse tempo adicional de paginação pode prejudicar a execução de processos críticos. |
| * //Segurança//: informação temporária sensível escrita na memória (como senhas) pode ir para o disco através de //swapping// e ficar lá por muito tempo, podendo ser capturada mais facilmente. |
| |
| Por causa de seus possíveis efeitos sobre outros processos (reduzindo a disponibilidade de memória do sistema), normalmente só processos do administrador podem travar páginas em memória (mais detalhes na página de manual de ''mlock''). |
| |
| <code c> |
| #include <sys/mman.h> |
| int mlock (const void *addr, size_t len) |
| </code> |
| |
| Permite travar na memória um conjunto de páginas do processo. A faixa de memória a travar inicia em ''addr'' e tem ''len'' bytes de tamanho. Todas as páginas atingidas por essa faixa são travadas. |
| |
| <code c> |
| #include <sys/mman.h> |
| int munlock (const void *addr, size_t len) |
| </code> |
| |
| Faz o inverso de ''mlock''. |
| |
| <code c> |
| #include <sys/mman.h> |
| int mlockall (int flags) |
| </code> |
| |
| ''mlockall'' trava todas as páginas usadas pelo processo (ou que virão a ser usadas no futuro). ''flags'' indica o tipo de travamento a realizar: ''MCL_CURRENT'' (trava as páginas atuais) e/ou ''MCL_FUTURE'' (trava as páginas que vierem a fazer parte do espaço de endereços do processo no futuro). |
| |
| <code c> |
| #include <sys/mman.h> |
| int munlockall (void) |
| </code> |
| |
| Faz o contrário de ''mlockall'', destravando todas as páginas presentes e futuras do processo. |
| |
| <code c> |
| #include <sys/mman.h> |
| int mprotect(const void *addr, size_t len, int prot); |
| </code> |
| |
| Controla a forma como uma região de memória pode ser acessada. Caso a forma de acesso definida seja violada, o processo recebe um sinal ''SIGSEGV''. A região de memória a proteger inicia em ''addr'' e possui ''len'' bytes de tamanho (''addr'' deve ser um múltiplo de ''PAGESIZE'', pois a unidade básica de proteção é a página); o campo ''prot'' indica o tipo de proteção a aplicar, que pode ser uma combinação (OU binário) das seguintes macros: |
| |
| * ''PROT_NONE'' : a memória não pode ser acessada. |
| * ''PROT_READ'' : a memória pode ser lida. |
| * ''PROT_WRITE'' : a memória pode ser escrita. |
| * ''PROT_EXEC'' : a memória pode conter código executável. |
| |
| ===== Operações em blocos de memória ===== |
| |
| As funções aqui indicadas são úteis para operar com blocos de memória alocados estatica ou dinamicamente. |
| |
| <code c> |
| #include <string.h> |
| void * memcpy (void *restrict to, const void *restrict from, size_t size) |
| </code> |
| |
| Copia ''size'' bytes do bloco iniciando no endereço from para o bloco iniciando no endereço to. O comportamento dessa função é imprevisível caso haja sobreposição das áreas de memória indicadas. |
| |
| <code c> |
| #include <string.h> |
| void * memmove (void *to, const void *from, size_t size) |
| </code> |
| |
| Copia ''size'' bytes do bloco iniciando no endereço ''from'' para o bloco iniciando no endereço ''to'', mesmo que haja sobreposição entre os blocos. |
| |
| <code c> |
| #include <string.h> |
| void * memccpy (void *restrict to, const void *restrict from, int test, size_t size) |
| </code> |
| |
| Copia até ''size'' bytes do bloco iniciando em ''from'' para o bloco iniciando em ''to'', parando ao copiar todos os bytes ou ao encontrar um byte cujo valor seja igual a ''test''. |
| |
| <code c> |
| #include <string.h> |
| void * memset (void *addr, int value, size_t size) |
| </code> |
| |
| Copia o valor de ''value'' (convertido para ''unsigned char'') nos primeiros ''size'' bytes do bloco de memória iniciando em ''addr''. |
| |
| ===== Depurando problemas de memória ===== |
| |
| As operações envolvendo ponteiros e alocação dinâmica de memória costumam levar a bugs complexos e muitas vezes difíceis de resolver. Existem várias ferramentas para auxiliar o programador a localizar e resolver problemas relacionados à alocação dinâmica de memória e uso inadequado de ponteiros em C/C++: |
| |
| * [[http://dmalloc.com/|Dmalloc]] |
| * [[http://www.gnu.org/software/checker/checker.html|GNU Checker]] |
| * [[http://libmss.sourceforge.net/index.php|Memory Supervision System]] |
| * [[http://www.andreasen.org/LeakTracer/|LeakTracer]] |
| * [[http://valgrind.kde.org/|Valgrind]] |
| * [[http://www.parasoft.com/jsp/smallbusiness/tool_description.jsp?product=Insure|Parasoft Insure++]] |
| * [[http://www-306.ibm.com/software/awdtools/purify/unix/|Rational Purify]] |
| |
| ===== Atividades ===== |
| |
| A definir... |
| |