Kaleb Pederson
Kaleb Pederson

Reputation: 46479

system call to map memory to a file descriptor (inverse mmap)?

I want to be able to map memory to a file descriptor so I can use some existing functions that need a file descriptor. Here's essentially what I'm looking for:

void do_operation1(int fd);

char data[DATA_MAX] = { /* embedded binary data */ };
int fd = addr_to_fd(data, DATA_MAX);

do_operation1(fd);
/* ... operate on fd ... */

What system call, or calls, can I use to accomplish this?

Upvotes: 6

Views: 3792

Answers (4)

emandret
emandret

Reputation: 1399

You cannot map "some existing memory buffer" to a file descriptor. As said in a comment above, the fmemopen() function associates a memory buffer with a "FILE *" stream pointer which can be manipulated with the libc-provided streams functions. No file descriptor is allocated: the "FILE *" stream is a high-level abstraction and is NOT compatible with a file descriptor which is a low-level handle.

Instead, you may want to allocate a new shared memory buffer and map it to a file descriptor. This is widely used and known as a "memory-mapped file" in the Linux jargon.

You can use a file descriptor obtained with open() that refers to a file or a file descriptor obtained with shm_open() that refers to a shared memory object. Any file descriptor handle will do the job. You can then invoke mmap() to map the file descriptor to a shared memory buffer.

Note: mmap() will fail if the file descriptor refers to a non-regular file such as a pipe, a socket or a character device file (e.g., /dev/ttys001). Due to this, you cannot usually create a memory-mapped file for the STDIN, STDOUT or STDERR file descriptors.

You can manipulate the memory buffer in an array-like fashion and modifications to the memory-mapped file are committed to disk. The opposite is also true, with any modifications made to the file (e.g., with a write() syscall) committed to memory as well.


The following snippet opens a file of your choice and maps it into memory. It will print the first 256 characters and replace them with "*" in the original file. Compile it with cc mmap_test.c -o mmap_test on a POSIX-compliant system.

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

int main(int ac, char *av[])
{
    int pagesize, fd;
    unsigned char *data;

    if ( ac < 2 ) {
        printf("Usage: %s <filepath>\n", av[0]);
        return 1;
    }

    pagesize = getpagesize();

    if ( (fd = open(av[1], O_RDWR)) == -1 ) {
        perror("Error: cannot open file for reading");
        return 1;
    }

    if ( (data = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED ) {
        perror("Error: cannot create memory-mapped file");
        return 1;
    }

    write(1, data, 256);
    memset(data, '*', 256);

    return 0;
}

Upvotes: 1

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136425

Sure, just open(argv[0], ...), scan through the file to find where your binary data starts, lseek() there and done. That file won't have the length of your binary data of course.

Upvotes: 2

Enquimot
Enquimot

Reputation: 96

You should Check out shm_open().

Upvotes: 3

Randy Proctor
Randy Proctor

Reputation: 7544

Some implementations have fmemopen(). (Then of course you have to call fileno()).

If yours doesn't, you can build it yourself with fork() and pipe().

Upvotes: 7

Related Questions