Multiprocessing in C POSIX - c

I have two processes that need to communicate with each other. One is the sender and the other is the receiver.
The receiver should read a word from the user and save it in the shared memory. The sender should then read this word and use it to save an answer to it in the shared memory. Now the receiver should output this answer.
Small minimal example:
//Process 1
//
do
{
strcpy(shared_mem->message, getCommand());
sem_post(&semaphore_getMessage);
sem_wait(&semaphore_getAnswer);
printf("%s",shared_mem->Answer);
} while (getCommand()!=0);
//Process 2
//
sem_wait(&semaphore_getMessage);
do
{
strcpy(shared_mem->Answer, getAnswer(shared_mem->Message));
sem_post(&semaphore_getAnswer);
sem_wait(&semaphore_getMessage);
} while (strcmp(shared_mem->Message, "Exit\n") != 0);
Unfortunately, I sometimes get a deadlock (i.e. my programme is permanently stuck in a state). I just don't know how... Does anyone have an idea? Language is C
The Semaphores are defined in an struct:
typedef struct
{
sem_t semaphore_getMessage;
sem_t semaphore_getAnswer;
} semaphores;
as are the filedescriptors and the mapping of the memory:
typedef struct
{
sempahores *mapped_region_locks;
} mappings;
typedef struct
{
int locks;
} filedescriptor;
The locks are then mapped to shared space:
filedescriptor->locks = shm_open(NAME_SEMAPHORES, FLAGS_READWRITE_ACCES, MODE_RW);
ftruncate(filedescriptor->locks, sizeof(filedescriptor));
mappings->mapped_region_locks = mmap(NULL, sizeof(semaphores),
PROT_READ | PROT_WRITE, MAP_SHARED, filedescriptor->locks, 0);
sem_init(&mapped_region_locks->semaphore_getMessage, 0, 1);
//in the first code snippet "mapped_region_locks->semaphore_getMessage" is referred to as "semaphore_getMessage" as it is easier to read.

Related

POSIX unnamed semaphore in shared memory is not responding to post or wait

I have unnamed semaphores that I am placing in shared memory in one process following the method that I found here on SO
In P0:
/* addr is a pointer to the base of the shared memory area */
sem_t *sem = (sem_t*) addr;
void *usableSharedMemory = (char*) addr + sizeof(sem_t)
sem_init(sem, 1, 0);
In P1:
if ((addr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
exit(EXIT_FAILURE);
}
sem_t *my_sem = (sem_t*) addr;
...
sem_post(my_sem);
...
sem_wait(my_sem);
If I call sem_getvalue(my_sem) before and after a post or wait, the value of the semaphore does not change. I have prints like this to debug:
int v = 0;
v = sem_getvalue(rsem, &v);
printf("BEFORE-POST:%d\n", v);
sem_post(rsem);
v = sem_getvalue(rsem, &v);
printf("AFTER-POST:%d\n", v);
Before and after the call to sem_post, the semaphore value is zero (0).
I haven't used this kind of semaphore before, but I see a number of things one could trip over.
I'm not excited about the funky pointer math that the other SO post was suggesting, and I can't tell from here whether both processes are actually talking to the same chunk of shared memory.
A good idea in this case is to avoid the pointer math and use a struct to overlay the shared memory segment so you have a clear organization, and add a magic number so everybody can tell if they're getting a valid segment or some random bogus memory:
#define MYSHM_MAGIC 12345987 // can be anything random-ish
struct mysharedmem {
int magicvalue;
sem_t MySemaphore;
void *UsableMemory;
};
This structure overlays whatever your shared memory segment is and allows you to use consistent and more readable access methods, especially if you add additional variables that all sides agree on. The code that creates the segment should initialize it:
// in P1 ?
struct mysharedmem *myshm = mmap(NULL, SIZE, ...);
myshm->magic = MYSHM_MAGIC;
sem_init(&myshm->MySemaphore, 1, 0);
...
and then in the other process, once you obtain the shared address, actually ask the segment "did you come from the place I think?" by checking the magic number. The value of the number doesn't matter, only that both sides agree.
// In P0 ?
struct mysharedmem *myshm = addr; // obtained somehow
if (myshm->magic != MYSHM_MAGIC)
{
error - we don't see the special magic number
}
.. do stuff
How does P0 obtain a handle to the shared memory segment created by the P1?

if an unnamed semaphore is initialized in shared memory, does shm_unlink() destroy the semaphore?

I'm using POSIX semaphores and shared memory to coordinate a single producer and single consumer buffer. I want to completely initialize this shared memory and semaphore, whether or not the shared memory is named or is being used. This is the function I want to use:
/* shared data */
typedef struct {
sem_t sem;
size_t head;
size_t tail;
} shared_struct;
shared_struct * create_shmem_struct(const char *name) {
/* re-open shared memory */
shm_unlink(name);
int fd = shm_open (name, (O_CREAT | O_RDWR), (S_IRUSR | S_IWUSR));
ftruncate (fd, sizeof(shared_struct));
shared_struct *ptr = (shared_struct *) mmap (NULL, sizeof(shared_struct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
/* initialize semaphore */
/* can i do this here? : sem_destroy(&ptr->sem); */
sem_init(&ptr->sem, 1, 1);
return ptr;
}
What happens to the semaphore if it already is initialized but no sem_destroy is called before shm_unlink? Does it hurt to call sem_destroy on a possibly uninitialized unnamed semaphore before sem_init?
I should note, I'm basing this strategy on Michael Kerrisk's slides from the man pages found here.

Can anyone help me make a Shared Memory Segment in C

I need to make a shared memory segment so that I can have multiple readers and writers access it. I think I know what I am doing as far as the semaphores and readers and writers go...
BUT I am clueless as to how to even create a shared memory segment. I want the segment to hold an array of 20 structs. Each struct will hold a first name, an int, and another int.
Can anyone help me at least start this? I am desperate and everything I read online just confuses me more.
EDIT: Okay, so I do something like this to start
int memID = shmget(IPC_PRIVATE, sizeof(startData[0])*20, IPC_CREAT);
with startData as the array of structs holding my data initialized and I get an error saying
"Segmentation Fault (core dumped)"
The modern way to obtain shared memory is to use the API, provided by the Single UNIX Specification. Here is an example with two processes - one creates a shared memory object and puts some data inside, the other one reads it.
First process:
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#define SHM_NAME "/test"
typedef struct
{
int item;
} DataItem;
int main (void)
{
int smfd, i;
DataItem *smarr;
size_t size = 20*sizeof(DataItem);
// Create a shared memory object
smfd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0600);
// Resize to fit
ftruncate(smfd, size);
// Map the object
smarr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, smfd, 0);
// Put in some data
for (i = 0; i < 20; i++)
smarr[i].item = i;
printf("Press Enter to remove the shared memory object\n");
getc(stdin);
// Unmap the object
munmap(smarr, size);
// Close the shared memory object handle
close(smfd);
// Remove the shared memory object
shm_unlink(SHM_NAME);
return 0;
}
The process creates a shared memory object with shm_open(). The object is created with an initial size of zero, so it is enlarged using ftruncate(). Then the object is memory mapped into the virtual address space of the process using mmap(). The important thing here is that the mapping is read/write (PROT_READ | PROT_WRITE) and it is shared (MAP_SHARED). Once the mapping is done, it can be accessed as a regular dynamically allocated memory (as a matter of fact, malloc() in glibc on Linux uses anonymous memory mappings for larger allocations). Then the process writes data into the array and waits until Enter is pressed. Then it unmaps the object using munmap(), closes its file handle and unlinks the object with shm_unlink().
Second process:
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#define SHM_NAME "/test"
typedef struct
{
int item;
} DataItem;
int main (void)
{
int smfd, i;
DataItem *smarr;
size_t size = 20*sizeof(DataItem);
// Open the shared memory object
smfd = shm_open(SHM_NAME, O_RDONLY, 0600);
// Map the object
smarr = mmap(NULL, size, PROT_READ, MAP_SHARED, smfd, 0);
// Read the data
for (i = 0; i < 20; i++)
printf("Item %d is %d\n", i, smarr[i].item);
// Unmap the object
munmap(smarr, size);
// Close the shared memory object handle
close(smfd);
return 0;
}
This one opens the shared memory object for read access only and also memory maps it for read access only. Any attempt to write to the elements of the smarr array would result in segmentation fault being delivered.
Compile and run the first process. Then in a separate console run the second process and observe the output. When the second process has finished, go back to the first one and press Enter to clean up the shared memory block.
For more information consult the man pages of each function or the memory management portion of the SUS (it's better to consult the man pages as they document the system-specific behaviour of these functions).

Issues with mmap of Complex Types

I have an issue attempting to access shared memory using mmap for complex types.
So I allocate my memory as so in my parent process:
/* Create mmap file */
fid = open(TMP_FILE_NAME, O_RDWR | O_CREAT | O_EXCL, (mode_t) 0755);
if (fid < 0)
{
printf("Bad Open of mmap file <%s>\n", TMP_FILE_NAME);
die(-1);
}
/* Make mmap file Buffer Size */
status = ftruncate(fid, INPUT_BUFFER_SIZE);
if (status)
{
printf("Failed to ftruncate the file <%s>, status = %d\n", TMP_FILE_NAME, status);
die(-1);
}
/* Initialize Shared Memory */
mmap_ptr = mmap((caddr_t) 0,
INPUT_BUFFER_SIZE, // Default Buffer Size
PROT_WRITE | PROT_READ, // R/W Permissions
MAP_SHARED, // No file backing
fid,
(off_t) 0);
if (mmap_ptr == MAP_FAILED)
{
printf("Failed to perform mmap, Exiting\n");
die(-1);
}
Now the Struct that I'm passing in memory to my child process is as follows:
/* Data structue for IPC */
typedef struct {
int current_active_id;
int consume_remaining;
Queue buffer;
} input_buffer;
where Queue is a data structure class from the following:
http://www.idevelopment.info/data/Programming/data_structures/c/Queue/Queue.shtml
In my child process it's okay when I do this, it returns the correct value:
printf("Got here... Shared Mem: %d\n", input_queue->consume_remaining);
but when I do something like:
IsEmpty(input_queue->buffer)
it crashes and in the code of the Queue it's only doing this:
return Q->Size == 0;
Any help would be appreciated, thanks!!
Queue is a pointer to struct QueueRecord, and should be allocated as such, presumably using the same shared memory segment. note that this should also be mapped at the same address in both parent and child, or you will not be able to dereference it.
The structure you are putting in the map contains pointers. The pointers are all relative to the address space of the process that created them.
If the other process doesn't mmap at the same address, or if it does but the allocations made for the queue aren't taken from inside that buffer, the pointers will be invalid in the other process.

Initializing shared memory safely

I have several processes communicating with each through POSIX shared memory on OS X.
My issue is these processes could spawn in any order, and try to initialize the shared memory segment at the same time.
I tried using advisory locks with fcntl and flock but both fail telling me I'm passing an invalid file descriptor (I'm positive the file descriptor is not invalid). So clearly that's out of the picture.
Are there any alternatives to this? Or is there any details about using locks with shared memory that I'm not aware of?
Edit:
My attempt at using locks looks like this:
// Some declarations...
struct Queue {
int index[ENTRIES_PER_QUEUE];
sem_t lock;
sem_t readWait;
sem_t writeSem;
struct Entry slots[ENTRIES_PER_QUEUE];
};
struct ipc_t {
int fd;
char name[512];
struct Queue* queue;
};
ipc_t ipc_create(const char* name, int owner) {
int isInited = 1;
struct Queue* queue;
struct flock lock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 0
};
ipc_t conn = malloc(sizeof(struct ipc_t));
sprintf(conn->name, "/arqvenger_%s", name);
conn->fd = shm_open(conn->name, O_CREAT | O_RDWR, 0666);
if (conn->fd == -1) {
free(conn);
perror("shm_open failed");
return NULL;
}
if (fcntl(conn->fd, F_SETLKW, &lock) == -1) {
perror("Tanked...");
}
// Do stuff with the lock & release it
The output I get is:
Tanked...: Bad file descriptor
A common technique is to first call shm_open with O_CREAT|O_EXCL. This will succeed for only one process that then has to do the setup. The others then would have to do the open as before and wait a bit, probably polling, that the setup is finished.
Edit: To show how this could work as discussed in the comments.
struct head {
unsigned volatile flag;
pthread_mutex_t mut;
};
void * addr = 0;
/* try shm_open with exclusive, and then */
if (/* we create the segment */) {
addr = mmap(something);
struct head* h = addr;
pthread_mutex_init(&h->mut, aSharedAttr);
pthread_mutex_lock(&h->mut);
h->flag = 1;
/* do the rest of the initialization, and then */
pthread_mutex_unlock(&h->mut);
} else {
/* retry shm_open without exclusive, and then */
addr = mmap(something);
struct head* h = addr;
/* initialy flag is guaranteed to be 0 */
/* this will break out of the loop whence the new value is written to flag */
while (!h->flag) sched_yield();
pthread_mutex_lock(&h->mut);
pthread_mutex_unlock(&h->mut);
}
I have been able to reproduce the problem. Then I found a sad notice in the standard:
When the file descriptor fildes refers to a shared memory object, the
behavior of fcntl() shall be the same as for a regular file except the
effect of the following values for the argument cmd shall be
unspecified: F_SETFL, F_GETLK, F_SETLK, and F_SETLKW.
So it's likely it's not yet supported. For such a simple setup I would use a semaphore for signaling "the memory is ready".
EDIT
It seems I need to mention semaphores can be created using "O_EXCL" (so there are no races).

Resources