файлы, отображенные в память - PullRequest
2 голосов
/ 07 декабря 2011

Я написал код для записи содержимого в сопоставленный буфер, который сопоставлен с помощью системного вызова mmap ().После того, как я сделал некоторые изменения в отображенном буфере, я вызвал msync (). Он должен обновиться до файла на диске.

Но он не внес никаких изменений в файл на диске.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/mman.h>
#include<fcntl.h>
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH
#define MAX 150

main(int argc,char *argv[])
{
int fd,ret,len;
long int len_file;
struct stat st;
char *addr;
char buf[MAX];


if(argc > 1)
{
    if((fd = open(argv[1],O_RDWR | O_APPEND | O_CREAT ,FILEMODE)) < 0)
        perror("Error in file opening");

    if((ret=fstat(fd,&st)) < 0)
        perror("Error in fstat");

    len_file = st.st_size;

           /*len_file having the total length of the file(fd).*/


    if((addr=mmap(NULL,len_file,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0)) == MAP_FAILED)
        perror("Error in mmap");

    len = len_file; 

    while((fgets(buf,MAX,stdin)) != NULL)
    {
        strcat(addr+len,buf);
        printf( "Val:%s\n",addr ) ; //Checking purpose
        len = len + (strlen(buf));
    }
    if((msync(addr,len,MS_SYNC)) < 0)
        perror("Error in msync");

    if( munmap(addr,len) == -1)
        printf("Error:\n");
    printf("addr %p\n",addr);
}
else
{
    printf("Usage a.out <filename>\n");
}
}

Ответы [ 2 ]

17 голосов
/ 07 декабря 2011

Если вы хотите, чтобы ваши изменения были отражены в файле на диске, вы должны сопоставить файл как MAP_SHARED, а не MAP_PRIVATE.

Кроме того, вы не можете расширить файл, просто записав за пределыконец отображения.Вы должны использовать ftruncate(), чтобы расширить файл до нового размера, затем измените отображение, чтобы включить новую часть файла.Переносимый способ изменить отображение - это удалить отображение, а затем воссоздать его с новым размером;в Linux вы можете вместо этого использовать mremap().

Ваши переменные len и len_file должны иметь тип size_t, и вы должны использовать memcpy() вместо strcat(), поскольку вы точно знаетедлина строки, где именно вы хотите ее скопировать, и вы не хотите скопировать нулевой терминатор.

Следующая модификация вашего кода работает в Linux (используяmremap()):

#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/mman.h>
#include<fcntl.h>
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH
#define MAX 150

int main(int argc,char *argv[])
{
    int fd, ret;
    size_t len_file, len;
    struct stat st;
    char *addr;
    char buf[MAX];

    if (argc < 2)
    {
        printf("Usage a.out <filename>\n");
        return EXIT_FAILURE;
    }

    if ((fd = open(argv[1],O_RDWR | O_CREAT, FILEMODE)) < 0)
    {
        perror("Error in file opening");
        return EXIT_FAILURE;
    }

    if ((ret = fstat(fd,&st)) < 0)
    {
        perror("Error in fstat");
        return EXIT_FAILURE;
    }

    len_file = st.st_size;

    /*len_file having the total length of the file(fd).*/

    if ((addr = mmap(NULL,len_file,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
    {
        perror("Error in mmap");
        return EXIT_FAILURE;
    }

    while ((fgets(buf,MAX,stdin)) != NULL)
    {
        len = len_file;
        len_file += strlen(buf);
        if (ftruncate(fd, len_file) != 0)
        {
            perror("Error extending file");
            return EXIT_FAILURE;
        }
        if ((addr = mremap(addr, len, len_file, MREMAP_MAYMOVE)) == MAP_FAILED)
        {
            perror("Error extending mapping");
            return EXIT_FAILURE;
        }
        memcpy(addr+len, buf, len_file - len);
        printf( "Val:%s\n",addr ) ; //Checking purpose
    }
    if((msync(addr,len,MS_SYNC)) < 0)
        perror("Error in msync");

    if (munmap(addr,len) == -1)
        perror("Error in munmap");

    if (close(fd))
        perror("Error in close");

    return 0;
}
2 голосов
/ 07 декабря 2011

Обратите внимание, что вы предоставили сопоставление для файла, которое точно соответствует размеру файла.Если вы создадите файл при вызове open(2), он будет иметь длину 0, и я не удивлюсь, если ядро ​​не потрудится настроить отображение памяти любого типа с длиной 0отображение.(Может, так и есть? Я никогда не пробовал ...)

Я бы предложил использовать ftruncate(2) для увеличения длины вашего файла до выполнения сопоставления.(Обратите внимание, что расширение файлов с использованием ftruncate(2) не очень переносимо; не все платформы предоставляют расширенные функциональные возможности, и не все драйверы файловой системы поддерживают расширенные функциональные возможности. Подробнее см. На справочной странице вашей системы.)

Вы должны использовать MAP_SHARED отображение ваших изменений файла для сохранения на диск.

Использование perror(3) не совсем корректно;perror(3) будет не завершать вашу программу, поэтому она будет продолжать выполняться с неверными допущениями:

if((ret=fstat(fd,&st)) < 0)
    perror("Error in fstat");

Должно читаться:

if((ret=fstat(fd,&st)) < 0) {
    perror("Error in fstat");
    exit(1);
}

(или exit(EXIT_FAILURE)если вы хотите быть более портативным - я нахожу это немного сложнее для глаз, но я живу в Linux-стране.)

strcat(3) ожидает найти символ ASCII NUL (значение байта 0x00,Представление C '\0') - обычный маркер конца строки C - в конце строки dest.Ваш файл не будет содержать ASCII NUL, если вы создадите его в этой программе - его длина равна нулю, в конце концов - и я не знаю последствий попытки прочитать файл с нулевым байтом через mmap(2).Если файл уже существует и содержит данные, то , вероятно, не имеет ASCII NUL, закодированного в файле.strcat(3) почти наверняка неправильный инструмент для записи в ваш файл.(Никто не хочет ASCII NULs в своих файлах.) Попробуйте вместо этого memcpy(3).

...