Unix/C: put a file into shared memory - c

Have a problem.
I have a file which contents look like number:error_description.
Now i need to put this file to shared memory (POSIX). If any contents are modified it should be saved to the base-file.
There is a need to search in the content in the shared memory (results will be sent to a client over a message queue).
How do I implement all this? First I thought I have to open (fopen("my_file", "r")) and then I have to create shared memory and mmap the file.
Can someone help me?
edit:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
/*
* \ /tmp/errors -> Error File
*/
#define MSGQ_HANDLER "/error_handler"
#define PATH_TO_FILE "/tmp/errors"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main(void) {
int fd = open(PATH_TO_FILE, O_RDWR);
struct stat file_stat;
fstat(fd, &file_stat);
printf("File size: %zd\n", file_stat.st_size);
char *byte_ptr = mmap(NULL, file_stat.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if(byte_ptr == MAP_FAILED){
perror("error:");
}
while(1){
printf("%s\n", byte_ptr);
if(byte_ptr)
exit(1);
}
return EXIT_SUCCESS;
}
So far it is what I have now.
Read a line works.
How do I change the content?

Don't use fopen and forget about shared memory (the sh* API I mean). mmap is all that's needed.
Open your file with open and the right options (read/write). Then use mmap with the option MAP_SHARED. All changes in the file will be reflected directly and visible to all processes that map the same file. On Linux and Solaris (on other systems I don't know, but it is not guaranteed by POSIX or any standard) you can even access the file concurrently with read/write. It is a bad idea though.
Concurrent memory accesses from different processes will, of course, need synchronisation (mutex, semaphores etc.).

Related

allocation error when trying to do shmat

when trying to do shmat i get allocation error, telling me i cannot accsess the memory, it did not happen to me before and i really dont know what to do.
this is the error :
0xffffffffffffffff error: Cannot access memory at address 0xffffffffffffffff
and the wiered thing is that vecBoard is allocated in the process mapped area and only get crazy when shmat is triggered. thank you all!
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define SHM_SIZE 4096
#define FLAGS IPC_CREAT
#define COLUMNS 8
#define ROWS 8
key_t key;
int main()
{
char* vecBoard;
// Creating shared memory :
if ((key = ftok("ex31.c",'k')) == -1)
{
perror("ftok");
exit(1);
}
int shm_id;
shm_id=shmget(key,SHM_SIZE,FLAGS);
if(shm_id==-1)
{
printf("error creating shared memory\n");
exit(0);
}
printf("the shared memory segment ID is: %d\n",shm_id);
vecBoard = (char *)shmat(shm_id,0,0);
if((vecBoard = (char *)shmat(shm_id,0,0)) == (char*)-1)
{
printf("error in attaching to the shared memory\n");
exit(0);
}
}
The shown code calls shmat() twice, for the same memory segment.
That's obviously wrong.
Looking at errno would help diagnose the problem (i.e. call perror on failure instead of just printf). The OS gives you information when a system call fails; don't ignore it!
If you carefully read the man page for shmget, the third argument (flags) is supposed to contain, in its low 9 bits, the permissions desired for the shared memory segment. If you just pass IPC_CREAT here, those bits are cleared to 0, so you create a segment for which nobody has either read or write permissions. Thus shmat fails (with EACCES because you are (implicitly) asking to both read and write that segment, and you don't have permission to do either.
You probably want to change it to something like IPC_CREAT | 0600, if you want the current user to be able to access the segment.
Note that before testing this again, you'll probably have to remove the existing shared memory segment with the erroneous permissions. Use the ipcs and ipcrm tools for this.

In C mmap the file for write: Permission denied. Linux

This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main()
{
int fd=open("/home/victor/hello",O_WRONLY);
if(fd<0)
{
perror("Open");
exit(EXIT_FAILURE);
}
struct stat sbuf;
if(fstat(fd, &sbuf)==-1){
perror("stat");
close(fd);
exit(EXIT_FAILURE);
}
void* file_memory= mmap(NULL, sbuf.st_size, PROT_WRITE, MAP_SHARED,fd,0);
if (file_memory == MAP_FAILED ) {
perror("Error mmapping the file");
close(fd);
exit(EXIT_FAILURE);
}
return 0;
}
I tried this too
int fd=open("/home/victor/hello",O_WRONLY|0777);
but it's the same error:
Error mmapping the file: Permission denied
Doing ls -l | grep hola
-rwxrwxrwx 1 victor victor 24 oct 24 01:47 hello
What's wrong?
From the glibc manual, and as already pointed out by R.. and Iwillnotexist Idonotexist above:
Note that most hardware designs cannot support write permission
without read permission, and many do not distinguish read and execute
permission. Thus, you may receive wider permissions than you ask for,
and mappings of write-only files may be denied even if you do not use
PROT_READ.
http://www.gnu.org/software/libc/manual/html_node/Memory_002dmapped-I_002fO.html

How do I directly access reserved memory with a kernel module?

I'm trying to limit the OS (Ubuntu Server 15.04) to a certain memory usage and reserve the rest but write a kernel module to read/write to the reserved memory. I figured out how to limit the usage/reserve memory using the kernel parameters "mem=4G memmap=4G#0 memmap=4G$4G" (4GB for OS and 4GB reserved, split at 4GB point) but I don't know how DMA to reserved memory works with kernel modules. I was thinking just create a proc file but I'm not sure if you can create one outside of the OS's allocated memory.
Any suggestions? Thanks!
Edit: This is for research so it doesn't need to be "nice"
Update:
Maybe I don't need to write a kernel module. I just found this and I'm going to give it a shot:
http://elinux.org/Memory_Management#Reserving_.28and_accessing.29_the_top_of_memory_on_startup
Update:
I tried the link above but I segfault whenever I try to write. Here's my code:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mann.h>
#define RESERVED_MEMORY_SIZE 0x100000000
int main() {
int fd;
char *reserved_memory;
fd = open("/dev/mem", O_RDWR | O_SYNC);
reserved_memory = (char *) mmap(0, RESERVED_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 4096);
reserved_memory[0] = 'a';
return 0;
}
dmesg shows:
a.out[1167]: segfault at ffffffffffffffff ip 00000000004005d7 sp 00007ffeffccbd80 error 7 in a.out[400000+1000]
For kicks I tried reserved_memory[1]:
a.out[1180]: segfault at 0 ip 00000000004005db sp 00007ffc388d77b0 error 6 in a.out[400000+1000]
I'll look into the format of those messages so I can figure out what it's telling me.
Update:
I found this question by somebody with the same issue as me however the only solution appears to be a kernel rebuild. I'm going to try to avoid this so maybe my best option is a custom kernel module again.
accessing mmaped /dev/mem?
Ok, so I think I solved it. Turns out I just didn't understand how mmap works and I guess the kernel has no restriction on writing/reading /dev/mem if it's in reserved memory. Below are two programs that will write to my reserved spot in memory and read from it.
Write "Hello World!":
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#define RESERVED_MEMORY_OFFSET 0x100000000 /* Offset is 4GB */
int main() {
int fd;
char *reserved_memory;
char *buffer = "Hello World!";
fd = open("/dev/mem", O_RDWR | O_SYNC):
/* Returns a pointer to the 4GB point in /dev/mem - the start of my reserved memory. Only mapping 4096 bytes. */
reserved_memory = (char *) mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, RESERVED_MEMORY_OFFSET);
if (reserved_memory == MAP_FAILED) {
printf("Failed to creating mapping.\n");
printf("ERRNO: %s\n", strerror(errno));
return -1;
}
sprintf(reserved_memory, "%s", buffer);
return 0;
}
Read from beginning of reserved memory:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#define RESERVED_MEMORY_OFFSET 0x100000000 /* Offset is 4GB */
int main() {
int fd;
char *reserved_memory;
char buffer[13];
fd = open("/dev/mem", O_RDWR | O_SYNC):
/* Returns a pointer to the 4GB point in /dev/mem - the start of my reserved memory. Only mapping 4096 bytes. */
reserved_memory = (char *) mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, RESERVED_MEMORY_OFFSET);
if (reserved_memory == MAP_FAILED) {
printf("Failed to creating mapping.\n");
printf("ERRNO: %s\n", strerror(errno));
return -1;
}
snprintf(buffer, 13, "%s", reserved_memory);
printf("%s\n", buffer);
return 0;
}
Special thanks to #knm241!

C Linux shared memory error - ftruncate

I'm trying to open a shared mem file and write into it. The problem is that ftruncate is returning -1 .
Here is my code:
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include <fcntl.h>
int main(void) {
int fd;
fd=shm_open("/shmem-m", O_CREAT,0777);
printf("%d\n",fd);
int a=ftruncate(fd, 1024);
printf("%d\n",a);
void* addr=mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0);
char* msg= "hola mundo!";
memcpy(addr,msg,strlen(msg));
exit(EXIT_SUCCESS);
//return 0;
}
The output is:
3
-1
Segmentation fault
Any ideas? Thank you very much
The problem is that POSIX requires the file to be opened in write mode for a call to ftruncate to succeed as stated in the ftruncate man page.
So the call to shm_open becomes shm_open("/shmem-m", O_CREAT | O_RDWR, 0777), with the O_RDWR flag set (shm_open man page).

C - why I cannot mmap a small (256UL or smaller) size of memory?

Please tell me, why my simple application cannot mmap a small size of memory?
And, why such a specific boundary - 257UL?
// #define MAP_SIZE 256UL or below - fail
// #define MAP_SIZE 257UL - ok
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char **argv) {
int fd;
void *map_base, *virt_addr;
unsigned long read_result, writeval;
off_t target = strtoul("0x00002000", 0, 0);
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
printf("/dev/mem opened.\n");
fflush(stdout);
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
printf("Memory mapped at address %p.\n", map_base);
fflush(stdout);
...
}
mmap works in multiples of the page size on your system. If you're doing this on i386/amd64 or actually most modern CPUs, this will be 4096.
In the man page of mmap on my system it says: "offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).". On some systems for historical reasons the length argument may be not a multiple of page size, but mmap will round up to a full page in that case anyway.
Probably you just don't have the rights to write to /dev/mem. This is probably not what you want, mapping all the low end physical memory into your address space.
Have a look into shm_open to open memory segments or MAP_ANONYMOUS to map anonymously.
Edit:
Do a man mem to know what the /dev/mem device node is about:
Byte addresses in mem are interpreted as physical memory
addresses.
References to nonexistent locations cause errors to be returned.
If you want to map to a device node to have a memory segment you should use /dev/zero, but nowadays the tools I describe above should be sufficient.
Then don't, really don't, run such a code with root privileges unless you really know what you are doing. Writing into the physical memory and thus overwriting kernel and userspace data and programs can only lead to catastrophes.

Resources