I am trying to complete an exercise that is done with system calls and need to allocate memory for a struct *. My code is:
myStruct * entry = (myStruct *)mmap(0, SIZEOF(myStruct), PROT_READ|PROT_WRITE,
MAP_ANONYMOUS, -1, 0);
To clarify, I cannot use malloc() but can use mmap(). I was having no issues with this on Windows in Netbeans, now however I'm compiling and running from command line on Ubuntu I am getting "Segmentation Fault" each time I try to access it.
Is there a reason why it will work on one and not the other, and is mmap() a valid way of allocating memory in this fashion? My worry was I was going to be allocating big chunks of memory for each mmap() call initially, now I just cannot get it to run.
Additionally, the error returned my mmap is 22 - Invalid Argument (I did some troubleshooting while writing the question so the error check isn't in the above code). Address is 0, the custom SIZEOF() function works in other mmap arguments, I am using MAP_ANONYMOUS so the fd and offsetparameters must -1 and 0 respectively.
Is there something wrong with the PROT_READ|PROT_WRITE sections?
You need to specify MAP_PRIVATE in your flags.
myStruct * entry = (myStruct *)mmap(0, SIZEOF(myStruct),
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
From the manual page:
The flags argument determines whether updates to the mapping are
visible to other processes mapping the same region, and whether
updates are carried through to the underlying file. This behavior is
determined by including exactly one of the following values in flags:
You need exactly one of the flags MAP_PRIVATE or MAP_SHARED - but you didn't give either of them.
A complete example:
#include <sys/mman.h>
#include <stdio.h>
typedef struct
{
int a;
int b;
} myStruct;
int main()
{
myStruct * entry = (myStruct *)mmap(0, sizeof(myStruct),
PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (entry == MAP_FAILED) {
printf("Map failed.\n");
}
else {
entry->a = 4;
printf("Success: entry=%p, entry->a = %d\n", entry, entry->a);
}
return 0;
}
(The above, without MAP_PRIVATE of course, is a good example of what you might have provided as a an MCVE. This makes it much easier for others to help you, since they can see exactly what you've done, and test their proposed solutions. You should always provide an MCVE).
The man page for mmap() says that you must specify exactly one of MAP_SHARED and MAP_PRIVATE in the flags argument. In your case, to act like malloc(), you'll want MAP_PRIVATE:
myStruct *entry = mmap(0, sizeof *entry,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
(I've also made this more idiomatic C by omitting the harmful cast and matching the sizeof to the actual variable rather than its type).
Related
so I need to map a file into memory, but at the same time allocate space infront of it to store some data along with it.
Basically I have this:
int fd = open(path, O_RDONLY);
if (fd < 0) {
// error
}
struct stat file_info;
if (fstat(fd, &file_info) == -1) {
close(fd);
// error
}
int length = file_info.st_size;
int* data_pointer = mmap((void*) 0, length, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
and I'd like to have space infront of the memory, maybe like one page or something, where I can do this:
*data_pointer = length; // store length in space before file
// print out contents of file
const char* p = data_pointer + PAGE_SIZE;
for (int i = 0; i < *data_pointer; i++) {
printf("%c", p[i]);
}
printf("\n");
so that I can do this later:
munmap(data_pointer, *data_pointer + PAGE_SIZE);
for example. I've already tried allocating the amount of space I want with malloc and then telling mmap to map into that space with its addr argument, but I don't think that would work 100% of the time, since its more of a hint than an order, and I'm scared that on some platforms, the mmap implementation may ignore the addr argument completely.
Would you know of any better way to accomplish what I want without having to keep track of the size of every mmap call seperately (I'm sharing these pointers between dynamically loaded libraries, some of which do the mmap'ing, some of which do the munmap'ing, and it would be pretty inelegant to have to handle all of that is some kind of seperate list)?
It's late at night and I don't really think I'm making much sense here, but any help or insight would be greatly appreciated regardless. Thanks alot for your time in reading this!
You're on the right track. The missing piece you need is MAP_FIXED:
int* data_pointer = mmap((void*) 0, length + PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(data_pointer == MAP_FAILED) {
perror("mmap");
exit(1);
}
if(mmap(data_pointer + PAGE_SIZE, length, PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, 0) == MAP_FAILED) {
perror("mmap");
exit(1);
}
You correctly pointed out that normally, the address is "more of a hint than an order", but passing MAP_FIXED makes it an order.
If you're worried about safety, man 2 mmap says:
The only safe use for MAP_FIXED is where the address range specified by addr and length was previously reserved using another mapping
And that's exactly this use.
I'm pretty sure my mistake is very evident, but I just can't seem to find where the problem is.
I'm learning how to use mmap() in C, everything looks correct to me, but I get a segmentation fault.
Here is my code:
int n=50;
char * tab = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, -1, 0);
for(int i=0; i<n; i++)
{
tab[i] = 1;
}
Using valgrind, I get an error saying "Invalid write of size 1" at the line where I do tab[i]=1, (I have tried replacing 1 by '1' thinking that maybe a char has a smaller size than an int, but still get the same error), followed by "Address 0xfffff..ff is not stack'd, malloc'd, or (recently) free'd".
I have no idea where my mistake is. Can somebody help me find it?
From man 2 mmap:
The contents of a file mapping (as opposed to an anonymous mapping;
see MAP_ANONYMOUS below), are initialized using length bytes starting
at offset offset in the file (or other object) referred to by the
file descriptor fd.
I suppose that you are trying to create an anonymous mapping (i.e. not backed by a file). In such case, you need to add MAP_ANONYMOUS to the flags, otherwise the system will try to read from the specified fd, which is invalid (-1) and will fail.
The correct code is:
char *tab = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (tab == MAP_FAILED) {
perror("mmap");
exit(1);
}
For the future, note that you can easily detect the error like I did above with a simple call to perror() in case the returned value indicates failure. In your case it should have printed the following:
mmap: Bad file descriptor
Checking the manual again you can see in the "ERRORS" section:
EBADF: fd is not a valid file descriptor (and MAP_ANONYMOUS was not set).
I've been pulling my hairs out for hours on this very basic piece of code and I haven't managed to understand why mmap(2) was failing.
#include <sys/mman.h>
#include <sys/user.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#define NPAGES 50
#define SLABSIZE (PAGE_SIZE * NPAGES) // 200 kB
int
main(int argc, char *argv[])
{
char *slab;
printf("DEBUG: mmap(%#x, %u, %#x, %#x, %d, %u)\n",
NULL, SLABSIZE, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
slab = mmap(NULL, SLABSIZE, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
if (slab == MAP_FAILED)
err(1, "mmap");
}
But when I run it:
$ make mmap
cc mmap.c -o mmap
$ ./mmap
DEBUG: mmap(0, 204800, 0x3, 0x20, -1, 0)
mmap: mmap: Invalid argument
I checked and re-checked mmap(2)'s manpage [1], and it seems that all requirements are OK but I must be missing something.
I'm running Linux kernel 4.8.13.
Thanks.
-- Jeremie
[1] http://man7.org/linux/man-pages/man2/mmap.2.html
When strace ing your program, I see:
mmap(NULL, 204800, PROT_READ|PROT_WRITE, MAP_FILE|MAP_ANONYMOUS, -1, 0) = -1 EINVAL (Invalid argument)
You forgot a MAP_SHARED flag (or a MAP_PRIVATE one). With it (either MAP_SHARED or MAP_PRIVATE, but you need one of them) your program works:
slab = mmap(NULL, SLABSIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
Quoting the mmap(2) man page:
This behavior is determined by including exactly one of the following values in flags:
(emphasis is mine)
MAP_SHARED
Share this mapping. Updates to the mapping are visible to
other processes mapping the same region, and (in the case of
file-backed mappings) are carried through to the underlying
file. (To precisely control when updates are carried through
to the underlying file requires the use of msync(2).)
MAP_PRIVATE
Create a private copy-on-write mapping. Updates to the
mapping are not visible to other processes mapping the same
file, and are not carried through to the underlying file. It
is unspecified whether changes made to the file after the
mmap() call are visible in the mapped region.
So the general advice before pulling your hairs: read once again the documentation; take a nap; read another time the documentation and think about what did you miss.
Another hint would be to use strace(1) on some (or several) existing executable(s). You'll learn a lot.
int fp, page;
char *data;
if(argc > 1){
printf("Read the docs");
exit(1);
}
fp = open("log.txt", O_RDONLY); //Opening file to read
page = getpagesize();
data = mmap(0, page, PROT_READ, 0,fp, 0);
initscr(); // Creating the ncurse screen
clear();
move(0, 0);
printw("%s", data);
endwin(); //Ends window
fclose(fp); //Closing file
return 0;
Here is my code I keep getting a segmentation fault for some reason.
All my header files have been included so that's not the problem (clearly, because its something to do with memory). Thanks in advance.
Edit: Got it - it wasn't being formatted as a string. and also had to use stat() to get the file info rather than getpagesize()
You can't fclose() a file descriptor you got from open(). You must use close(fp) instead. What you do is passing a small int that gets treated as a pointer. This causes a segmentation fault.
Note that your choice of identifier naming is unfortunate. Usually fp would be a pointer-to-FILE (FILE*, as used by the standard IO library), while fd would be a file descriptor (a small integer), used by the kernel's IO system calls.
Your compiler should have told you that you pass an int where a pointer-to-FILE was expected, or that you use fclose() without a prototype in scope. Did you enable the maximum warning level of your compiler?
Another segfault is possible if the data pointer does not point to a NUL (0) terminated string. Does your log.txt contain NUL-terminated strings?
You should also check if mmap() fails returning MAP_FAILED.
Okay so here is the code that got it working
#include <sys/stat.h>
int status;
struct stat s;
status = stat(file, &s);
if(status < 0){
perror("Stat:");
exit(1);
data = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Before i was using 'getpagesize();' thanks beej !!!
mmap's man page gives you information on the parameters:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
As you can see, your second argument may be wrong (except you really want to exactly map a part of the file fitting into a single page).
Also: Probably 0 is not a valid flag value? Let's have a look again at the man page:
The flags argument determines whether updates to the mapping are
visible to other processes mapping the same region, and whether
updates are carried through to the underlying file. This behavior is
determined by including exactly one of the following values in flags: MAP_SHARED or MAP_PRIVATE
So you could try something like
data = mmap(0, size, PROT_READ, MAP_SHARED, fp, 0);
Always use the provided flags, as the underlying value may differ from machine to machine.
Also, the mapped area should not be larger than the underlying file. Check the size of log.txt beforehand.
The second argument to mmap should not be page size, it should be the size of your file. Here is a nice example.
I'm having trouble accessing a global struct pointer that I'm initalizing with mmap. Attempting to access members of the struct in functions outside of the one it is declared in throw segfaults.
the struct:
typedef struct foo {
uint32_t size;
bar_t array[0];
} foo_t;
the initialization:
foo_t* foo; // global
static void* init_function(...) {
fd = open(filename, O_CREAT | O_WRONLY, 0644);
write(...);
lseek(...);
write(...);
foo = mmap(0, BIG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
foo->size = 0;
}
what causes the segfault:
static int another_function(...) {
if (foo->size == 0) {...} //foo->size causes it
}
BIG_SIZE is a defined value that should be adequately large for my needs.
Any ways, I apologize for the (...)s, but this is where the issues are showing up. I've looked into mmap docs and variable length methods with no luck. Thanks in advance!
You don't show us everything, but it seems that you just assign your global variable, but that you don't initialize the contents of the mapped region. You should at least give it a
foo->size = 0 somewhere, if the file is newly created, or alternatively you should use ftruncate instead of your fseek write sequence to warrant that the blocks (including size) are 0 filled.
Firstly check if mmap really succeeded:
foo = mmap(...);
if ( MAP_FAILED == foo )
{
//no memory for me...
}
And if I remember correctly, the file must be at least BIG_SIZE long, otherwise mmap will fail - but I may be wrong on that part...