I tried to realize shared memory with C, but I came up with a problem with my output.
I tried to make a thread called TA put the grade into a shared memory space and make "student" thread output the grade.
In the TA thread:
const int SIZE = 4096;
const char *name_ta = "Students_Information_ta";
int studentGrade = (int)((random() % (100 - 80 + 1)) + 80); // TA gives out a grade to a student
char *grade = (char *)&studentGrade;
/* shared memory file descriptor */
int shm_fd_ta;
/* pointer to shared memory object */
void *ptr_ta;
/* create the shared memory segment of ta */
shm_fd_ta = shm_open(name_ta, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory segment of ta*/
ftruncate(shm_fd_ta, SIZE);
/* map the shared memory segment of ta in the address space of the process */
ptr_ta = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_ta, 0);
if (ptr_ta == MAP_FAILED)
{
printf("Map failed\n");
return -1;
}
/* write to the shared memory region */
sprintf(ptr_ta, "%d", grade);
ptr_ta += strlen(grade);
And here is the output sentences in student thread:
/* name of shared memory object */
const char *name_ta = "Students_Information_ta";
/* size of shared memory object in bytes */
const int SIZE = 4096;
int shm_fd_ta;
void *ptr_ta;
/* open the shared memory segment of ta */
shm_fd_ta = shm_open(name_ta, O_RDWR, 0666);
if (shm_fd_ta == -1)
{
printf("shared memory failed\n");
exit(-1);
}
/* map the shared memory segment of ta in the address space of the process */
ptr_ta = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_ta, 0);
if (ptr_ta == MAP_FAILED)
{
printf("Map failed\n");
exit(-1);
}
printf("The grade assigned by the TA is %d\n", ptr_ta); // student receives a grade
I thought it should gave me the grades which are numbers between 80 and 100, but in fact the output is some very large numbers like 251142144. Perhaps it has output the address. What can I do to fix this mistake?
char *grade = (char *)&studentGrade;
Okay, so grade is of type pointer to character but is pointing to an integer. Not sure why you'd want to do that, but okay.
sprintf(ptr_ta, "%d", grade);
You told sprintf to print an integer but you passed it a pointer to a character that points to an integer. The sprintf function certainly has no idea what to do with such a mismatched pointer, especially when you told it you were going to pass it an integer.
Why not:
sprintf(ptr_ta, "%d", studentGrade);
If you tell it you're going to give it an integer, maybe give it an integer.
--
ptr_ta = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_ta, 0);
if (ptr_ta == MAP_FAILED)
{
printf("Map failed\n");
exit(-1);
}
Now, ptr_ta is a pointer to the string you printed.
printf("The grade assigned by the TA is %d\n", ptr_ta); // student receives a grade
You told printf you were going to pass it an integer, but instead you passed it a pointer to a string. How is that supposed to work?
I'd change a lot of things, but this is a start:
printf("The grade assigned by the TA is %s\n", (char *) ptr_ta); // student receives a grade
Related
I am a complete rookie to programming in C and have been trying to program a system that will take an integer input, perform a calculation, and tack them onto a string that will then be passed to a shared memory. Apologies if I am being an idiot but I am getting an error about an incompatible pointer type. I dont know how I can fix this error.
Edit: I apologize for the bad initial question. Full code is included
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
/* The size (in bytes) of shared-memory object */
const int SIZE = 4096;
/* The name of shared-memory object */
const char *Obj = "Shm";
/* The shared-memory file descriptor */
int shm_fd;
/* The pointer to shared-memory object */
void *ptr;
/* Create the shared-memory object */
shm_fd = shm_open(Obj, O_CREAT | O_RDWR, 0666);
/* Configure the size of the shared-memory object */
ftruncate(shm_fd, SIZE);
/* Map the shared-memory object in the address space of the process */
ptr = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED)
{
printf("Map failed\n");
return -1;
}
int cal;
char newStr[200];
char currentStr[200];
char *temp;
char value;
printf("Enter an integer");
scanf("%d", &cal);
/* Create a message and write it to the shared-memory object */
/*fgets(ptr, SIZE, stdin);*/
if (cal == 0) {
printf("0 is not valid");
return -1;
}
if (cal < 1) {
printf("Please enter a positive int");
return -1;
}
sprintf(newStr, "%d", cal);
while (cal != 1) {
if (cal % 2 == 0) {
cal = cal / 2;
}
else {
cal = 3 * cal + 1;
}
value = cal + '0';
sprintf(currentStr, " --- %d", value);
strcat(newStr, currentStr);
}
fgets(ptr, SIZE, newStr);
printf("Writing the message to the shared memory is done! \n");
return 0;
}
Due to nature of my coding environment testing and figuring out the exact nature of errors is particularly difficult.
Edit: Here is the exact error message
Collatz-Producer.c:84:2: warning: passing argument 3 of ‘fgets’ from incompatible pointer type [enabled by default]
And I had cut out the section above with ptr since I was confident it worked, though here is the specifics of what ptr equals
void *ptr;
ptr = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
According to this documentation, the function fgets takes 3 parameters, in this order:
a pointer to the memory buffer to write to
the size of the memory buffer
the FILE * stream to read from, for example stdin
The array newStr is not a FILE * stream. Therefore, it is not valid as a third parameter.
If you do not intend to read from a FILE * stream (such as stdin or a file opened with fopen), then you should not be using the function fgets.
I currently try to access UIO shared memory via memcopy.
My approach is:
open the corresponding device
map the memory via mmap with the offset speciality N*getpagesize()
memcopy / memset to the pointer that is returned from mmap
I tried also with ftruncate after Step 2 resulting in an error.
The memcopy / memset causes a bus error which is normaly a sign of writing out of the files boundaries.
Via cat /proc/'pid'/maps I'm able to see that there is a mapping for /dev/uioX
Also /sys/class/uio/uioX/maps/ has two map directories, of which I try to access the second one (map1 therefore N = 1)
Am I missing out something?
Would I have to mmap the full size of the memory specified in /sys/class/uio/uioX/maps/map1/size ?
I could not find any example for accessing the memory via memcopy, is there something that prevents memcopy on UIO mmaped memory?
Sources
unsigned char* GetMemPtr(const char *name, unsigned long Size)
{
long fd;
long truncret;
void* MemPtr;
unsigned long offst;
printf("Opening: %s with size %u\n" , name, Size);
fd = open(name, O_RDWR);
if (fd < 0) {
printf("Error: open : %u : %s\n", fd, strerror(errno));
}
offst = 1 * getpagesize();
/*truncret = ftruncate(fd, offst + Size);
if (truncret < 0)
{
printf("Error: ftruncate : %s : %d\n", strerror(errno), truncret);
}*/
MemPtr = mmap(0, Size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offst);
if (MemPtr == MAP_FAILED)
{
printf("Error: mmap : %p : %s\n", MemPtr, strerror(errno));
}
//MemPtr = MemPtr + offst;
printf("Mem pointer is %p\n", MemPtr);
memset(MemPtr, 0, Size);
printf("Pointer is : %p\n" , MemPtr);
return (unsigned char*) MemPtr;
}
Results in the output:
Opening: /dev/uio0 with size 4096
Mem pointer is 0xffff89232000
Bus error (core dumped)
Currently I have got my shared memory working between 2 processes
my parent looks like this
/* strings written to shared memory */
const char *message_0 = "Hello";
const char *message_1 = "World!";
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory obect */
void *ptr;
/* create the shared memory object */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory object */
ftruncate(shm_fd, SIZE);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
/* write to the shared memory object */
sprintf(ptr,"%s",message 0);
ptr += strlen(message_0);
sprintf(ptr,"%s",message 1);
ptr += strlen(message_1);
and my child process receives the code like so
const char *name = "OS";
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory obect */
void *ptr;
/* open the shared memory object */
shm_fd = shm_open(name,O_CREAT | O_RDWR, 0666);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
/* read from the shared memory object */
//char message = ptr;
//int newmsg;
//newmsg = atoi(message);
printf("%s",(char *)ptr);
printf("\n");
Now instead of passing hello world I would like to pass arrays instead, so I tried changing the end of my parent to try passing a single integer.
sprintf(ptr, "%d", 5);
ptr += 20; //just used 20 since it should be big enough for now
and in my child process I changed
printf("%d",(char *)ptr);
to
printf("%s", (int *)ptr);
However my message always messes up somewhere, and I print an invalid number instead. Can anyone tell me what i am missing?
You shouldn't try to represent your numeric values as strings when passing them via memory. Your receiver should just take a pointer to the address where you put your int, and interpret it as int:
child:
ptr = (int*) mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
....
int value = *ptr;
I created a process which calls mmap with MAP_SHARED flag set,when i attempt to copy a string to that address i receive Bus error core dumped,could some one please explain the reason behind it and how to fix it. Following is my code
int main()
{
int fd=0;
char* ret = NULL;
void *map_addr = NULL;
fd = open("./shared_file.txt", O_RDWR, S_IRUSR | S_IWUSR);
if(fd == -1) {
printf("errno = %d\n",errno);
printf("Aborting process1###########\n");
abort();
}
map_addr = mmap(NULL, 5*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map_addr == MAP_FAILED) {
printf("mmap failed error no =%d\n",errno);
close(fd);
return -1;
}
printf("map_addr = %p#################\n",(int*)map_addr);
printf("processid = %d#################\n",(int)getpid());
ret = strcpy((char*)map_addr,"Stack Overflow");
if(ret == (char*)map_addr)
printf("strcpy success\n");
/*if(msync(map_addr, sizeof(int), MS_SYNC))
printf("msync failed errno = %d\n",errno);*/
close(fd);
sleep(120);
return (0);
}
The cause of a bus error is usually an attempt to dereference a pointer that has not been initialized properly and contains junk data that are not accessible in a multiple of 4 or 1 or as related to datatype sizes.
First you should check if the shared_file.txt file size is >= 20 bytes(assuming sizeof int is 4 bytes) as specified in the mmap() length argument(where you put 5*(sizeof(int))) in the line below:
map_addr = mmap(NULL, 5*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
If file size is less than 20 bytes, you could use fallocate call to pre allocate the memory.
If shared_file.txt file size is <= 20 bytes and then you ask mmap to map 20 bytes, it could cause a bus error when you write beyond the actual no. of bytes available in file, because that would be access to an undefined portion of a memory. MMAP_FAILED will not be returned in this case, during memory initialization.
A quick check is to see if you can write a single character in the mmap char* pointer. If you can't( you will get a SIGBUS), that means file size is zero.
I have a input file which has a header like this:
P6\n
width\n
height\n
depth\n
and then a struct is writen, pixel*, into this file, which is going to be mapped.
So, I want to skip the header and make my mmap function return the ptr to that structure. How can I do this? with lseek perhaps? Could you please exemplify?
I will leave part of my code here:
printf("Saving header to output file\n");
if (writeImageHeader(h, fpout) == -1) {
printf("Could not write to output file\n");
return -1;
}
last_index = (int)ftell(fpout);
//printf("offset after header= %d\n",last_index);
//alloc mem space for one row (width * size of one pixel struct)
row = malloc(h->width * sizeof (pixel));
/*Create a copy of the original image to the output file, which will be inverted*/
printf("Starting work\n");
for (i = 0; i < h->height; i++) {
printf("Reading row... ");
if (getImageRow(h->width, row, fpin) == -1) {
printf("Error while reading row\n");
}
printf("Got row %d || ", (i + 1));
printf("Saving row... ");
if (writeRow(h->width, row, fpout) == -1) {
printf("Error while reading row\n");
}
printf("Done\n");
}
/*Open file descriptor of the ouput file.
* O_RDWR - Read and Write operations both permitted
* O_CREAT - Create file if it doesn't already exist
* O_TRUNC - Delete existing contents of file*/
if ((fdout = open(argv[2], O_RDWR, FILE_MODE)) < 0) {
fprintf(stderr, "Can't create %s for writing\n", argv[2]);
exit(1);
}
/*Get size of the output file*/
if (fstat(fdout, &sbuf) == -1) {
perror("Stat error ---------->\n");
exit(1);
}
//printf("Size of output file: %d\n",(int)sbuf.st_size);
/*Maps output file to memory*/
if ((data = mmap((caddr_t) 0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) (-1)) {
perror("Error mmaping");
exit(EXIT_FAILURE);
}
As you see, right now my ppm image is mapped to char* data, but I want to skip the header and map just to the pixel* part.
Here's my code with the suggestion of using 2 pointers, a char* from mmap and another one equals that + offset.
main
c functions
header
makefile
If you read the man page for mmap, you wil find that its final parameter is off_t offset. The description:
... continuing or at most 'len' bytes to be mapped from the object described by 'fd', starting at byte offset 'offset'.
I suspect if you pass your offset in as that parameter, it will do what you want.
You can't if the amount you need to skip is less than the system page size, since offset must be a multiple of the page size on some systems.
You just need to keep 2 pointers - the pointer to the start of the mmap'd block, and the pointer to the start of the data you want inside there. As in:
unsigned char *block = mmap(0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0);
unsigned char *data = block + offset;
where offset is the offset in the file to the data you want.
So, from what I understand, can I do something like this?
off_t offset_after_header = lseek(fdout, last_index, SEEK_SET);
printf("Pointer is on %d\n",(int)offset_after_header);
/*Maps output file to memory*/
if ((data = mmap((caddr_t) 0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, offset_after_header)) == (caddr_t) (-1)) {
perror("Error mmaping");
exit(EXIT_FAILURE);
}
and, from that, I could map my file to whatever type I want, in this case the pixel*
If this is ok, what cautions should I take? For example, like those Ignacio Vazquez-Abrams said
Um, you did notice the 'offset' parameter that you are supplying with a zero? Assuming you know the absolute offset of what you want, you pass it.