Initializing array of struct in a struct after mmap - c

I have a custom queue implementation as below
struct Customer {
pid_t pid;
int priority;
int order;
};
typedef struct Customer Customer_t;
struct CustomerNode {
int next;
int filled;
Customer_t customer;
};
typedef struct CustomerNode CustomerNode_t;
struct CustomerQueue {
int front;
int rear;
int filledNodes;
CustomerNode_t nodes[NODE_SIZE];
};
typedef struct CustomerQueue CustomerQueue_t;
and I need to map a CustomerQueue_t *queue as shared memory object, shared between processes. When I mmap first then initialize the struct, it gives segmentation fault.
void initQueue() {
sharedQueue->front = 0;
sharedQueue->rear = 0;
sharedQueue->filledNodes = 0;
sharedQueue->nodes = {0};
for (int i = 0; i < NODE_SIZE; ++i) {
sharedQueue->nodes[i] = (CustomerNode_t) { .next = -1, .filled = 0, (Customer_t) {-1, -1, -1 } };
}
}
Mmap part
#define SHMQUEUE "SHMQUEUE"
...
CustomerQueue_t* sharedQueue;
int sharedQueueFd;
// open queue shared memory
sharedQueueFd = shm_open(SHMQUEUE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
failChecker(sharedQueueFd, "Failed to open shared memory");
failChecker(ftruncate(sharedQueueFd, sizeof(CustomerQueue_t)), "Failed to ftruncate");
// map pizza new shared memory
sharedQueue = mmap(NULL, sizeof(CustomerQueue_t*), PROT_READ | PROT_WRITE, MAP_SHARED, sharedQueueFd, 0);
if(sharedQueue == MAP_FAILED) {
perror("Failed mmap");
_exit(-1);
}
Here is the valgrind result when I changed sizeof(CustomerQueue_t) instead of its pointer.
How can I solve it

Related

Different shared memory values between processes

I create a buffer via this function.
int create_buffer(const char *shmem_name, void **shmem_obj)
{
int returnval = 0;
int fd;
fd = shm_open(shmem_name, O_CREAT | O_RDWR, 0666);
ftruncate(fd, sizeof(ShareStruct));
*shmem_obj = (ShareStruct *)mmap(NULL, sizeof(ShareStruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmem_obj < 0)
{
returnval = -1;
}
return returnval;
}
Then when I use the pointer in process 1 and its functions, everything works.
In process 2 I fetch the buffer using this function:
int fetch_buffer(const char *shmem_name, void **shmem_obj)
{
int returnval = 0;
int fd;
fd = shm_open(shmem_name, O_RDWR, 0666);
ftruncate(fd, sizeof(ShareStruct));
*shmem_obj = (ShareStruct *)mmap(NULL, sizeof(ShareStruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmem_obj < 0)
{
returnval = -1;
}
printf("buffer fetched \n");
return returnval;
}
But the contents of the buffer are different (weird values).
What am I doing wrong?
EDIT:
process 1 code:
#define SHMEM_NAME "OS"
int main(void) {
int exitcode = 0;
void *shmem_obj;
/* Initialize the buffer and create the shared memory */
if( create_buffer(SHMEM_NAME, &shmem_obj) != 0) {
fprintf(stderr, "Something went wrong while initializing the buffer.\n");
exitcode = 1;
} else {
printf("Shared buffer created.\n");
initParameters(&shmem_obj);
subscribe(0, &shmem_obj);
printf("\n[PRESS ENTER TO CLOSE THE SHARED BUFFER]");
getchar();
/* unlink the shared memory */
if(destroy_buffer(SHMEM_NAME) != 0) {
fprintf(stderr, "Something went wrong while destroying the buffer.\n");
exitcode = 2;
} else {
printf("Shared buffer closed.\n");
}
}
return exitcode;
}
Process 2 code:
#define SHMEM_NAME "OS"
int main(void) {
void *shmem_obj;
/* fetch the buffer from shared memory */
fetch_buffer(SHMEM_NAME, &shmem_obj);
subscribe(1, &shmem_obj);
printf("Subscriber 0 subscribed\n");
}
Other code:
typedef struct ShareStruct
{
Subscriber subscriberList[20];
int subscriberActiveList[20];
unsigned int writepointer_tail;
unsigned int readpointer_head;
Task taskList[21];
sem_t freePostions;
pthread_mutex_t mutex;
unsigned int subscriberCount;
} ShareStruct;
int initParameters(void *shmem_obj)
{
ShareStruct *shmem = shmem_obj;
int returnval = 0;
shmem->writepointer_tail = 0;
shmem->readpointer_head = 0;
shmem->subscriberCount = 0;
pthread_mutex_init(&shmem->mutex, NULL);
sem_init(&shmem->freePostions, 1, 20);
return returnval;
}
int destroy_buffer(const char *shmem_name)
{
int returnval = 0;
returnval = shm_unlink(shmem_name);
return returnval;
}
int subscribe(subscriber_id subscriber, void *shmem_obj)
{
int returnval = 0;
ShareStruct *shmem = shmem_obj;
int value;
sem_getvalue(&shmem->freePostions, &value);
printf("sem value : %d\n", value);
pthread_mutex_lock(&shmem->mutex);
int freeposition = returnNextFreePosition(shmem_obj);
if (freeposition != -1)
{
printf("subscribe logic");
}
else
{
printf("Subscriber list is full \n");
}
pthread_mutex_unlock(&shmem->mutex);
return returnval;
}
The problem is that you're being inconsistent in the number of layers of pointer indirection there are, and your use of void * rather than more specific pointer types is preventing the compiler from realizing and telling you that you're doing that. Replace all of your void *s with ShareStruct *s, and then fix the resulting errors and warnings your compiler gives you, and your code will start to work.

Share memory between multiple child processes belonging to same parent

I am trying to create Client/Server application based on master/slave architecture. The parent Server is responsible for handling new socket requests from client and uses fork() in which the client interaction happens.
I want to create a Linked List in the application in which the child processes add nodes and it is accessible to every other child. I have tried creating share memory using mmap and shmget but other processes are not able to read the linked list after the first child creates the list.
node structure:
struct node{
char data[1024];
struct node *next;
};
mmap approach:
void* create_shared_memory(size_t size){
int protection = PROT_READ |PROT_WRITE;
int visibility = MAP_SHARED | MAP_ANONYMOUS;
return mmap(NULL, size, protection, visibility, -1, 0);
}
shmget approach:
void * my_malloc(int size)
{
void * ptr = NULL;
key_current = key_first++;
int shm_id;
if ((shm_id = shmget(key_current, size , IPC_CREAT | 0666)) < 0) {
perror("shmget error.");printf("errno= %d EINVAL=%d \n ", errno , EINVAL);
return NULL;
}
if ((ptr = shmat(shm_id, NULL, 0)) == (void *) - 1) {
perror("shmat error");
//exit(1);
return NULL;
}
current_index ++ ;
shm_id_arr[current_index] = shm_id ;
return ptr;
}
I solved this by creating an array with predefined number of node elements in the parent process using shared memory and initializing all of the indices before the server listens for client requests and uses fork().
#define MAX_NODES 1000
typedef struct node{
char data[1024];
} node;
node *nodes;
void init_array(){
int id;
if((id = shmget(12345, sizeof(node)*MAX_NODES , IPC_CREAT | 0666) < 0){
perror("shmget error");
}
nodes = (node*) shmat(id, NULL, 0);
}

How to share ck_list across processes?

I'm trying to use CK_LIST from http://concurrencykit.org/ across multiple processes but when I do, the values inside the list nodes are garbage. But the list values are correct when I use the list only from one process.
Here is an example of using CK_LIST in a struct with in a single process.
#include <stdio.h>
#include <stdlib.h>
#include <ck_queue.h>
struct shared_map
{
CK_LIST_HEAD(list, list_node) list;
};
struct list_node
{
void *data;
CK_LIST_ENTRY(list_node) list_entry;
};
int main(void)
{
struct list_node *node, *node2;
struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
struct shared_map *map = &mapping;
node = malloc(sizeof(struct list_node));
if(node == NULL)
{
perror("malloc");
return -1;
}
CK_LIST_INIT(&map->list);
int rtrn = asprintf((char **)&node->data, "test");
if(rtrn < 0)
{
perror("asprintf");
return -1;
}
CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
CK_LIST_FOREACH(node2, &map->list, list_entry)
{
printf("out: %s\n", node2->data);
}
return 0;
}
But when I try to use the list between two separate processes the value of node->data is garbage and causes the process using it to crash. Below is an example.
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <ck_queue.h>
struct shared_map
{
CK_LIST_HEAD(list, list_node) list;
};
struct list_node
{
void *data;
CK_LIST_ENTRY(list_node) list_entry;
};
static int create_shared(void **pointer, int size)
{
*pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if(*pointer == MAP_FAILED)
{
perror("mmap:");
return -1;
}
return 0;
}
int main(void)
{
struct list_node *node, *node2;
struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
struct shared_map *map = &mapping;
int rtrn = create_shared((void **)&map, sizeof(struct shared_map));
if(rtrn < 0)
{
printf("Can't create shared mapping\n");
return -1;
}
CK_LIST_INIT(&map->list);
pid_t pid;
pid = fork();
if(pid == 0)
{
/* Child. */
node = malloc(sizeof(struct list_node));
if(node == NULL)
{
perror("malloc");
return -1;
}
int rtrn = asprintf((char **)&node->data, "test");
if(rtrn < 0)
{
perror("asprintf");
return -1;
}
CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
}
else if(pid > 0)
{
/* Parent. */
sleep(1); // Make sure child runs first.
CK_LIST_FOREACH(node2, &map->list, list_entry)
{
printf("out: %s\n", node2->data);
}
}
else
{
perror("fork");
return -1;
}
return 0;
}
CK_LIST is listed as a multi-reader single-writer linked list so I thought I only had to lock on writes not reads. So why does node->data become garbage when using it between processes as opposed to when used by a single process?
Fortunately, this has nothing to do with CK and just to do with where your memory is coming from.
Your allocation in the child comes from malloc and lives in the child's address space. The child and parent do not share the same address space (being separate processes), so your malloc(3)-obtained storage for the node and data are not at addresses within the parent's address space.
You need to allocate the memory for the node and node->data from some shared space as well. I made a minimal set of changes to the code to get it to work for demonstration purposes. Hopefully obviously, you'd want your API to be less fragile in terms of magic numbers to mmap.
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ck_queue.h>
struct shared_map {
CK_LIST_HEAD(list, list_node) list;
};
struct list_node {
void *data;
CK_LIST_ENTRY(list_node) list_entry;
};
static int
create_shared(void **pointer, int size)
{
*pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (*pointer == MAP_FAILED) {
perror("mmap:");
return -1;
}
return 0;
}
int
main(void)
{
struct list_node *node, *node2;
struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
struct shared_map *map = &mapping;
int rtrn = create_shared((void **)&map, sizeof(struct shared_map) + sizeof(struct list_node) + 5);
if (rtrn < 0) {
printf("Can't create shared mapping\n");
return -1;
}
CK_LIST_INIT(&map->list);
pid_t pid;
pid = fork();
if (pid == 0) {
/* Child. */
node = (struct list_node *)(map + 1);
node->data = node + 1;
memcpy(node->data, "test", 5);
CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
} else if (pid > 0) {
/* Parent. */
sleep(1); // Make sure child runs first.
CK_LIST_FOREACH(node2, &map->list, list_entry) {
printf("out: %s\n", (char *)node2->data);
}
} else {
perror("fork");
return -1;
}
return 0;
}
Fundamentally, I just changed the size of the mmap to include space for the list_node and the C string attached to node->data and made the child set that up.

Child process can't access shared memory in C

I have a program where I want to set up a pointer to a struct as shared memory. I think I've set up the shared memory correctly in the main method; then I call a function to initialize the struct, and fork. However, the child process can't access the shared memory; the parent process works as expected, which isn't that big of a surprise. I know for sure that the child process executes and works, but it cannot access the shared memory, so the function doesn't really do much besides print out printf statements.
struct OverSharedData{
struct SharedData ** rep;
int rop;
};
void initialize( struct OverSharedData * bill){
bill->rep = (struct SharedData**)malloc(sizeof(struct SharedData*)*consumerthreads);
int on =0;
for (on=0; on<consumerthreads; on++) {
*(bill->rep+on) = (struct SharedData *)malloc(sizeof(struct SharedData));
init(*(bill->rep + on), on); //
}}
int main(int argc, const char * argv[])
{
databases(argv[1]); /* Takes care of setting up the database*/
categories(argv[2]); /*Takes care of setting up the book categories*/
bookorders = argv[3];
key_t key = ftok("garbage.txt", 71);
int eyedee = shmget(key, sizeof(struct OverSharedData ),
IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (eyedee == -1)
{
perror("shmget");
exit(1);
}
struct OverSharedData *remp = (struct OverSharedData *) shmat(eyedee, 0, 0);
if (remp == (void *) -1)
{
perror("shmat");
exit(1);
}
initialize(remp);
struct SharedData * d = *(remp->rep + 0);
printf("Hallo\n");
shmctl(eyedee, IPC_RMID, 0);
pid_t forkk = fork();
if (forkk==0) {
/*THIS DOES NOT WORK*/
printf("Entered consumer check: %d\n", remp->rop);
int z = 0;
pthread_t Consumer_Threads[consumerthreads];
for (z=0; z<consumerthreads; z++) {
remp->rop = z;
d = *(remp->rep + z);
d->da = z;
pthread_create((Consumer_Threads+z), 0, Consumer, d);
}
for (z = 0; z<consumerthreads; z++) {
pthread_join(Consumer_Threads[z], NULL);
}
shmdt(remp);
}
else{
/*THIS WORKS*/
printf("Entered Producer: %d\n",remp->rop);
pthread_t Produc;
pthread_create(&Produc, 0, Producer, remp);
pthread_join(Produc, NULL);
printf("Hey guys: %d\n", remp->rop);
shmdt(remp);
}
My guess is that I didn't initialize the struct correctly, but I'm not all too clear what I'm doing wrong. I left out some of the other initializing code but I figured since I can't even access the int in the OverSharedData struct, it's more of a matter where I can't access the struct in the first place.
The problem is that your shared data (the single OverSharedData object) contains pointers to non-shared data. You need to allocate all the data that you want shared in the shared memory segment, rather than with malloc. Something like:
static void *shared_available;
static size_t shared_left;
void init_shared(size_t size) {
key_t key = ftok("garbage.txt", 71);
int eyedee = shmget(key, size,
IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (eyedee == -1) {
perror("shmget");
exit(1); }
shared_available = shmat(eyedee, 0, 0);
if (shared_available == (void *) -1) {
perror("shmat");
exit(1); }
shared_left = size;
}
void *alloc_shared(size_t size) {
void *rv = shared_available;
if (size > shared_left) {
fprintf(stderr, "Ran out of shared memory!\n");
exit(1); }
shared_available = (char *)rv + size;
shared_left -= size;
return rv;
}
OverSharedData *initialize() {
init_shared(sizeof(struct OverSharedData) +
sizeof(struct SharedData *) * consumerthreads +
sizeof(struct SharedData) * consumerthreads)
OverSharedData *bill = alloc_shared(sizeof(OverSharedData));
bill->rep = alloc_shared(sizeof(struct SharedData*)*consumerthreads);
for (int on=0; on<consumerthreads; on++) {
bill->rep[on] = alloc_shared(sizeof(struct SharedData));
init(&bill->rep[on], on); }
}
The above will still have problems if the init routine tries to store pointers to non-shared memory into the SharedData struct (you don't show the definition of either, so we can't say).
If you want to be able to more flexibly allocate and manage shared memory across processes, you really need to use a general purpose shared memory allocator/manager, such as this

Shared Memory Ftruncate

I am trying to share some data between parent and child process. For the first time I can write and read data to shared memory. However if I want to write one more data, I got truncate error. I know something is wrong with my code. I feel like I need to implement base address part but I don't know how to do it.
void *attach_shmem(int shmem_fd, int object_size){
void *addr;
/* resize it to something reasonable */
if (ftruncate(shmem_fd, object_size) == -1){
perror("failed to resize shared memory object\n");
exit(EXIT_FAILURE);
}
addr = mmap(base, object_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmem_fd, 0);
if (addr == MAP_FAILED){
perror("failed to map shared memory object\n");
exit(EXIT_FAILURE);
}
return addr;
}
void *map_shmem(char *path, int object_size){
int shmem_fd;
/* open one that has already been created */
shmem_fd = shm_open(path, O_RDWR, S_IRUSR | S_IWUSR);
if (shmem_fd == -1){
fprintf(stdout, "failed to open shared memory object\n");
exit(EXIT_FAILURE);
}
return attach_shmem(shmem_fd, object_size);
}
void add_ip_to_shared_memory(char *ip, int val, int num)
{
sem_t *sem = sem_open("/basag_sem", 0);
struct SharedMem *node;
char ip_binary[100];
void *addr;
strcpy(ip_binary, transform_ip_to_bits(ip));
node = malloc(sizeof(struct SharedMem));
strcpy(node->ip, strndup(ip_binary, num));
node->val = val;
//semaphore block
sem_wait(sem);
//add node to shared memory
addr = map_shmem("gungor_shmem", sizeof(*node));
memcpy(addr, node, sizeof(*node));
base = addr;
sem_post(sem);
free(node);
//semaphore release
//write_to_screen(node->ip);
setbuf(stdout, NULL);
}

Resources