Reputation: 6815
I'm trying to create a program in C for Unix. Given an array I should pass every item of the array from parent process to its child through shared memory and using a semaphore. This is what I've got.
const key_t sem_key = (key_t)0x12345611;
const key_t shm_key = (key_t)0x12339611;
int sem_wait(int semafor_id)
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = SEM_UNDO;
return semop(semafor_id, &sb, 1);
}
int sem_signal(int semafor_id)
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = SEM_UNDO;
return semop(semafor_id, &sb, 1);
}
int main(int argc, char *argv[])
{
int status;
char str[3];
str[0] = 'z';
str[1] = 'x';
str[2] = 'y';
// Create shared memory
int memory_id = shmget(shm_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
if (memory_id < 0)
{
printf("Shared memory creating failed\n");
return 1;
}
// Create semafor
int semafor_id = semget(sem_key, 10, 0600 | IPC_CREAT | IPC_EXCL);
if (semafor_id < 0)
{
printf("SEMAFOR creating failed\n");
return 1;
}
semctl(semafor_id, 0, SETVAL, 1); // init semafor
pid_t pid = fork();
if (pid < 0)
printf("FORK FAILED\n");
else if (pid > 0)
{
printf("PARENT\n");
void *address = shmat(memory_id, NULL, 0);
if (address == NULL)
{
printf("Failed to attach memory\n");
return 1;
}
if (sem_wait(semafor_id) < 0) // sem_wait() decrements (locks) the semaphore pointed to by sem.
{
printf("Failed wait parent\n");
return 1;
}
for (int i = 0; i < 3; i++)
{
void *address = shmat(memory_id, NULL, 0);
if (address == NULL)
{
printf("Failed to attach memory\n");
return 1;
}
printf("writer started.\n");
char *data = (char *) address;
*data = str[i];
printf("writer ended.\n");
}
if (shmdt(address) != 0)
printf("Failed to detach shared memory\n");
if (sem_signal(semafor_id) < 0)
printf("Failed signal parent\n");
else
printf("Parent leave generating number\n");
// wait for child
wait(&status);
printf("Destroy semafor\n");
semctl(semafor_id, 0, IPC_RMID, 0);
printf("Destroy shared memory\n");
shmctl(memory_id, IPC_RMID, 0);
return 0;
} // close parent case
else
{
printf("CHILD\n");
if (sem_wait(semafor_id) < 0)
printf("FAILED wait child\n");
else
{
void *address = shmat(memory_id, NULL, 0);
if (address == NULL)
{
printf("Failed to atach memory in child\n");
return 1;
}
char *data = (char *) address;
printf("Child read data %c\n", *data);
if (shmdt(address) != 0)
printf("Failed to detach shared memory in child\n");
}
}
if (sem_signal(semafor_id) < 0)
printf("Failed signal child\n");
else
printf("Leave data reading\n");
return 0;
}
The current output is this:
writer started
writer ended
writer started
writer ended
writer started
writer ended
child read data y
destroy semafor
destroy shared memory
As you can see the parent write in the shared memory 3 times and then the child get the access of the shared memory printing the data of the shared memory. The output I'd like to have is this:
writer started
writer ended
child read data z
writer started
writer ended
child read data x
writer started
writer ended
child read data y
destroy semafor
destroy shared memory
Upvotes: 1
Views: 612
Reputation: 2865
The problem is in the logic. After removing boilerplate code, the logic is as follows:
Get shared memory
Get semaphore with value 1
fork
in parent:
wait semaphore (problem #1, #2) <-- supposed to decrement it to 0
for(i=0;i<3;++i)
{
write next char <<-- no synchronization here :-(
}
signal semaphore
wait for child
STOP
in child:
wait semaphore (problem #1)
read next char
signal semaphore
(problem #3)
STOP
in english:
the writer acquires write lock
writes 3 values into the same location
then signals the reader to read data.
At this moment writer moves on to wait for child PID to die.
The reader reads the last value once
signals the semaphore
then exits.
At this moment writer sees reader died
cleans up
...and exits
Problem #1: parent and child processes run concurrently at the same time.
Any of them can first succeed at sem_wait
. If child does that first it would
print out gibberish initial uninitialized value from shared memory and parent
would be locked waiting for semaphore forever.
Try adding imaginary (or not) sleep(1) into parent before wait semaphore.
This will emulate processor context switch.
Problem #2: You wait/signal only once. E.g. you write all the values and only then signal. That is why only the last value is printed.
Problem #3: Child does not have any loops. How is he supposed to read 3 values?!
The logic should be:
Writer
Do 3 times:
acquire write semaphore
write next value
signal reader semaphore
wait child to die
clean up
exit
reader:
Do 3 times:
acquire reader semaphore
read next value
signal writer semaphore
exit
Upvotes: 2