paulo1205
(usa Ubuntu)
Enviado em 22/05/2013 - 06:17h
Tanto o padrão da linguagem C (e também o de C++) quanto a biblioteca padrão fazem muito poucas exigências e assunções sobre características da máquina ou do SO em que os programas escritos em C podem executar. De seu ponto de vista, um programa conforme deve executar de modo absolutamente idêntico num super mainframe, num PC parrudão, numa máquina teórica com memória infinita, num micrinho de 8 bits antigo ou por um aluno aplicando o "método do chinês" com a ajuda do seu caderno. Supõe-se pouco além de uma entidade capaz de executar as instruções e alguma forma de memória que possa sofre acessos de leitura e gravação e que permita operações indiretas por meio de ponteiros. Quando se fala em limites (e mais nos requisitos da biblioteca padrão do que características da linguagem em si), geralmente são limites mínimos para que as algumas funções possam executar com conforto.
"Comum a todos os sistemas", portanto, é algo que não vai existir em C puro, pois cada sistema funciona de um jeito e tem API diferente de outros sistemas. No máximo, se você optar por uma biblioteca que esconda particularidades do SO, vai transferir para essa biblioteca do "trabalho sujo" de fazer encadeamentos de #ifdef.
Se existe uma maneira "padrão" de testar a memória disponível para um programa, talvez seja fazer um loop que vai alocando memória sem parar, acumulando o total já alocado, até que uma tentativa de alocação venha a dar erro, de modo mais ou menos parecido com a função
alloc_max () do programa abaixo.
/*
Programa de teste de alocacao maxima de memória
usando apenas C padrão (conforme ISO C99).
*/
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t alloc_max(size_t chunk_size){
void *old_p, *new_p;
size_t total;
if(chunk_size<sizeof(size_t)){
fprintf(stderr, "O tamanho minimo do bloco deve ser de %zu bytes.\n", sizeof(size_t));
return 0;
}
total=0;
old_p=NULL;
while((new_p=malloc(chunk_size))!=NULL){
total+=chunk_size;
/*
XXX: Preenche memória alocada (para forçar seu uso efetivo em sistema com lazy allocation.
Por isso, aliás, a restrição de tamanho mínimo, imposta acima.
*/
memset(new_p, rand(), chunk_size);
/* Guarda o endereço do bloco anterior no começo do novo bloco, para poder desalocar depois. */
*(size_t **)new_p=old_p;
old_p=new_p;
}
for(new_p=old_p; new_p!=NULL; new_p=old_p){
old_p=*(void **)new_p;
free(new_p);
}
return total;
}
int main(void){
size_t chunk, max;
for(chunk=4096; chunk<SIZE_MAX/32+1; chunk*=16){
max=alloc_max(chunk);
printf("Total de %zu bytes (%zu blocos de %zu bytes).\n", max, max/chunk, chunk);
}
return 0;
}
No entanto, quando eu testei esse programa na minha máquina (um notebook com 4GiB de RAM e 6GiB de swap com Ubuntu de 64 bits), ele realmente saiu comendo memória feito um louco mas, quando estava chegando perto de 8GiB, em lugar de receber erro de alocação, ele foi abortado pelo SO, com a seguinte mensagem de erro do kernel (obtida a partir da saída do comando
dmesg ).
[1376327.747104] Out of memory: Kill process 19883 (a.out) score 810 or sacrifice child
[1376327.747108] Killed process 19883 (a.out) total-vm:8301144kB, anon-rss:3373256kB, file-rss:4kB
O programa acima talvez tivesse funcionado noutro sistema, ou mesmo na minha própria máquina se não houvesse outros processos em execução, competindo com ele pela alocação de memória. Mesmo assim, como se usou apenas C puro, não se tem, por exemplo, distinção entre memória física e memória virtual (tudo é memória), nem interessa se o desempenho vai ficar sofrível. Também não se tem controle sobre eventuais restrições impostas pelo ambiente de execução, como a atribuição de um limite na quantidade de memória que poderá ser usada pelo processo, ou eventuais efeitos de outros processos sobre o ambiente de execução -- afinal, o C padrão não tem a menor ideia de o que seja um processo.
Note ainda, no comentário marcado com "XXX", que uma característica de alocação particular de uma implementação (usada por padrão pela glibc no Linux, mas não obrigatória de modo algum num sistema qualquer -- incluindo o próprio Linux) implica a geração de código artificioso para fazer com que o programa efetivamente use a memória que pediu ao sistema para poder usar.