Invalid argument in mmap

Asked

Viewed 89 times

0

I want to write to the "server.log" file and when I compile the code, the result is "Invalid argument" in the "mmap" function".

 void MMP(){
    char* addr;
    int fd;
    struct stat sb;

    fd = open("server.log", O_WRONLY | O_CREAT, 0700);

    //para obter o tamanho do ficheiro
    if(fd == -1){
        perror("Erro na abertura do ficheiro server.log");
        exit(1);
    }

    if(fstat(fd, &sb) == -1){
        perror("Erro no stat");
        exit(1);
    }

    addr = mmap((caddr_t)0, sb.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
    if(addr == -1){
       perror("Erro no mapeamento do ficheiro em memória partilhada");
       exit(-1);
    }

}
  • put the parameter MAP_ANONYMOUS next to MAP_PRIVATE, will look like this MAP_ANONYMOUS | MAP_PRIVATE

1 answer

0

First we have to make some things clear:

  • Use void* for the variable addr
  • Open the file with Read and Write enabled (O_RDWR). This is necessary so that the mmap() perform the mapping.
  • In the permissions (the mode of open()), to create the file use S_IRWXU instead of 0700.
  • If the size of server log. for zero, to write to the file you need to increase it (and the mmap() unaccepted length zero). (see next item)
  • There’s a problem with you using the argument MAP_PRIVATE in mmap():  With MAP_PRIVATE, as it says in the manual user@host:~$ man 2 mmap, what you write in the mapped region will not be written in the file server log.; and also only its process has access to the mapped region (not shared). To write in server log., as much as to have shared mapping, you need to use MAP_SHARED, whose implementation is a little different and I will show it below.
  • If the offset is different from zero, he accurate be a multiple of the value returned when you call the function sysconf(_SC_PAGE_SIZE).
  • Compare addr with MAP_FAILED instead of -1.
  • Remember at the end of closing the file close (fd) and also "wean" it with munmap(addr, mapped region size).
  • And also remember to include all headers (headers) needed at the beginning of the code.

Just in case:

prototype of open()
  int open(const char *pathname, int flags, mode_t mode);

prototype of mmap()
  void *mmap(void *addr, size_t length, int prot, int flags,
  int fd, off_t offset);

The Code


First I tried to make a version of your code (with MAP_PRIVATE) corrected (I commented her with some of the items I mentioned above):
Compile it with: gcc mmap.c -o mmap

mmap. c

#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int
main (void) {
    int fd;
    void *addr; /* use void*  */
    struct stat sb;

    int retval = 0; /* valor que será retornado por main() */

    /* use O_RDWR ao invés de O_WRONLY ( necessário para o mmap() )
     * e também S_IRWXU ao invés de 00700 */
    if ((fd = open("server.log", O_RDWR | O_CREAT, S_IRWXU)) == -1) {
        perror("open");
        return 1;
    }

    /* fstat() para obter o tamanho do arquivo */
    if (fstat(fd, &sb) == -1) {
        perror("stat");
        retval = 1;
        goto _go_close_fd;
    }

    /* se o tamanho for igual a zero, precisamos aumentá-lo!
     * Caso contrário não conseguiremos escrever nada e mmap() vai falhar */
    if (sb.st_size == 0) {
        printf("file size is zero!\n");
        ftruncate(fd, 1024); /* __define__ o tamanho do arquivo
                              * (não aumenta em cima do que já é) */
        sb.st_size = 1024;
    }

    /* use NULL no primeiro argumento */
    /* E se quiser usar outro offset, este precisa ser maior que o valor
     * retornado por  sysconf(_SC_PAGE_SIZE)  */
    addr = mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (addr == MAP_FAILED) { /* use MAP_FAILED ao invés de -1 */
        perror("mmap"); /* "memória partilhada"?  MAP_PRIVATE inibe o
                         * compartilhamento do map(eamento) e também não
                         * escreve suas alterações no arquivo em disco.
                         * Se quiser compartilhar com outros processos e
                         * também "salvar" o que escreveu no arquivo
                         * deve usar MAP_SHARED */
        goto _go_close_fd;
    }

    /* **************************************************************
     * Escreva o que quiser no arquivo mapeado DESDE QUE não exceda o
     * tamanho do arquivo `sb.st_size`. E lembre-se de que nada será
     * "salvo" (transferido) para o arquivo `server.log` no disco
     * ************************************************************** */

    munmap(addr, sb.st_size); /* unmap o file descriptor */
_go_close_fd:
    close(fd); /* precisamos fechar o file descriptor */
    return retval;
}


This other code uses MAP_SHARED and write to the archive (I took some comments to make the code as clean as possible):
Compile it with: gcc mmap_shared.c -o mmap_shared

mmap_shared. c

#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int
main (void) {
    int fd;
    void *addr;
    struct stat sb;

    int retval = 0; /* valor que será retornado por main() */

    if ((fd = open("server.log", O_RDWR | O_CREAT, S_IRWXU)) == -1) {
        perror("open");
        return 1;
    }

    if (fstat(fd, &sb) == -1) {
        perror("stat");
        retval = 1;
        goto _go_close_fd;
    }

    if (sb.st_size == 0) {
        printf("file size is zero!\n");
        ftruncate(fd, 1024);
        sb.st_size = 1024;
    }

    /* se quiser pode mapear além do tamanho do arquivo, mas o que escrever
     * nesta memória além-arquivo não irá ter efeito nele :-) */
    addr = mmap(NULL, sb.st_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        goto _go_close_fd;
    }

    /* **************************************************************
     * Escreva o que quiser no arquivo mapeado DESDE QUE não exceda o
     * tamanho do arquivo `sb.st_size`. Exemplo:
     * ************************************************************** */
    snprintf((char*) addr, sb.st_size, "Hello World!\n");

    /* vamos usar msync() para ter certeza que nossa informação irá para o
     * arquivo em disco!
     * msync() faz um flush nos dados :-)
     * -> A flag MS_SYNC diz que msync só irá retornar para a gente quando
     *    a informação for escrita no arquivo */
    msync(addr, sb.st_size, MS_SYNC);

    munmap(addr, sb.st_size); /* unmap o file descriptor */
_go_close_fd:
    close(fd); /* precisamos fechar o file descriptor */
    return retval;
}

What I changed from the first to this second code was the flag MAP_SHARED, the snprintf() that I added and the msync() which guarantees the writing in the file.

For more details see these manuals (in English):
ftruncate, mmap, msync, open and snprintf

Browser other questions tagged

You are not signed in. Login or sign up in order to post.