简体   繁体   中英

Parent is executing before child process, how to force opposite through semaphores?

I am having trouble using shared memory, semaphores and forks in Unix C. My semaphores are not posix. I create a pointer to shared memory 2*sizeof(float). I initialize the value of my semaphore to 2 with semctl. I do a fork() in a for loop (i<2). In the child processes (if fork() == 0) each child does ap operation on the semaphore (-1), writes to shared memory then does av operation (+1) then exits. The Parent process does ap operation (-2) on the semaphore, reads the entirety of the shared memory segment(with a for loop) and does av operation (+2). He waits on the child processes before exiting to avoid zombies. The problem i have in output is that i get :

 Parent reading 0
 Parent reading 1
 Parent reading 0
 Parent reading 1
 Child writing 0
 Child writing 1

When what i should be getting is :

 Child writing 0
 Child writing 1
 Parent reading 0
 Parent reading 1

I have tried initializing my semaphore to 1 instead of 2 but that just stalls the program since the semaphore will never have a value of two and thus the parent process will never read.

If what i have understood about semaphores is correct, the fact that i initialize it to 2 means that the parent process can directly read even though none of the children have written anything. How can i resolve this problem?

EDIT: I added a simplified version of my code after request, i have removed error checking, and waiting for children to reduce length.

/** OPEN MEMORY **/

int shmid1 = shmget(1990, (size),  IPC_CREAT | 0666 ); 

float * shmPt = (float*)shmat(shmid1, NULL, 0);    

/** CREATE INITIALIZE SEMAPHORE **/   

semun1.val = 2;

int semid = semget(1991, 1, 0666 | IPC_CREAT)

semctl(semid, 0, SETVAL, semun1 )


  /**  CREATE PROCESSES **/
  for ( ii = 0; ii < 2; ++ii) {
    if ((p = fork()) == 0) {

      int semid = semget(1991, 1, 0666);

      struct sembuf p_buf;

      p_buf.sem_num = 0;p_buf.sem_op = -1;p_buf.sem_flg = SEM_UNDO;
      /** LOCK **/ 
      semop(semid, &p_buf,1);
      /** WRITE **/
      shmPt[ii] = RandomFloat;

      v_buf.sem_num = 0;v_buf.sem_op = 1;v_buf.sem_flg = SEM_UNDO;
      /** UNLOCK **/
      semop(semid, &v_buf,1) 

      exit(0);
    }
    else {

      int semid = semget(1991, 1, 0666);

      struct sembuf p_buf;

      p_buf.sem_num = 0;p_buf.sem_op = -2;p_buf.sem_flg = SEM_UNDO;
      /** LOCK **/ 
      semop(semid, &p_buf,1);
      /** READ **/
      for(int j =0;j<2;j++) tabFloat[j] = shmPt[j];

      v_buf.sem_num = 0;v_buf.sem_op = 2;v_buf.sem_flg = SEM_UNDO;
      /** UNLOCK **/
      semop(semid, &v_buf,1) 

    }
}

EDIT : My ultimate goal is to have 24 children writing one by one into a shared memory segment of the same size and only when it is full, then the parent can read everything and process the information. On top of that all of this needs to be in a while loop (imagine 24 cars that keep generating random times everytime they complete a lap until the first car has finished 50 laps)

You're mis-using semaphores. The general idea is that a semaphore counts "how many entities (threads, whatever) are allowed to use this data right now". By starting the count at 2, you're saying "two threads may use this now". Semaphores do not say which entities, nor how (read vs write), only how many . For example, semaphores can to be used to count the number of retrievable items in a producer/consumer queue: the producer increments and the consumer decrements. (Of course, semaphores come in all kinds of expanded flavors; since you say these are "not POSIX", but not what they are , it's hard to generalize much more.)

One way to make this work as described—but of course, actual code tends to vary from descriptions—is to start the semaphore count at 0, fork a child, have the child write without looking at the semaphore count, fork another child, have that child also write without looking at the semaphore count, and then have the parent wait on the semaphore (P). That is, the semaphore says "none shall pass" but the children don't actually look at it. Then, the two children each do V operations (+1 in each). Once the semaphore has gone to 1, the parent starts: he can then find at least one (but perhaps only one) child-result. The parent can do another P immediately if he needs to have both results.

(More generally, though, you may want reader/writer locks or mutexes and condition variables. If you have POSIX threads, see pthread_cond_init() , pthread_cond_wait() , pthread_cond_signal() , pthread_mutex_init() , etc.)


Aha, from the comment and question-edit, I see that you're using the wretched System V shared memory and semaphore interface.

  • Are you really stuck with that? The POSIX thread stuff is nicer, in my opinion (and generally lighter-weight).
  • How do you intend to organize your shared-memory? You may get less lock-contention if each car has its own lap times region, shared only with the display thread/proc: there's one single producer (the car) and one single consumer (display thread/proc), but 24 such locks (one per car). If all cars share one shared-memory region with the display thread/proc, you need only one lock, but it's much more active. Which one is "better" depends on what you are doing.
  • And, if you want to wait for "some car to finish 50 laps", consider having each car have its own private (or possibly shared-with-display) counter, and one counting semaphore for "number of cars that have hit 50 laps". Each car simply counts up and upon reaching 50, increments the counting semaphore (once) too.

Final (I hope) edit: after fixing smaller problems, the last remaining one was the use of SEM_UNDO in each child process, which would do a V (of +1) to signal "data produced and all done" and then exit . SEM_UNDO records a balancing adjustment value that is applied on process exit, so the semaphore would count up, but then immediately count right back down, leaving the parent waiting for another V that would never occur.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM