… Conforme definição do Wikipédia sobre a chamada mmap(), vemos abaixo!

Em computação, mmap é uma chamada de sistema do Unix, em conformidade com o POSIX, que mapeia arquivos ou dispositivos na memória. É um método de E/S de arquivo mapeado em memória. Ela implementa naturalmente a paginação por demanda, pois os conteúdos iniciais dos arquivos não são inteiramente lidos do disco e não usam a memória RAM física completamente. A real leitura do disco é feita de maneira “preguiçosa”, após uma posição específica ter sido acessada.

Ou seja, podemos fazer muitas coisas utilizando a chamada mmap(), principalmente quando e preciso manipular arquivos de grande portes, e você não quer ter buffers auxiliares entre tantas outras possibilidades, que na grande maioria seguimos o seguinte fluxo.

open() -> read() || write() -> close()

Abaixo segue o  código cp-no-mmap.c detalhando melhor meu comentário!

/**
 * Autor: Jorge Pereira 
 * Data: Thu Oct  9 22:47:47 BRT 2008
 * Desc: Exemplo de um codigo para copia de arquivos.
 * Arquivo: cp-no-mmap.c
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <err.h>

#define IO_SIZE 4096

int
main(int argc, char *argv[])
{
int32_t fdr, fdw;
ssize_t reads;

/* Como de costume, temos um buffer temporario para auxiliar a copia */
char buf[IO_SIZE];

if(argc < 3)
{
  printf("Usage: %s  \n", argv[0]);
  exit(EXIT_FAILURE);
}

/* Arquivo de origem */
fdr = open(argv[1], O_RDONLY);
if(fdr < 0)
{
  err(1, "Falha ao abrir (%s)", argv[1]);
  exit(EXIT_FAILURE);
}

/* Arquivo de destino */
fdw = open(argv[2], O_CREAT | O_RDWR, 0644);
if(fdw < 0)
{
  err(1, "Falha ao abrir (%s)", argv[2]);
  exit(EXIT_FAILURE);
}

/* Inicio da copia */
while ((reads = read(fdr, buf, sizeof(buf))) > 0)
{
  if(write(fdw, buf, reads) < reads)
  {
    err(1, "Problemas com write()");
    break;
  }
}

/* fechando os descritores */
close(fdr);
close(fdw);

return EXIT_SUCCESS;
}

O Arquivo de exemplo se chama “Prison.Break.S04E07.HDTV.XviD-0TV.avi”, abaixo segue o tamanho dele para termos idéia em relação ao tempo de cópia!

[jpereira@jiraya blog]$ ls -lh Prison.Break.S04E07.HDTV.XviD-0TV.avi
-rw-r–r– 1 jpereiran jpereiran 351M 2008-10-09 22:06 Prison.Break.S04E07.HDTV.XviD-0TV.avi
[jpereira@jiraya blog]$ gcc -o cp-no-mmap cp-no-mmap.c
[jpereira@jiraya blog]$ time ./cp-no-mmap Prison.Break.S04E07.HDTV.XviD-0TV.avi Prison.Break.S04E07.HDTV.XviD-0TV.avi.copia
real 0m3.115s
user 0m0.008s
sys 0m2.496s
[jpereiran@jiraya blog]$

Agora veremos exemplo do mesmo código chamado cp-mmap.c utilizando uma chamada mmap(), fazendo com que todos os bytes lidos pela chamada read() sejam salvos em uma região de memória previamente mapeada para que possamos efetuar operações de leitura, e escrita caso seja necessário! repare que não esta sendo utilizado um buffer auxiliar para salvar os dados do read(), como e feito de costume! :)

/**
 * Autor: Jorge Pereira 
 * Data: Thu Oct  9 22:47:47 BRT 2008
 * Desc: Exemplo de um codigo para copia de arquivos utilizando mmap()
 * Arquivo: cp-mmap.c
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <err.h>

#define IO_SIZE 4096

int
main(int argc, char *argv[])
{
int32_t fdr, fdw, fdm;
ssize_t reads;
/* repare que ao inves de utilizarmos um buffer auxilia, iremos ter um ponteiro apenas */
void *mem;

if(argc < 3)
{
  printf("Usage: %s  \n", argv[0]);
  exit(EXIT_FAILURE);
}

/* Arquivo de origem */
fdr = open(argv[1], O_RDONLY);
if(fdr < 0)
{
  err(1, "Falha ao abrir (%s)", argv[1]);
  exit(EXIT_FAILURE);
}

/* Arquivo de destino */
fdw = open(argv[2], O_CREAT | O_RDWR, 0644);
if(fdw < 0)
{
  err(1, "Falha ao abrir (%s)", argv[2]);
  exit(EXIT_FAILURE);
}

/* tal device abaixo ira prover caracteres nulos '\0', estaremos apenas fazendo
* o mapeamento do dispositivo para uma regiao de memoria com tamanho definido
* neste caso por "IO_SIZE", com protecao R+W e sendo visivel apenas para o
* processo corrente, sem offsets referente a paginas de memoria.
*
* 1) Abrindo o device /dev/zero
* 2) Fazendo o mapeamento na linha 67
*
* OBS: Na duvida consulte a man do mmap(), e veja um exemplo utilizando mais
* detalhes conforme sua necessidade!
*/
if((fdm = open("/dev/zero", O_RDWR)) < 0)
{
  err(1, "Falha ao abrir (%s)", argv[2]);
  exit(EXIT_FAILURE);
}
mem = mmap(NULL, IO_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fdm, 0);
close(fdm);

/**
* Inicio da copia, repare que estamos lendo do disco e "copiando" para regiao
* de  memoria mapeada, e escrevendo apartir do mesmo mapeamento! funcionando
* como uma variavel temporaria.
*/
while ((reads = read(fdr, mem, IO_SIZE)) > 0)
{
  if(write(fdw, mem, reads) < reads)
  {
    err(1, "Problemas com write()");
    break;
 }
}

munmap(mem, IO_SIZE);

/* fechando os descritores */
close(fdr);
close(fdw);

return EXIT_SUCCESS;
}

Abaixo vamos compilar e fazer o mesmo teste, copiando o mesmo arquivo!
[jpereira@jiraya blog]$ gcc -o cp-mmap cp-mmap.c
[jpereira@jiraya blog]$ time ./cp-mmap Prison.Break.S04E07.HDTV.XviD-0TV.avi Prison.Break.S04E07.HDTV.XviD-0TV.avi.copia
real 0m2.777s
user 0m0.020s
sys 0m2.056s
[jpereiran@jiraya blog]$

Conclusão

Repare que o ganho e imperceptível quando executamos tais rotinas em computadores x86 compostos de processadores de alta capacidade, sem falar dos discos rígidos de alta rotação! certo?
Porém quando você está trabalhando em um ambiente embarcado limitado de recursos, que quaisquer ganho de performance e velocidade e bem vindo! este foi meu caso em que precisava-se efetuar operações de I/O em um SD-CARD com um processador ARM um tanto quanto “humilde”, e tais dados além de ser copiados eram necessário gerar chaves MD5 entre outros detalhes com tal stream de dado, lembro que o ganho ficou por volta de 20% e 30% com o mapeamento utilizando mmap(). Dúvidas e comentários serão bem vindos

Referências

  • man 2 open
  • man 2 mmap
  • http://www.gnu.org/software/libtool/manual/libc/Memory_002dmapped-I_002fO.html

Autor

Jorge Pereira