Os ambientes POSIX fornecem pelo menos duas maneiras de acessar arquivos. Há o padrão de chamadas de sistema open()
, read()
, write()
, e amigos, mas também há a opção de usar mmap()
para mapear o arquivo para a memória virtual.
Quando é preferível usar um sobre o outro? Quais são as vantagens individuais que merecem, incluindo duas interfaces?
Respostas:
mmap
é ótimo se você tiver vários processos acessando dados somente leitura do mesmo arquivo, o que é comum no tipo de sistema de servidor que escrevo.mmap
permite que todos esses processos compartilhem as mesmas páginas de memória física, economizando muita memória.mmap
também permite que o sistema operacional otimize as operações de paginação. Por exemplo, considere dois programas; programaA
que lê um1MB
arquivo em um buffer criando commalloc
e programa B commmaps
o arquivo de 1 MB na memória. Se o sistema operacional precisar trocar parte daA
memória, ele deverá gravar o conteúdo do buffer para trocar antes de poder reutilizar a memória. NoB
caso, todasmmap
as páginas d não modificadas podem ser reutilizadas imediatamente, porque o SO sabe como restaurá-las a partir do arquivo existente de onde elas forammmap
. (O sistema operacional pode detectar quais páginas não são modificadas, marcando inicialmentemmap
as páginas graváveis como somente leitura e detectando falhas seg , semelhante à estratégia Copiar na gravação ).mmap
também é útil para comunicação entre processos . Você podemmap
um arquivo como leitura / gravação nos processos que precisam se comunicar e, em seguida, usar primitivas de sincronização nammap'd
região (é para isso que serve oMAP_HASSEMAPHORE
sinalizador).Um lugar que
mmap
pode ser estranho é se você precisar trabalhar com arquivos muito grandes em uma máquina de 32 bits. Isso ocorre porquemmap
é necessário encontrar um bloco de endereços contíguo no espaço de endereço do processo que seja grande o suficiente para caber em todo o intervalo do arquivo que está sendo mapeado. Isso pode se tornar um problema se o seu espaço de endereço ficar fragmentado, onde você poderá ter 2 GB de espaço livre, mas nenhum intervalo individual poderá caber em um mapeamento de arquivo de 1 GB. Nesse caso, talvez seja necessário mapear o arquivo em pedaços menores do que você gostaria de ajustá-lo.Outro constrangimento potencial
mmap
em substituição à leitura / gravação é que você precisa iniciar o mapeamento com desvios do tamanho da página. Se você quiser apenas obter alguns dados em offsetX
, precisará corrigi-los para que sejam compatíveismmap
.E, finalmente, de leitura / gravação são a única maneira que você pode trabalhar com alguns tipos de arquivos.
mmap
não pode ser usado em coisas como tubos e ttys .fonte
MAP_HASSEMAPHORE
é específico para o BSD.Uma área em que achei o mmap () não ser uma vantagem foi ao ler arquivos pequenos (abaixo de 16K). A sobrecarga da página que falhou ao ler o arquivo inteiro foi muito alta em comparação com apenas uma chamada de sistema read (). Isso ocorre porque o kernel às vezes pode satisfazer uma leitura inteiramente em seu intervalo de tempo, o que significa que seu código não muda. Com uma falha de página, parecia mais provável que outro programa fosse agendado, tornando a operação do arquivo com uma latência mais alta.
fonte
malloc
um pedaço de memória e colocar 1read
nele. Isso permite ter o mesmo código que manipula os mapas de memória manipulados em malloc'ed.read
acessos seja maior que a sobrecarga da manipulação de memória virtual.mmap
é necessário atualizar 4 entradas na tabela de páginas. Mas usarread
para copiar em um buffer de 16K também envolve a atualização de entradas da tabela de 4 páginas, sem mencionar que ele precisa copiar o 16K no espaço de endereço do usuário. Então, você poderia elaborar as diferenças de operações na tabela de páginas e como é mais carommap
?mmap
tem a vantagem quando você tem acesso aleatório a arquivos grandes. Outra vantagem é que você o acessa com operações de memória (memcpy, ponteiro aritmético), sem se preocupar com o buffer. Às vezes, a E / S normal pode ser bastante difícil ao usar buffers quando você possui estruturas maiores que o seu buffer. O código para lidar com que muitas vezes é difícil de acertar, o mmap é geralmente mais fácil. Dito isto, existem algumas armadilhas ao trabalhar commmap
. Como as pessoas já mencionaram,mmap
é muito caro configurar, portanto vale a pena usá-lo apenas para um determinado tamanho (variando de máquina para máquina).Para acessos seqüenciais puros ao arquivo, nem sempre é a melhor solução, embora uma chamada apropriada
madvise
possa atenuar o problema.Você precisa ter cuidado com as restrições de alinhamento de sua arquitetura (SPARC, itanium). Com as E / S de leitura / gravação, os buffers costumam estar alinhados adequadamente e não se interceptam ao remover a referência de um ponteiro fundido.
Você também deve ter cuidado para não acessar fora do mapa. Isso pode acontecer facilmente se você usar funções de seqüência de caracteres no seu mapa e seu arquivo não contiver um \ 0 no final. Ele funcionará na maioria das vezes quando o tamanho do arquivo não for múltiplo do tamanho da página, pois a última página será preenchida com 0 (a área mapeada sempre terá o tamanho de um múltiplo do tamanho da página).
fonte
Além de outras respostas legais, uma citação da programação do sistema Linux, escrita pelo especialista do Google, Robert Love:
fonte
O mapeamento de memória tem um potencial para uma enorme vantagem de velocidade em comparação com as E / S tradicionais. Ele permite que o sistema operacional leia os dados do arquivo de origem à medida que as páginas do arquivo mapeado na memória são tocadas. Isso funciona criando páginas com falha, que o sistema operacional detecta e, em seguida, o sistema operacional carrega os dados correspondentes do arquivo automaticamente.
Isso funciona da mesma maneira que o mecanismo de paginação e geralmente é otimizado para E / S de alta velocidade, lendo dados nos limites e tamanhos das páginas do sistema (geralmente 4K) - um tamanho para o qual a maioria dos caches do sistema de arquivos é otimizada.
fonte
pread
. No Solaris 9 Sparc (V890), o acesso ao cabeçote é entre 2 e 3 vezes mais lento que omemcpy
do mmap. Mas você está certo de que o acesso seqüencial não é necessariamente mais rápido.Uma vantagem que ainda não está listada é a capacidade de
mmap()
manter um mapeamento somente leitura como páginas limpas . Se alguém alocar um buffer no espaço de endereço do processo e usá-loread()
para preenchê-lo de um arquivo, as páginas de memória correspondentes a esse buffer estarão sujas desde que foram gravadas.Páginas sujas não podem ser descartadas da RAM pelo kernel. Se houver espaço de troca, eles poderão ser paginados para troca. Mas isso é caro e em alguns sistemas, como pequenos dispositivos incorporados com apenas memória flash, não há troca. Nesse caso, o buffer ficará preso na RAM até o processo terminar, ou talvez devolva-o
madvise()
.As
mmap()
páginas não gravadas estão limpas. Se o kernel precisar de RAM, ele pode simplesmente descartá-los e usar a RAM em que as páginas estavam. Se o processo que teve o mapeamento acessá-lo novamente, isso causa uma falha na página. O kernel recarrega novamente as páginas do arquivo de onde vieram originalmente . Da mesma forma que eles foram preenchidos em primeiro lugar.Isso não requer mais de um processo usando o arquivo mapeado para ser uma vantagem.
fonte
read()
, as páginas em que os dados são colocados eventualmente não têm relação com o arquivo do qual podem ter vindo. Portanto, eles não podem ser escritos, exceto para trocar de espaço. Se um arquivo émmap()ed
e o mapeamento é gravável (em vez de somente leitura) e gravado em, isso depende se o mapeamento foiMAP_SHARED
ouMAP_PRIVATE
. Um mapeamento compartilhado pode / deve ser gravado no arquivo, mas um particular não pode ser.