Simply storing an integer in a shared memory segment: C - c

Im just trying to share an integer between two processes, but the memory segment is initialized in program 1 and is used in program 4. Here is the initialization in program 1:
shmid = shmget(key, sizeof(int*), 0666 | IPC_CREAT);
int *data = (int *)shmat(shmid, (void*)0,0);
Here I get a warning of "cast to pointer from integer of different size". Argh.
Simple, I'm assuming, but I'm a big time noob with IPC. And many other things in c....
Then I pass it to another program:
snprintf(shmarg, sizeof(shmarg), "%n", data);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
Not sure how to access it on the other side though. How can I get the int back in Program 4?

Pretty sure you're missing the header that defines shmat() and thus the compiler is guessing that shmat returns an int (not a pointer).

Here I get a warning of "cast to pointer from integer of different size". Argh.
Try using NULL instead of (void*)0.
Then I pass it to another program:
snprintf(shmarg, sizeof(shmarg), "%n", data);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg,
semarg, pipe_from_p2_2, pipe_to_p5_2);
Not sure how to access it on the other side though. How can I get the int back in Program 4?
The pointer itself isn't useful unless the shared memory area happens to be loaded at the exact same virtual memory address in the other program: you probably don't even want to try to arrange that - instead, let the other program (hw11-4) load the shared memory segment and let the OS choose the virtual memory address, then simply look at that address for the int. So, hw11-4 needs to be passed the same shared memory key (e.g. as a command line argument) and itself use shmget to open and get the key for, then shmat to map, the shared memory segment into memory....

Related

Are memory addreses portable in C?

Say we have program 1...
./program1
int main(int argc, char *argv[])
{
int *i;
*i = 10;
printf("%lld", i);
return 0;
}
Now program 2...
./program2 program1output 10
int main(int argc, char *argv[])
{
int *t;
t = (int*)atoll(argv[1]);
*t = atoi(argv[2]);
return 0;
}
Will this work? Can you share memory addresses between different programs?
This behavior is not defined by the C standard. On any general-purpose multi-user operating system, each process is given its own virtual address space. All of the memory assigned to a process is separate from the memory assigned to other processes except for certain shared memory:
Read-only data may be shared between processes, especially the instructions and constant data of two processes running the same executable and the instructions and constant data of shared libraries. That data may have the same address in different processes or different addresses (depending on various factors, including whether the code is position-independent and whether address space layout randomization is in use).
Some operating systems also map system-wide shared data into processes by default.
Memory may be shared between processes by explicit request of those processes to map shared memory segments. Those segments may or may not appear at the same virtual address in the different processes. (A request to map shared memory may request a certain address, in which case different processes could arrange to use the same address, or it could let the mapping software choose the address, in which case different processes cannot rely on receiving the same address assignment.)
In a special-purpose operating system, different processes could share one address space.
Supplement
This is not correct code:
int *i;
*i = 10;
The declaration int *i; defines i to be a pointer but does not assign it a value. Then using *i is improper because it attempts to refer to where i points, but i has not been assigned to point to anything.
To define an int and make its address visible in output, you could define int i; and then print &i.
This is not the proper way to print an address:
printf("%lld", i);
To print an address, cast it to void * and format it with %p. The result of the formatting is implementation-defined:
printf("%p", (void *) &i);
This is not a good way to reconstruct an address:
int *t;
t = (int*)atoll(argv[1]);
As with printf, the type should be void *, and there are problems attempting the conversion with atoll. The C standard does not guarantee it will work; the format produced by printing with %p might not be a normal integer format. Instead, use the %p specifier with sscanf:
void *temp;
if (1 != sscanf(argv[1], "%p", &temp))
exit(EXIT_FAILURE);
int *t = temp;
When the address comes from other process, the behavior of the sscanf conversion is not defined by the C standard.
In principal, an application operates on its own/private memory. There are ways of sharing memory among different processes, but this requires special mechanism to overcome above mentioned "principal" (memory mapped files, for example). Have a short look at, for example, this article on sharing memory.
In your case, program one will have ended and its memory is not available any more; and the way you access it is definitely not one of the "special mechanisms" necessary to access shared memory:
Though an integer vale may be converted to a pointer value, accessing this pointer is only valid if the integer value has originally been converted from a pointer to a valid object. This is not the case in your example, since the integral value calculated in t = (int*)atoll(argv[1]); never pointed to a valid object in the current program.
In general, memory addresses are tied to processes because each process may have its own memory space. So, the addresses are virtual addresses rather than physical addresses, which means they are references to a location in the process's memory space rather than references to a location on a chip.
(Not all environments have virtual memory. For example, an embedded system might not.)
If you have two programs running in the same process, a pointer can be passed between them. For example, a main program can pass a pointer to a dynamically linked library it loads.

Why does setting a value at an arbitrary memory location not work?

I have this code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main (int argc, char** argv) {
*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;
printf("%i", var);
printf("%i", &var);
return (EXIT_SUCCESS);
}
I want to see a 1 and the address of that int, which i specified previously. But when compiled by gcc in bash, only "command terminated" without any error will be shown. Does anyone know why so?
PS: I am newbie to C, so just experimenting.
What you are doing:
*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;
is totally wrong.
You have no guarantee whatsoever that an arbitrary address like 0x12345678 will be accessible, not to mention writable by your program. In other words, you cannot set a value to an arbitrary address and expect it to work. It's undefined behavior to say the least, and will most likely crash your program due to the operating system stopping you from touching memory you don't own.
The "command terminated" that you get when trying to run your program happens exactly because the operating system is preventing your program from accessing a memory location it is not allowed to access. Your program gets killed before it can do anything.
If you are on Linux, you can use the mmap function to request a memory page at an (almost) arbitrary address before accessing it (see man mmap). Here's an example program which achieves what you want:
#include <sys/mman.h>
#include <stdio.h>
#define WANTED_ADDRESS (void *)0x12345000
#define WANTED_OFFSET 0x678 // 0x12345000 + 0x678 = 0x12345678
int main(void) {
// Request a memory page starting at 0x12345000 of 0x1000 (4096) bytes.
unsigned char *mem = mmap(WANTED_ADDRESS, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Check if the OS correctly granted your program the requested page.
if (mem != WANTED_ADDRESS) {
perror("mmap failed");
return 1;
}
// Get a pointer inside that page.
int *ptr = (int *)(mem + WANTED_OFFSET); // 0x12345678
// Write to it.
*ptr = 123;
// Inspect the results.
printf("Value : %d\n", *ptr);
printf("Address: %p\n", ptr);
return 0;
}
The operating system and loader do not automatically make every possible address available to your program. The virtual address space of your process is constructed on demand by various operations of the program loader and of services inside the process. Although every address “exists” in the sense of being a potential address of memory, what happens when a process attempts to access an address is controlled by special data structures in the system. Those data structures control whether a process can read, write, or execute various portions of memory, whether the virtual addresses are currently mapped to physical memory, and whether the virtual addresses are not currently mapped to memory but will be provide with physical memory when needed. Initially, much of a process’ address space is marked not in use (or at least implicitly marked, in that none of the explicit records for the address space apply to it).
In the executions of your program you have attempted so far, the address 0x12345678 has not been mapped and marked available to your process, so, when your process attempted to use it, the system detected a fault and terminated your process.
(Some systems randomize the layout of the address space when a program is being loaded, to make it harder for an attacker to exploit bugs in a program. Because of this, it is possible that 0x12345678 will be accessible in some executions of your program and not others.)
The quote from C11 standard 6.5.3.2p4:
4 The unary * operator denotes indirection. [...] If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
You use * operator on (volatile uint8_t*)0x12345678u pointer. Is this a valid pointer? Is it invalid pointer? What is an "invalid value" of a pointer?
There is no check that allows to find out which particilar pointer values are valid, which aren't. It is not implemented in C language. A random pointer may just happen to be a valid pointer. But most, most probably it is an invalid pointer. In which case - the behavior is undefined.
Dereferencing an invalid pointer is undefined behavior. But - outside of C scope and into operating system - on *unix systems trying to access memory that you are not allowed to, should raise a signal SIGSEGV on your program and terminate your program. Most probably this is what happens. Your program is not allowed to access memory location that is behind 0x12345678 value, the operating system specifically protects against that.
Also note, that systems use ASLR, so that pointer values within your program are indeed in some degree random. There are not linear, ie. *(char*)0x01 will not access the first byte in your ram. Operating system (or more exact, the underlying hardware as configured by the operating system) translates pointer values in your program to physical location in ram using what is called virtual memory. The same pointer values may just happen to be valid on the second run of your program. But most probably, because pointers can have so many values, most probably it isn't a valid pointer. Your operating system kills your program, as it detects an invalid memory access.

How to put a variable in a shared memory? [duplicate]

This question already has answers here:
how to use shared memory to communicate between two processes
(3 answers)
Closed 5 years ago.
I have a variable with a value and I want to share it with the proccesses.
Ex:
typedef struct {
unsigned int a;
unsigned int b;
another_struct * c;
} struct1;
...
struct1 A ={...};
...
Now, I want to create a shared memory region and put the A variable in this region. How can i do this?
Shared memory is an operating system feature (which does not exist in C11). It is not "provided" by the C standard.
I guess that you are coding for Linux. BTW, read Advanced Linux Programming.
Read first shm_overview(7). You'll need to synchronize, so read also sem_overview(7).
You'll get some shared memory segment into a pointer and you'll use that pointer.
First, open the shared memory segment with shm_open(3):
int shfd = shm_open("/somesharedmemname", O_RDWR|O_CREAT, 0750);
if (shfd<0) { perror("shm_open"); exit(EXIT_FAILURE); };
Then use mmap(2) on that shfd:
void* ad = mmap(NULL, sizeof(struct1), PROT_READ|PROT_WRITE, MAP_SHARED,
shfd, (off_t)0);
if (ad==MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); };
Then you can cast that address into a pointer:
struct1* ptr = (struct1*)ad;
and use it. (Don't forget to close).
BTW, you don't and you cannot put a variable into a shared memory. You get a pointer to that shared memory and you use it, e.g. ptr->a = 23;
Of course, don't expect the same shared segment to be mapped at the same address (so you can't easily deal with pointer fields like c) in different processes. You probably should avoid pointer fields in shared struct-s.
Notice that C variables exist only at compile time. At runtime, you only have locations and pointers.
PS. Shared memory is a quite difficult inter-process communication mechanism. You should perhaps prefer pipe(7)-s or fifo(7)-s and you'll need to multiplex using poll(2).
Take a look at Beej's Guide to IPC.
I would basically treat the whole shard memory segment as a void* that you can place items at. You could use memcpy, of string.h, to copy A into your shared memory space. However your pointer in A would become invalid and cause a segfault if you attempted to use it in another process connected to shared memory segment.

Segmentation fault when accessing shared memory in C

I have 2 programs which need to share memory. Let's call one program A, the other one B.
There is a struct in this format for this purpose:
struct town_t {
int population;
char * name;
}
In program A, data gets written to shared memory using mmap. This works for program A. (It uses strcpy for the name)
Program B's purpose is to simply read the data. This too works with mmap. Accessing the shared memory's population field works without any problems. However, accessing the population field gives a segmentation fault.
Since I used strcpy, the entire string should be in the shared memory right?
I use the following flags to get the pointer to shared memory, which returns no error.
tptr = (struct town_t *) mmap(0, 1024, PROT_READ, MAP_SHARED, shm_fd, 0)
How could I make it so I can actually read the string (char*) from program B?
There's no point in putting a pointer in shared memory. A pointer gives a location inside the address space of a particular process. It would have no meaning to another process with another address space. (With some complicated exceptions such as a pointer to memory allocated before a call to fork accessed by related processes running the same executable.)
You can store the string data itself in shared memory if you wish. For example, this will work:
#define MAX_NAME_SIZE 100
struct town_t
{
int population;
char name[MAX_NAME_SIZE];
};

C pass void pointer using shared memory

I need to pass void handler to another application, To replicate the scenario I have created one small program using shared memory and try to pass the void pointer to another application and print the value, I can get the void pointer address in another application, but when I try to dereference the pointer second application crash.
Here are the sample application wire.c .
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int main() {
key_t key=1235;
int shm_id;
void *shm;
void *vPtr;
shm_id = shmget(key,10,IPC_CREAT | 0666);
shm = shmat(shm_id,NULL,NULL);
sprintf(shm,"%d",&vPtr);
printf("Address is %p, Value is %d \n", (void *)&vPtr, * (int *)vPtr);
return;
}
Here is read.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
key_t key=1235;
int shm_id;
void *shm;
void *p = (void *)malloc(sizeof(void));
shm_id = shmget(key,10,NULL);
shm = shmat(shm_id,NULL,NULL);
if(shm == NULL)
{
printf("error");
}
sscanf(shm,"%d",&p);
printf("Address is %p %d\n",(void *)p);
return 0;
}
When I try to dereference p it crash. I need to pass the void pointer address and value in second application.
I don't want to share value between application, It works using shared memory,I know.
By default void *ptr will have some garbadge value (for ex. add= 0xbfff7f, value=23456), Can you please tell me, how can i pass void pointer address to another application and from the second application using that address i can print the value which was found in first application (i.e. 23456).
Apart from the shared memory is there any other alternate available?
Thanks.
This is probably because the pointer is still virtual; it's pointing at memory that is shared but there is no guarantee that two processes sharing the same memory maps it to the same virtual address. The physical addresses are of course the same (it's the same actual memory, after all) but processes never deal with physical addresses.
You can try to request a specific address by using mmap() (in Linux) to do the sharing. You can also verify the virtual address theory by simply printing the addresses of the shared memory block in both processes.
Also, don't cast the return value of malloc() in C and don't allocate memory when you're going to be using the shared memory. That part just makes no sense.
If you want to call a function within a process from another process, it is NOT IPC.
IPC is sharing of data between multiple threads/processes.
Consider adding the shared function into a DLL/shared-object to share the code across processes. If not, then you could add RPC support to your executables as shown here.
Why passing function pointers between 2 process does NOT work?
A function pointer is a virtual address referring to the physical memory location where the function code is currently loaded into physical memory. Whenever a function pointer(virtual address) is referred to in the process, the kernel is responsible for performing the mapping to physical address. This is successful as the mapping is present in the page-tables for the current process.
However when a context-switch occurs and another process is running, the page-tables containing the mappings of that particular process are loaded and currently active. these will NOT contain the mapping of the function pointer form the previous process. Hence attempting to use the function pointer from another process will fail.
Why the page-tables do NOT contain the mapping of function in another process?
If this was done then there would be no advantages with having multiple processes. All the code that could ever be run would have to be loaded into physical memory simultaneously. Also the entire system would then effectively be a single process.
Practically speaking whenever a context-switch happens and a different process is executing, the code/data segments of the earlier process can even be swapped out of physical memory. Hence even maintaining a function pointer and passing it to the new process is useless as it cannot guarantee that the function code will be held in memory even after newer process is loaded in memory and starts executing.
This is illegal:
void *p = (void *) malloc(sizeof(void));
void is an incomplete type, sizeof(void) is invalid. You can't do this for the same reason that you can't declare a void variable:
void i; /* WRONG */
What happens when you dereference p is undefined. If you just want the pointer value, you don't need to call malloc on read.c - that's the whole concept of shared memory - if it's shared, why would you allocate space on the reader program?

Resources