How to map an empty file using mmap

2019-03-05 03:09发布

I am trying to create an empty file if it does not exists. And than map it using mmap() so, that i can pass it to my other program for writing. I am not sure which arguments for mmap are suitable for an empty file. My code works for non empty files but gives error "Invalid argument" if file is empty

Code program1 (only creates an empty file if not exists)

int i;
int fd = open("/home/sungmin/dummy_programs/dummy.txt", O_RDONLY | O_CREAT, 0777);
char *pmap;
pid_t child;

if (fd == -1)
{
    perror("Error opening file for writing");
    exit(EXIT_FAILURE);
}        

struct stat fileInfo = {0};

if (fstat(fd, &fileInfo) == -1)
{
    perror("Error getting the file size");
    exit(EXIT_FAILURE);
}

/*if (fileInfo.st_size == 0)
{
    fprintf(stderr, "Error: File is empty, nothing to do\n");
    exit(EXIT_FAILURE);
}*/

pmap = mmap(0, fileInfo.st_size, PROT_READ | PROT_EXEC  , MAP_ANONYMOUS, fd, 0);
if (pmap == MAP_FAILED)
{
    close(fd);
    perror("Error mmapping the file");
    exit(EXIT_FAILURE);
}

/* Calling fork function */
if((child=fork())==0){

printf("Iam Child process\n\n");
static char *argv[]={"This is some sample text. I need to write this text in my dummy file.","/home/sungmin/dummy_programs/dummy.txt",NULL};
execv("/home/sungmin/dummy_programs/pro2",argv);
    exit(127); 
}
else { 
    printf("Iam parent, waiting for child process to exit\n\n");        
    waitpid(child,0,0); 
    printf("Existing parent\n\n");
}

/* Don't forget to free the mmapped memory*/
if (munmap(pmap, fileInfo.st_size) == -1)
{
    close(fd);
    perror("Error un-mmapping the file");
    exit(EXIT_FAILURE);
}

/* Un-mmaping doesn't close the file, so we still need to do that.*/
close(fd);

Code program2 (opens same file as program1 and writes text passed by program1)

 size_t i;
int fd;
char *pmap;
pid_t child;
struct stat fileInfo = {0};
const char *text = argv[0];

fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);

if (fd == -1)
{
    perror("Error opening file for writing");
    exit(EXIT_FAILURE);
}  

size_t textsize = strlen(text) + 1; // + \0 null character

if (lseek(fd, textsize-1, SEEK_SET) == -1)
{
    close(fd);
    perror("Error calling lseek() to 'stretch' the file");
    exit(EXIT_FAILURE);
}

if (write(fd, "", 1) == -1)
{
    close(fd);
    perror("Error writing last byte of the file");
    exit(EXIT_FAILURE);
}

pmap = mmap(0, textsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if (pmap == MAP_FAILED)
{
    close(fd);
    perror("Error mmapping the file");
    exit(EXIT_FAILURE);
}

/* Writting users text to file */
for (i = 0; i < textsize; i++)
{
    pmap[i] = text[i];  
}   

// Write it now to disk
if (msync(pmap, textsize, MS_SYNC) == -1)
{
    perror("Could not sync the file to disk");
}

/* Don't forget to free the mmapped memory*/
if (munmap(pmap, textsize) == -1)
{
    close(fd);
    perror("Error un-mmapping the file");
    exit(EXIT_FAILURE);
}

/* Un-mmaping doesn't close the file, so we still need to do that.*/
close(fd);  

1条回答
一纸荒年 Trace。
2楼-- · 2019-03-05 03:23

You need to use truncate to extend the file length after creating it before mapping it.

Yes, the function name sounds wrong, but truncate can actually set the file length to any number. Be sure to use a multiple of 4K for best results.

Then, if you want to keep the mapping open to see data between Program 1 and 2, you need to get rid of ANONYMOUS and map with MAP_SHARED in Program 1. A mapping that isn't shared will not show changes made by other programs. Or it might, if it has to reload from disk. It's weird, don't mix SHARED and not-SHARED mappings.

Once you've changed Program 1 to use truncate, take that lseek and write code out of Program 2. The file will already have been created and extended by Program 1.

查看更多
登录 后发表回答