1

I have a problem, I would like to do a fork for example a fork of 20processes, this fork created, should not do anything until the last one is not created, and I want to do it with semaphore, how can I implement it?

for (i=0; i<20; i++) {
   switch (fork()) {
       case: -1:
        exit(EXIT_FAILURE);

        case 0:
            execve(....); 
            exit(EXIT_FAILURE);

        default: 
            printf ("Child Created!");
   }

}

  • Could you be more specific? Did you check the waitpid() functions? Because a semaphore won't help you here unless you use mmap to share variables or (unix)sockets to communicate with the other processes. – Unh0lys0da Dec 01 '17 at 14:06
  • i don't have to wait for a process to end, but i want them to start executing at the end of the last creation of the child. I need to use IPC objects with the System V standard –  Dec 01 '17 at 14:09
  • 1
    In that case mmap might do the trick, it allows you to share memory between processes. You could allocate an int with mmap, initialize it to 0, and have the last process that forks set it so 1. then all processes can do: while(!(*mapped_flag)); execve(); – Unh0lys0da Dec 01 '17 at 14:13
  • Ok, I understand what you say but i must use the object semaphore using the functions segment(),semctl() and semop() –  Dec 01 '17 at 14:17

1 Answers1

4

Here's you homework. You can pay me later.

#include <semaphore.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    sem_t *sem;
    if(MAP_FAILED==(sem=mmap(0, sizeof(*sem), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)))
        { perror("mmap"); exit(EXIT_FAILURE); }

    if(0>sem_init(sem, 1/*shared*/, 0/*init val*/))
        { perror("sem_init"); exit(EXIT_FAILURE); }

    for(int i=0; i<20; i++){
        switch(fork()){
        case -1: perror("fork"); /*you'll need to kill the children here*/ 
                   exit(EXIT_FAILURE);
        case 0: 
                 puts("waiting");
                 sem_wait(sem);
                 puts("running");
                 exit(0);
        default:
                 puts("Child created"); 
        }
    }
    puts("done forking. signalling children");
    usleep(1000000);
    for(int i=0; i<20; i++)
        sem_post(sem);

}

The idea is simple. Init the (necessarily, shared) semaphore to zero and make each child wait on it before it does its thing. When you're done forking in the parent, you post to the semaphore 20 times so that each child's sem_wait call completes.

Same thing with with SysV semaphores:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

//the manpage says we should define this union (as follows)
union semun {
           int              val;    /* Value for SETVAL */
           struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
           unsigned short  *array;  /* Array for GETALL, SETALL */
           struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                       (Linux-specific) */
       };

int main()
{
    key_t key;
    char tmpl[]="/tmp/XXXXXX";
    {
        int tmpfd; 
        if(0>(tmpfd = mkstemp(tmpl)))
            { perror("mkstemp"); exit(EXIT_FAILURE); }
        close(tmpfd);
    }

    //Get a key
    puts(tmpl);
    key=ftok(tmpl, 'A');

    int sem;
    int ec = EXIT_SUCCESS;

    if(0>(sem = semget(key, 1 /*1 sem*/, IPC_CREAT|IPC_EXCL|0600)))
        { perror("semget"); goto fail; }
    if(0>semctl(sem, 0 /*ix*/, SETVAL, (union semun){ .val=0 }))
        { perror("sem init"); goto fail; }

    for(int i=0; i<20; i++){
        switch(fork()){
        case -1: { perror("fork"); /*you'll need to kill the children here*/ exit(EXIT_FAILURE);  }
        case 0: 
                 puts("waiting");
                 semop(sem, (struct sembuf[1]){{ .sem_op=-1  }}, 1);
                 puts("running");
                 exit(0);
        default:
                 puts("Child created"); 
        }
    }
    puts("done forking. signalling children");
    usleep(1000000);
    //can up it by 20 in one go
    semop(sem, (struct sembuf[1]){{ .sem_op=+20  }}, 1);
    goto success;

fail:  ec = EXIT_FAILURE;
success:
    semctl(sem, 0, IPC_RMID);
    unlink(tmpl);
    exit(ec);
}

Here you have to dance around the SysV IPC API ugliness and the need to set up an file-based key, but then, as a reward, you get to increment the semaphore by 20 in one go.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • If you wanted to use SysV semaphores, you could make the coalesce all the `sem_post` calls into one `semop`, and there'd be implicit sharing without the need for `mmap`ping, but the logic could be the same. – Petr Skocik Dec 01 '17 at 14:22
  • ok thanks for the effort, but i need to use the struct sembuf and System V functions, like semget(), semctl() and semop(). –  Dec 01 '17 at 14:23
  • 2
    Then do that, google how it works, someone already showed you the idea of using the semaphore, actually implementing a specific function is up to you. If you still fail then edit your question or ask a new one where you show what you tried, why you think it failed, you can't expect people to do your homework assignments for you. – Unh0lys0da Dec 01 '17 at 14:31
  • @JimBelushi Sounds like something that could've been mentioned in the question, along with at least some kind of an attempt at a solution. ;) Fortunately for you, I felt very much like procrastinating, so I added a SysV solution as well. – Petr Skocik Dec 01 '17 at 15:04