Rick Santos Postado Novembro 12, 2017 em 03:20 Compartilhar Postado Novembro 12, 2017 em 03:20 Neste tópico pretendo realizar uma breve apresentação de diversos pontos que considero importantes relativamente à função malloc() em sistemas Linux. malloc() tem como função alocar memória dinamicamente, criando ou alterando assim um segmento pertencente ao processo chamado Heap. (Alterando também pois durante a carregamento do programa para a memória, tanto o Loader do OS quando a própria libc podem (mas nem 100% das vezes) ter pré-alocado o segmento Heap com páginas suficientes para o processo em questão, para assim, evitar o mapeamento de novas) A função malloc() é declarada em em "stdlib.h" a partir do seguinte protótipo: void *malloc(size_t size); Isto quer dizer que malloc() alocará size quantidade de memória e retornará um ponteiro * do tipo void para o primeiro endereço do segmento alocado. (A Heap não será inicializada com nenhuns dados inicialmente). Quando malloc() é executada com sucesso retorna um ponteiro do tipo void, isto quer dizer que (falando a nível de programação high-level), o ponteiro não tem nenhum tipo associado, sendo assim podendo ser usado por qualquer tipo de variável e ficando sempre com o tamanho de 8 bytes (64 bits), como qualquer ponteiro em arquiteturas atuais. Por outro lado, caso não tenha havido sucesso, ou seja não conseguindo alocar memória, quer dizer que o size = 0. Quando tal acontece malloc() retorna um valor NULL, em certos casos, poderá ser retornado um valor único de ponteiro podendo ser passado futuramente para uma outra função chamada free(). Quando a libc tenta alocar memória pedida por malloc() (atráves de system calls que irei descrever adiante) assume que existe espaço para a criação de novas páginas no espaço requisitado, o é quase sempre verdade devido ao uso do VAS (Virtual Address Space, derivado da Paginação). Mesmo assim é correto e essencial verificar sempre o valor de retorno, e caso não seja possível a alocação decidir o que fazer. Segue um código de exemplo sobre o falado até agora: #include <stdio.h> #include <stdlib.h> // malloc() tem protótipo aqui como: void *malloc(size_t size); int main(int argc, char *argv[]) { void *ptr; //Criação de um ponteiro do tipo void, podia ser de qualquer tipo. //Aqui usou-se a malloc para alocar sizeof(int) memória, neste caso será 4 bytes e caso malloc() retorne null, acontece algo, como coloquei em baixo. if (!!(ptr = malloc(sizeof(int)) == NULL)) { //OBS: O uso de !! foi apenas para o compilador não reclamar de uma atribuição "válida". printf("Não foi possível alocar memória\n"); exit(EXIT_FAILURE); // OBS: EXIT_FAILURE é uma macro neste caso para 0, mas portável para todos os sistemas operativos, enquanto 0 em si não. } free(ptr); //Limpar o espaço alocado por malloc atráves de ptr. (Sempre necessário quando não se vai mais utilizar a memória alocada) return 0; } PS: NULL é definido em C, por vários headers - <stddef.h>, <locale.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>. A partir da seguinte forma: #if !defined(NULL) #define NULL ((void*)0) #endif Querendo assim dizer que NULL é uma Macro, que pode ser atribuída a qualquer tipo de dado "void" e que aponta * para o valor 0, ou seja, usar NULL ou 0 é a mesma coisa pois NULL = 0. -------------------------------------------------------------------------------------------------------------------------------------------------------- Algo importante é que o Kernel apenas aloca segmentos de memória (blocos de páginas) com tamanhos múltiplos do tamanho de uma página, que por padrão é 4 KiB (O Linux como outros sistemas podem trabalhar com páginas de tamanhos maiores habilitando a extensão PSE, mas não nos interessa por agora). Isto quer dizer que se o programa tentar alocar menos do que 4 KiB o Kernel continuará a alocar uma página pelo menos e o código só tera acesso ao espaço requisitado dessa página (O que equivale a um desperdício de memória, e assim de performance). Felizmente a própria malloc() e outras funções de alocação, para ganho de performance, tem como objetivo reutilizar páginas já usadas e também aproveitar páginas já criadas mas com necessidade de remapeamento, assim evitando que elas andem "perdidas" pela memória. --------------------------------------------------------------------------------------------------------- Na verdade a malloc() não é nenhuma função do "sistema", ela é apenas uma função usada em C que é definida em "stdlib.h". Na realidade malloc() utiliza duas System Calls para a alocação de memória, chamadas sbrk() e mmap(). A malloc() utiliza a system call sbrk() quando pretende alocar blocos de memória menores que MMAP_THRESHOLD (ou seja 128 KiB (32 páginas)), isto é feito a partir da expansão de memória já alocada para o processo a partir de um ponto chamado "program break". Program Break é o primeiro endereço linear logo após ao segmento BSS (inicializado a zeros) do processo, o program break ao ser aumentado a partir de void *sbrk(intptr_t increment) aumenta também o número de páginas disponíveis que foram requesitadas incialmente para uso da heap por malloc(). (PS: Chamar sbrk() com um incremento de zero vai dar origem à obtenção do endereço do Program Break do processo em questão). Por fim o valor de retorno de sbrk() é um ponteiro para o endereço inicial do novo "segmento" de memória alocado. Caso tenha sido passado o argumento increment = 0 a sbrk(), o valor de retorno será o endereço do Program Break. Caso tenha havido algum erro o valor de retorno será do tipo void * com o valor de -1. (PS: O tipo do argumento passado para sbrk() varia entre sistemas POSIX, podendo ser: int, ssize_t, ptrdiff_t, intptr_t). Para blocos maiores que MMAP_THRESHOLD (ou seja 128 KiB (32 páginas)), malloc() utiliza a system call mmap(), que contém a seguinte definição: void *mmap(void *addr, size_t lengthint " prot ", int " flags , int fd, off_t offset); Como podem ver é uma função longa, não vou falar detalhadamente dela mas sim cobri-la no geral como fiz com sbrk(). mmap() aloca novas páginas não acessíveis no user-space com tamanho passado como argumento "length" e a partir de um endereço inicial passado em "addr". Caso o argumento addr seja passado como NULL o Kernel tenta alocar memória a partir do endereço que ele achar mais "performático" alocar, caso contrário (seja passado algum endereço em addr), o Kernel tenta alocar memória a partir daquele endereço passado (A passagem do parâmetro addr é apenas uma "dica", agora não quer dizer que seja sempre cumprida). O argumento "prot" de mmap está relacionado com algumas "opções" de proteções das páginas criadas cujo a descrição está fora do escopo do tópico. Por fim, o valor de retorno de mmap() é o endereço da primeira posição do segmento de memória alocado por esta função. PS: A função (system call) mmap() pode ser utilizada diretamente em C, assim tornando-se mais vantajo-so trabalhar com esta quando se quer ter um maior controle sobre páginas do que usar malloc(), que nos dará "recursos limitados". Basta incluir o seguinte header: #include <sys/mman.h> ---------------------------------------------------------------------------- Bem, esta foi apenas a apresentação de um pontos básicos que considero importantes acerca da tão conhecida função malloc() em C. Ricardo Santos Link para o comentário Compartilhar em outros sites More sharing options...
JuniorTCP Postado Novembro 27, 2017 em 17:41 Compartilhar Postado Novembro 27, 2017 em 17:41 Muito bom o material, usei muito o malloc() em estrutura de dados passando uma struct como base para o tamanho da memoria, Ótimo material parabéns, fez eu me recordar de um tempo muito bom em minha aprendizagem obrigado. Link para o comentário Compartilhar em outros sites More sharing options...
Posts Recomendados
Arquivado
Este tópico foi arquivado e está fechado para novas respostas.