views:

2177

answers:

7

Hello,

I have two process and a shared memory zone, my workflow is like this. The process A write some data in the shared memory, after that it should wait and send a signal to other process B to start running. The process B should read some data from the shared memory do some stuff write the result, and send a signal to the process A to keep running, after this process B should wait.

Can anyone plese provide an example or a place where I can find how can I stop a process and how can I start running again the process?. I am working in Linux and C++.

I already have semaphores, but the thing that I do not like, it is that one process is stop a bunch of seconds reading all the time from the shared memory, until it detects that it can run. That's why I was thinkin only in send a signal in the right moment

Update with the solution

I selected the answer of stefan.ciobaca as favourite because is a complete solution that it works and it has a very good explanation. But in all of the other answers there are other interesting options.

+4  A: 

Not quite what you've asked for, but could you use pipes (named or otherwise) to affect the synchronization? This puts the locking burden onto the OS which already knows how to do it.

Just a thought.


Response to comment: What I had in mind was using pipes rather than shared memory to more the data around, and getting synchronization for free.

For instance:

  1. Process A starts, sets up a bi-directional pipe and forks process B using popen (3).
  2. Immediately after the fork:

    • A does some work and writes to the pipe

    • B attempts to read the pipe, which will block until process A writes...

  3. Next:

    • A attempts to read the pipe, which will block until data is available...

    • B does some work and writes to the pipe

  4. goto step 2 until you reach a ending condition.

This is not what you asked for. No shared memory, no signals, but it should do the trick...

dmckee
Hi, can you provide more information in the use of pipes, or post a link with more info. Thanks
Eduardo
A: 

Do you really need to stop the process (exit it), and restart it, or do you just want it to wait until some event occurs?

If the latter you should read up on IPC and process synchronisation (e.g. semaphores, mutexes).

If the former, look at the source code for something like init in linux.

frankodwyer
Hello It just wait, I already have an implementation with sempahores, what I am looking for is something with signals, if it is possible
Eduardo
It could be done with signals (e.g some processes are written to restart themselves when they get SIGHUP) but something like a semaphore or the alternative approaches like pipes in the other answers are better I think.
frankodwyer
A: 

What you are looking for is called blocking. Process B should block on a call from process A and Process A should block on a call from process B. If a processes is blocked (waiting for the call from the other process) it sits idly in the background and only wakes up when it receives a message.

Select is probably the function you are looking for.

alumb
A: 

I suggest using semaphores to synchronize the processes.

Reading the headline, I thought that SIGSTOP and SIGCONT might be possibilities, but that's probably not a good idea; you want them to stop when they're in the right (safe) place to stop. That's what semaphores are for.

Many other IPC mechanisms could also achieve similar results, but semaphores are cross-process communication mechanisms (you'd use mutexes between different threads in a single process).

Jonathan Leffler
+1  A: 

What about using Unix domain sockets for the IPC instead of shared memory? That way each process can block on reading from the socket while the other does its work.

Edit: This is similar to dmckee's answer, but offers more control on the blocking and IPC. The popen approach is definitely easier to implement, however.

Jason Day
+1 similar to my thought. This would be socketpair (2), no?
dmckee
+6  A: 

Here is a proof-of-concept of how it can be done:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <assert.h>

typedef void (*sighandler_t)(int); 

#define SHM_SIZE 8  /* size of shared memory: enough for two 32 bit integers */

volatile int cancontinue = 0;

void halt(char *err) { perror(err); exit(1); }
void handler(int signum) { assert(signum == SIGUSR1); cancontinue = 1; }

int main(void)
{
  key_t key;
  int id;
  int *data;
  pid_t otherpid;

  printf("Hi, I am the %s process and my pid is %d\n", 
#ifdef PRODUCER_MODE
  "writer"
#else
  "reader"
#endif
, getpid());
  printf("Please give me the pid of the other process: ");
  scanf("%d", &otherpid);

  // get a pointer to the shared memory
  if ((key = ftok("test_concur.c", 'R')) == -1) halt("ftok");
  if ((id = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1) halt("shmget");
  if ((data = shmat(id, (void *)0, 0)) == (int *)(-1)) halt("shmat");

  sighandler_t oldhandler = signal(SIGUSR1, handler);

  while (1) {
#ifdef PRODUCER_MODE
    printf("Enter two integers: ");
    scanf("%d %d", data, data + 1);

    printf("Sending signal to consumer process\n");
    kill(otherpid, SIGUSR1);

    printf("Waiting for consumer to allow me to continue\n");
    while (!cancontinue);
    cancontinue = 0;

    if (*data + *(data + 1) == 0) { printf("Sum was 0, exiting...\n"); break; }
#else
    printf("Waiting for producer to signal me to do my work\n");
    while (!cancontinue);
    cancontinue = 0;

    printf("Received signal\n");
    printf("Pretending to do a long calculation\n");
    sleep(1);
    int sum = *data + *(data + 1);
    printf("The sum of the ints in the shared memory is %d\n", sum);

    printf("Signaling producer I'm done\n");
    kill(otherpid, SIGUSR1);

    if (sum == 0) break;
#endif
  }

  signal(SIGUSR1, oldhandler);

  /* detach from the segment: */
  if (shmdt(data) == -1) {
    perror("shmdt");
    exit(1);
  }

  // don't forget to remove the shared segment from the command line with

  // #sudo ipcs
  // ... and look for the key of the shared memory segment

  // #ipcrm -m <key>

  return 0;
}

The above program is actually two programs, a consumer and a producer, depending on how you compile it.

You compile the producer by making sure that the PRODUCER_MODE macro is defined:

# gcc -Wall -DPRODUCER_MODE -o producer test_concur.c

The consumer is compiled without defining the PRODUCER_MODE macro:

# gcc -Wall -o consumer test_concur.c

The consumer and producer share some global memory (8 bytes pointed to by data); the producer's role is to read two 32-bit integers from stdin and write them to the shared memory. The consumer reads integers from the shared memory and computes their sum.

After writing the data to shared memory, the producer signals to the consumer (via SIGUSR1) that it may begin the computation. After the computation is done, the consumer signals to the producer (via SIGUSR1 again) that it may continue.

Both processes stop when the sum is 0.

Currently, each program begins by outputing its pid and reading from stdin the other program's pid. This should probably :D be replaced by something smarter, depending on exactly what you are doing.

Also, in practice, the "while (!cancontinue);"-like loops should be replaced by something else :D, like semaphores. At least you should do a small sleep inside each loop. Also, I think you do not truly need shared memory to solve this problem, it should be doable using message-passing techniques.

Here is an example session, showed in parallel:

    # ./producer                                            # ./consumer
    Hi, I am the writer process and my pid is 11357         Hi, I am the reader process and my pid is 11358
    Please give me the pid of the other process: 11358      Please give me the pid of the other process: 11357
    Enter two integers: 2                                   Waiting for producer to signal me to do my work
    3
    Sending signal to consumer process                      Received signal
    Waiting for consumer to allow me to continue            Pretending to do a long calculation
                                        ... some times passes ...
                                                            The sum of the ints in the shared memory is 5
                                                            Signaling producer I'm done
    Enter two integers: 0                                   Waiting for producer to signal me to do my work
    0
    Sending signal to consumer process                      Received signal
    Waiting for consumer to allow me to continue            Pretending to do a long calculation
                                        ... some times passes ...
                                                            The sum of the ints in the shared memory is 0
                                                            Signaling producer I'm done
    Sum was 0, exiting...                                 

I hope this helps. (when you run the programs, make sure the file test_concur.c exists (it's used to establish the shared memory key (ftok function call)))

Exactly what he asked for. +1. I still think it is a mess, though.
dmckee
Nonetheless, a very informative and well-written answer. +1
A. Rex
Fantastic answer +1.
Eduardo
A: 

You may also want to look into boost message queues which will use shared memory under the hood, but hides all the hard parts. It offers both blocking and non-blocking functions (it sounds like you want blocking).

Evan Teran