As far as I understand, arrays are allocated contiguous blocks of memory. I wrote a program to test my understanding. It basically attaches a shared memory segment. Here is the code:
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main ()
{
int segment_id;
int* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
const int
shared_segment_size = 4096; //one page size
int *results;
int cpid=0;
int sum=0;
/* Allocate a shared memory segment. */
segment_id = shmget (IPC_PRIVATE, shared_segment_size,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
/* Attach the shared memory segment. Results now contain the starting address */
results =shmat (segment_id, 0, 0);
printf("%p\n",&results);
printf("%p\n",&results[1]);
printf("%p\n",&results[2]);
printf("%p\n",&results[3]);
return 0;
}
Here is the output:
0x7fffe4d62e40
0x7f6603987004
0x7f6603987008
0x7f660398700c
Can someone explain to me why the address of results[1] is:
0x7f6603987004
given that the address of results[0] is:
0x7fffe4d62e40
Shouldn't it be 0x7fffe4d62e44?
Am I missing something crucial?
Related
I am trying to write an integer (1114129) from my HPS on Cyclone V Altera FPGA from a PUTTY window to a 32bit PIO on the FPGA side via lightweight axis interface. I am using mmap() and cannot get it to map to the address i want it to which is 0xff206000, instead its mapping to the address 0xb6f31000. My code and result is shown below.
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <math.h>
#define MAPPED_SIZE 4
#define DDR_RAM_PHYS 0xff206000
#define PAGESIZE 0x1000
//1114129
int main(void)
{
int _fdmem;
void *map;
int a;
int c = 1114129;
const char memDevice[] = "/dev/mem";
_fdmem = open( memDevice, O_RDWR | O_SYNC );
if (_fdmem < 0){
printf("Failed to open the /dev/mem !\n");
return 0;
}
else{
printf("open /dev/mem successfully !\n");
}
a = sysconf(_SC_PAGESIZE);
printf("page %d", a);
/* mmap() the opened /dev/mem */
map = mmap(0, MAPPED_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, _fdmem, DDR_RAM_PHYS & -PAGESIZE);
if (map == MAP_FAILED) { perror("npheap_alloc()"); exit(1); }
int volatile * const p = (int *) (map + (0xff206000 % PAGESIZE));
*p = c;
printf("value %p", map);
printf("\nThe memory address of variable var = ptr = %p\n", p);
printf("\nIndirect access, variable var value = *ptr = %d", *p);
/* unmap the area & error checking */
if (munmap(map,MAPPED_SIZE)==-1){
perror("Error un-mmapping the file");
}
/* close the character device */
close(_fdmem);
}
open /dev/mem successfully !
page 4096value 0xb6f31000
The memory address of variable var = ptr = 0xb6f31000
Indirect access, variable var value = *ptr = 1114129#
Thanks in Advance
I think you're confusing the physical address (0xff206000) with the virtual address (0xb6f31000). You don't care what the virtual address is, you just what the pages at the physical address 0xff206000 to be mapped to any virtual address you can use.
The offset argument to mmap is a byte offset into the file. The pointer that mmap returns is a pointer to that location in the file, which in your case is a pointer to the exact address you want to write the value to.
So when you write the value, you write it to the exact location that map is pointing to:
int volatile * const map = (int volatile * const) mmap(...);
*map = value;
To create a new and empty shared memory segment I can do something like:
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
int* new_array = (int*)shmat(shmid, NULL, 0);
Now suppose int* array owns a chunk of pre-existing data. Something like
int* array = malloc(2 * sizeof(int));
array[0] = 42;
array[1] = 24;
I'd like to turn this memory segment into a shared memory segment with the System V shared memory C API, on Linux.
How can I do this?
I've tried stuff like
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
void* attach = shmat(shmid, array, SHM_RND);
and like
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
array = (int*)shmat(shmid, 0, 0);
but either shmat returns (void*)-1 or int* array gets all its values set to zero.
Here's a full program that shows the failures:
/*
gcc shm0.c -o shm0 && ./shm0
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define NELEMENTS 3
void array_show(int* array, size_t nelements){
puts("");
for(int i=0; i < nelements; ++i)
printf("%d\n", *(array + i));
}
int main(){
const size_t nbytes = NELEMENTS * sizeof(int);
int* array;
posix_memalign((void**)&array, 16*16*16, nbytes);
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
// 1st try...
void* attach = shmat(shmid, array, SHM_RND);
if(attach == (void*)-1)
puts("shmat() failed!");
// 2nd try...
memset(array, 1, nbytes); // Fill array with dummy data!
array_show(array, NELEMENTS);
array = (int*)shmat(shmid, 0, 0);
array_show(array, NELEMENTS);
if(array[1] == 0)
puts("array was reset!");
// 3rd try
array = (int*)shmat(shmid, array, 0); // Now accessing, say, array[0] yields a segfault!
shmdt(array);
}
Why SYSV shared memory and not mmap()? Because I'm using the X11 MIT shared memory extension, and, according to the docs:
... to be able to use this extension, your system must provide the SYSV shared memory primitives. There is not an mmap-based version of this extension. To use shared memory on Sun systems, you must have built your kernel with SYSV shared memory enabled ...
(If someone knows a way around this, or how to do this using shm_open(), I'd be delighted to hear about it.)
I got a question about shared memory and segmentation fault.
I thought that it would be fine to use huge size of memory.
When I checked Shmmax, i found the huge memory can be allocated.
under data is the result of $ipcs -lm
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 18014398509465599
max total shared memory (kbytes) = 18014398442373116
min seg size (bytes) = 1
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#define ARRAY_SIZE 40000000
int main(int argc, char *argv[]){
int shmid;
void *shared_memory = (void *)0;
shmid = shmget((key_t)1234, sizeof(float), IPC_CREAT|0666);
if (shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
shared_memory = (float *)shmat(shmid, NULL, 0);
if (shared_memory == (void *)-1)
{
perror("shmat failed : ");
exit(0);
}
static float *testx;
testx = (float *)shared_memory;
int k = 0;
for(k;k<400;k++){
testx[k] = 1.12;
}
for(k;k<40000000;k++){
testx[k] = 1.12;
}
}
the program can run the first for loop which has small amount of size
the problem, however, is the second loop with 40,000,000 size
any suggestion what should i edit to run this code?
The reason for your SEGFAULT is that you haven't created enough size segment with shmget.
The argument you passed to shmget as size is sizeof(float) which is just enough to store 1 float.
What you need to do is call shmget like this -
shmget((key_t)1234, sizeof(float)*40000000, IPC_CREAT|0666);
Then you can use all the memory correctly.
The reason that the smaller loop of 400 worked is because shmget creates segments that are multiple of PAGE_SIZE.
So even when you passed sizeof(float), it allocated atleast 1 page which was enough to hold 400 floats but not 40000000.
I hope that clears the confusion.
I have a problem to write struct into a mapped memory file.
I have two file namely mmap.write.c and mmap.read.c, and in these files, I'm writing an integer to a file and reading it from file.
When I want to write struct and read it, I could not think about that since in line 32 of mmap.write.c
sprintf((char*) file_memory, "%d\n", i);
and in line 25 of mmap.read.c
sscanf (file_memory, "%d", &integer);
There is no difference to write and read integer/double/float/char etc. since I can put pattern as second argument "%d" for integer. But what I will write here to indicate struct? That is my main problem.
The struct that I want to write and read:
#define CHANNELS 20
typedef dataholder struct {
int value[CHANNELS];
time_t time;
int hash;
}dataholder;
mmap.read.c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mmap.h"
#define FILE_LENGTH 0x10000
int main (int argc, char* const argv[])
{
int fd;
void* file_memory;
int integer;
/* Open the file. */
fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
printf("file opened\n");
/* Create the memory mapping. */
file_memory = mmap (0, FILE_LENGTH, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
printf("memfile opened\n");
close (fd);
printf("file closed\n");
/* Read the integer, print it out, and double it. */
while(1) {
sscanf (file_memory, "%d", &integer);
printf ("value: %d\n", integer);
usleep(100000);
}
//sprintf ((char*) file_memory, "%d\n", 2 * integer);
/* Release the memory (unnecessary because the program exits). */
munmap (file_memory, FILE_LENGTH);
return 0;
}
mmap.write.c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "mmap.h"
#define FILE_LENGTH 0x10000
/* Return a uniformly random number in the range [low,high]. */
int random_range (unsigned const low, unsigned const high)
{
unsigned const range = high - low + 1;
return low + (int) (((double) range) * rand () / (RAND_MAX + 1.0));
}
int main (int argc, char* const argv[])
{
int fd, i;
void* file_memory;
/* Seed the random number generator. */
srand (time (NULL));
/* Prepare a file large enough to hold an unsigned integer. */
fd = open (argv[1], O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
//lseek (fd, FILE_LENGTH+1, SEEK_SET);
write (fd, "", 1);
//lseek (fd, 0, SEEK_SET);
/* Create the memory mapping. */
file_memory = mmap (0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
/* Write a random integer to memory-mapped area. */
for(i=0; i<10000; i++) {
sprintf((char*) file_memory, "%d\n", i);
//goto a;
usleep(100000);
}
a:
/* Release the memory (unnecessary because the program exits). */
munmap (file_memory, FILE_LENGTH);
return 0;
}
Thanks a lot in advance.
First of all you have to keep track of where in the memory you want to write, second you have to remember that the mapped memory is just like any other pointer to memory. The last bit is important, as this means you can use normal array indexing to access the memory, or use functions such as memcpy to copy into the memory.
To write a structure, you have three choices:
Write the structure as-is, like in a binary file. This will mean you have to memcpy the structure to a specified position.
Write the structure, field-by-field, as text using e.g. sprintf to the correct position.
Treat the memory as one large string, and do e.g. sprintf of each field into a temporary buffer, then strcat to add it to the memory.
The simplest way is to just use a pointer:
dataholder *dh = file_memory;
/* now you can access dh->value, dh->time, dh->hash */
Since this struct doesn't contain any pointers, if you need to copy it in or out, you can just assign it, like:
dataholder dh_other = *dh;
or
*dh = dh_other;
Why can't get a sigsegv or something when I read and write from the same shared memory segment? Is it normal? Why don't I get any error?
(I know that I don't call shmclt or shmdt) (:
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int parent(char*);
int child(char*);
int main ()
{
int shmid = shmget (0xACA0E5,30*sizeof(char),
IPC_CREAT | S_IRUSR | S_IWUSR);
char *shared = (char*) shmat(shmid,0,0);
if (fork())
return parent(shared);
else
return child(shared);
}
int parent (char* shared)
{
while(1)
{
printf("F: %s",shared);
sprintf(shared,"FATHER \t%p\n",shared);
}
}
int child(char* shared)
{
while(1)
{
printf("C: %s",shared);
sprintf(shared,"CHILD \t%p\n",shared);
}
}
Yes, that behaviour is perfectly normal. The operating system does not prevent you from overwriting your own data in the shared memory segment. If you want to avoid this, you will need to introduce some kind of IPC that synchronises access to the shared memory area.