Why is remap_file_pages() failing in this example? - c

The following C code illustrates a problem I'm seeing on Linux 2.6.30.5-43.fc11.x86_64:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
char buf[1024];
void *base;
int fd;
size_t pagesz = sysconf(_SC_PAGE_SIZE);
fd = open("<some file, at least 4*pagesz in length>", O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
base = mmap(0, 4*pagesz, PROT_READ, MAP_SHARED, fd, 0);
if (base < 0) {
perror("mmap");
close(fd);
return 1;
}
memcpy(buf, (char*)base + 2*pagesz, 1024);
if (remap_file_pages(base, pagesz, 0, 2, 0) < 0) {
perror("remap_file_pages");
munmap(base, 4*pagesz);
close(fd);
return 1;
}
printf("%d\n", memcmp(buf, base, 1024));
munmap(base, 4*pagesz);
close(fd);
return 0;
}
This always fails with remap_file_pages() returning -1 and errno set to EINVAL. Looking at the kernel source I can see all the conditions in remap_file_pages() where it might fail but none of them seem to apply to my example. What's going on?

It's caused by the file being opened O_RDONLY. If you change the open mode to O_RDWR, it works (even if the mmap() still specifies just PROT_READ).
This code in do_mmap_pgoff is the root cause - it only marks the vma as VM_SHARED if the file was opened for writing:
vm_flags |= VM_SHARED | VM_MAYSHARE;
if (!(file->f_mode & FMODE_WRITE))
vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
So in remap_file_pages(), you fail on the first check:
if (!vma || !(vma->vm_flags & VM_SHARED))
goto out;

Related

how do i chunk a 1000MB file using mmapdemo

I'm following a tutorial from BeeJ's website and i've learnt quite a bit of it.
I'm in that stage of experimentation and want to somehow build upon this code.
In short, say the user types an address: 1050576 for a file that's roughly 1000MB, the program (shown below) should map the chunk in the file data up to a position where 1050576 falls between chunk beginning and its length.
When i run the code, it says the max address size is 924 and so i can't really chunk a file that's 1000mb.
Could someone help explain / provide an example on how i can tackle this?
/*
** mmapdemo.c -- demonstrates memory mapped files lamely.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int fd, offset;
char *data;
struct stat sbuf;
if (argc != 2) {
fprintf(stderr, "usage: mmapdemo offset\n");
exit(1);
}
if ((fd = open("mmapdemo.c", O_RDONLY)) == -1) {
perror("open");
exit(1);
}
if (stat("mmapdemo.c", &sbuf) == -1) {
perror("stat");
exit(1);
}
offset = atoi(argv[1]);
if (offset < 0 || offset > sbuf.st_size-1) {
fprintf(stderr, "mmapdemo: offset must be in the range 0-%ld\n", sbuf.st_size-1);
exit(1);
}
if ((data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (caddr_t)(-1)) {
perror("mmap");
exit(1);
}
printf("byte at offset %d is '%c'\n", offset, data[offset]);
return 0;
}

Different results when running inside a docker container

I am trying to run some code based on this libaio sample:
https://oxnz.github.io/2016/10/13/linux-aio/#example-1
I added the O_DIRECT flag according to libaio's documentation.
It seems to work inside my ubuntu 16.04 desktop machine (hello is written to /tmp/test).
However, when I compile and run the same sample inside a docker container nothing is written to the file. when running inside gdb I can see that an event is read by io_getevents and the result is set to -22 (EINVAL).
Any ideas?
This is my modified code
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <libaio.h>
int main() {
io_context_t ctx;
struct iocb iocb;
struct iocb * iocbs[1];
struct io_event events[1];
struct timespec timeout;
int fd;
fd = open("/tmp/test", O_WRONLY | O_CREAT | O_DIRECT) ;
if (fd < 0) err(1, "open");
memset(&ctx, 0, sizeof(ctx));
if (io_setup(10, &ctx) != 0) err(1, "io_setup");
const char *msg = "hello";
io_prep_pwrite(&iocb, fd, (void *)msg, strlen(msg), 0);
iocb.data = (void *)msg;
iocbs[0] = &iocb;
if (io_submit(ctx, 1, iocbs) != 1) {
io_destroy(ctx);
err(1, "io_submit");
}
while (1) {
timeout.tv_sec = 0;
timeout.tv_nsec = 500000000;
int ret = io_getevents(ctx, 0, 1, events, &timeout);
printf("ret=%d\n", ret);
if (ret == 1) {
close(fd);
break;
}
printf("not done yet\n");
sleep(1);
}
io_destroy(ctx);
return 0;
}
The filesystem inside the container is likely to be different to that of the host's filesystem (on modern setups is likely to be overlayfs but on older systems it could be aufs). For O_DIRECT on an open to work a device/filesystem has to at least "support" it (note the scare quotes) and it's likely your container's filesystem does not.

How to use shm_open with mmap properly

I am trying to create a shared memory area using examples and documentation I found online. My goal is IPC , so I can make different processes talk to each other.
This my C file
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
int main (int argc, char *argv[])
{
struct stat sb;
off_t len;
char *p;
int fd;
fd = shm_open("test", O_RDWR | O_CREAT); //,S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open");
return 1;
}
if (fstat(fd, &sb)==-1){
perror("fstat");
return 1;
}
/*if (!S_ISREG(sb.st_mode)){
fprintf(stderr, "%s is not a file\n",fileName);
return 1;
}*/
p = mmap(0, sb.st_size, PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED){
perror("mmap");
return 1;
}
if (close(fd)==-1) {
perror("close");
return 1;
}
for (len = 0; len < sb.st_size; len++) {
putchar(p[len]);
}
if (munmap(p, sb.st_size) == -1) {
perror("munmao");
return 1;
}
fprintf(stderr,"\n");
return 0;
}
The problem is that I am getting a mmap: Invalid argument. I assume something is wrong with fd but have no clue how to fix it, any help would be appreciated. I am on Yosemite using latest XCODE .
You need to extend the size of the shared memory mapping, at least the first time when you create it. Right now its size is 0, and mmap is not going to allow you to make a zero length mapping.
So instead of your fstat() call, do e.g.:
size_t len = 4096;
if (ftruncate(fd, len) == -1) {
perror("ftruncate");
return 1;
}
And pass this len to mmap().
Your addr parameter is set to 0, which might be reserved. Did you mean to use NULL? This would be different than 0.

mmap error on linux (using somethingelse)

Exactly, I thought that I finished my project until compiling isn't accepted on ubuntu because of mmap(). I'm trying to access(read) files by using fork(). It's okey. But, When I want to count number of read files, entered folder(directories) and child, I powned! How and What can I use or change mmap() because I get error: ‘MAP_ANON’ undeclared (first use in this function)|
. On mac, It's okey but on ubuntu error. Thank you for helps.
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <errno.h>
#include <ftw.h>
#include <ctype.h>
#include <sys/mman.h>
#define MAX_PATH_LEN 2048
static int *wordCount = 0;
static int *childCount = 0;
static int *folderCount = 0;
int relatedWord(const char *arr);
int checkWord(const char arr[], int size);
void err_sys(const char *msg);
int disp(const char *filepath, const struct stat *finfo, int flag, struct FTW *ftw);
int main(int argc, char *argv[])
{
//struct stat finfo;
//int count = 0;
wordCount = mmap(NULL, sizeof *wordCount, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON, -1, 0);
childCount = mmap(NULL, sizeof *childCount, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON, -1, 0);
folderCount = mmap(NULL, sizeof *folderCount, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON, -1, 0);
if (argc != 2) {
fprintf(stderr, "Wrong number of arguments!\nUsage: dirwalk6 <path>\n");
exit(EXIT_FAILURE);
}
if (nftw(argv[1], disp, 20, 0) < 0)
err_sys("ntfw");
printf( "\nTotal words = %d\n\n", *wordCount);
printf( "\nTotal folders = %d\n\n", *folderCount);
printf( "\nTotal childs = %d\n\n", *childCount);
return 0;
}
int disp(const char *filepath, const struct stat *finfo, int flag, struct FTW *ftw)
{
int count = 0; /* number of words */
switch (flag) {
case FTW_F: /* determining file */
printf("%*s%s\n", ftw->level * 4, "", filepath);
pid_t pid;
pid=fork();
if(pid < 0)
{
perror("Error corresponding to fork()");
}
else if (pid == 0)
{
count += relatedWord(filepath);
*wordCount += count;
*childCount += 1;
exit(1);
}
else
{
while( pid != wait(0) ) ;
}
// printf( "word = %d, file = %s \n", count,filepath);
break;
case FTW_D: /* determining folder */
printf("%*s%s\n", ftw->level * 4, "", filepath + ftw->base);
*folderCount += 1;
break;
}
return 0;
}
From the man page for mmap(2) (my bold):
Certain flags constants are defined only if either _BSD_SOURCE or _SVID_SOURCE is defined. (Requiring _GNU_SOURCE also suffices, and requiring that macro specifically would have been more logical, since these flags are all Linux specific.) The relevant flags are: MAP_32BIT, MAP_ANONYMOUS (and the synonym MAP_ANON), MAP_DENYWRITE, MAP_EXECUTABLE, MAP_FILE, MAP_GROWSDOWN, MAP_HUGETLB, MAP_LOCKED, MAP_NONBLOCK, MAP_NORESERVE, MAP_POPULATE, and MAP_STACK.
So you will need to define _BSD_SOURCE, _SVID_SOURCE or (if I read that right) _GNU_SOURCE at the top of the file, but in any event prior to
#include <sys/mman.h>
As per #mafso's comment, best to do this prior to any system header include (not least in case an another header itself includes <sys/mman.h>

‘ltrunc’ was not declared in this scope

I have problem with creating shared memory between two processes.
I getting this error, and I don't know what to do because I think a have all libraries included.
Log...
g++ exc8.c -o exc8
exc8.c: In function ‘int main(int, char**)’:
exc8.c:29:37: error: ‘ltrunc’ was not declared in this scope
size = ltrunc(fd, B_SIZE, SEEK_SET);
Code...
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#define B_SIZE 4
char* memory = new char[4];
int main(int argc, char *argv[]) {
int size, fd;
char *buf;
char memory[4];
fd = shm_open(memory, O_RDWR|O_CREAT, 0774);
if (fd < -0) {
perror("open");
exit(-1);
}
size = ltrunc(fd, B_SIZE, SEEK_SET);
if(size < 0) {
perror("trunc");
exit(-1);
}
buf = (char *)mmap(0, B_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(buf == NULL) {
perror("map");
exit(-1);
}
}
I don't know where ltrunc() comes from, but
you can set the size of a shared memory object with ftruncate():
if (ftruncate(fd, B_SIZE) == -1) {
// Handle error
}
(from http://pubs.opengroup.org/onlinepubs/009695299/functions/shm_open.html).
ltrunc is not a standard function. It seems defined in QNX Platform using qcc as the compiler, which truncates a file at given position. Probably POSIX provides the truncate() and ftruncate() functions for the job.

Resources