Остановите и снова запустите процессы в Linux, используя C ++ - PullRequest
2 голосов
/ 08 января 2009

У меня есть два процесса и зона общей памяти, мой рабочий процесс такой. Процесс A записывает некоторые данные в разделяемую память, после чего он должен подождать и отправить сигнал другому процессу B, чтобы начать работу. Процесс B должен прочитать некоторые данные из общей памяти, сделать что-то, записать результат и отправить сигнал процессу A для продолжения, после этого процесс B должен ждать.

Может ли кто-нибудь предложить пример или место, где я могу найти, как я могу остановить процесс и как я могу снова запустить процесс ?. Я работаю в Linux и C ++.

У меня уже есть семафоры, но что мне не нравится, так это то, что один процесс останавливает несколько секунд, читая все время из общей памяти, пока он не обнаружит, что он может работать. Вот почему я думал только о том, чтобы послать сигнал в нужный момент

Обновление с решением

Я выбрал ответ stefan.ciobaca как любимый, потому что это полное решение, которое работает и у него очень хорошее объяснение. Но во всех остальных ответах есть и другие интересные варианты.

Ответы [ 7 ]

7 голосов
/ 08 января 2009

Вот подтверждение концепции того, как это можно сделать:

#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;
}

Вышеупомянутая программа на самом деле две программы, потребитель и производитель, в зависимости от того, как вы его скомпилируете.

Вы компилируете производителя, убедившись, что макрос PRODUCER_MODE определяется:

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

Потребитель компилируется без определения макроса PRODUCER_MODE:

# gcc -Wall -o consumer test_concur.c

Потребитель и производитель имеют общую глобальную память (8 байтов, на которые указывают данные); роль продюсера состоит в том, чтобы прочитать два 32-битных целых числа из стандартного ввода и записать их в общий объем памяти. Потребитель читает целые числа из общей памяти и вычисляет их сумму.

После записи данных в общую память, производитель подает сигнал потребителю (через SIGUSR1), чтобы он мог начать вычисления. После вычисление сделано, потребитель сигнализирует производителю (через SIGUSR1 еще раз), что это может продолжаться.

Оба процесса останавливаются, когда сумма равна 0.

В настоящее время каждая программа начинает с вывода pid и чтения из стандартный пид другой программы. Это должно быть вероятно: D быть заменено на что-то умнее, в зависимости от того, что именно вы делаете.

Кроме того, на практике петли типа while (! Cancontinue);) должны быть заменено на что-то еще: D, как семафоры. По крайней мере, вы должны сделать маленький сон внутри каждой петли. Кроме того, я думаю, что для решения этой проблемы вам на самом деле не нужна общая память, это можно сделать с помощью методов передачи сообщений.

Вот пример сеанса, показанного параллельно:

    # ./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...                                 

Надеюсь, это поможет. (при запуске программ убедитесь, что существует файл test_concur.c (он используется для установки ключа общей памяти (вызов функции ftok)))

4 голосов
/ 08 января 2009

Не совсем то, что вы просили, но могли бы вы использовать каналы (именованные или нет), чтобы повлиять на синхронизацию? Это накладывает бремя блокировки на ОС, которая уже знает, как это сделать.

Просто мысль.


Ответ на комментарий: Я имел в виду использование каналов, а не разделяемой памяти для увеличения объема данных и бесплатной синхронизации.

Например:

  1. Процесс A запускается, устанавливает двунаправленную трубу и разветвляется процесс B, используя popen (3).
  2. Сразу после развилки:

    • A выполняет некоторую работу и пишет в канал

    • B пытается прочитать канал, который будет блокироваться, пока процесс A не запишет ...

  3. Далее:

    • A пытается прочитать канал, который будет блокироваться, пока не будут доступны данные ...

    • B выполняет некоторую работу и пишет в канал

  4. переходите к шагу 2, пока не достигнете конечного условия.

Это не то, что вы просили. Нет общей памяти, нет сигналов, но это должно сработать ...

1 голос
/ 08 января 2009

Как насчет использования доменных сокетов Unix для IPC вместо общей памяти? Таким образом, каждый процесс может блокировать чтение из сокета, в то время как другой выполняет свою работу.

Редактировать: Это похоже на ответ dmckee , но предлагает больше контроля над блокировкой и IPC. Однако подход popen определенно проще реализовать.

0 голосов
/ 08 января 2009

Возможно, вы также захотите заглянуть в очереди повышенных сообщений , которые будут использовать общую память под капотом, но скрывать все жесткие части. Он предлагает как блокирующие, так и неблокирующие функции (звучит так, будто вы хотите заблокировать).

0 голосов
/ 08 января 2009

Я предлагаю использовать семафоры для синхронизации процессов.

Читая заголовок, я подумал, что SIGSTOP и SIGCONT могут быть возможностями, но это, вероятно, не очень хорошая идея; Вы хотите, чтобы они остановились, когда они окажутся в правильном (безопасном) месте, чтобы остановиться. Вот для чего семафоры.

Многие другие механизмы IPC также могут достигать аналогичных результатов, но семафоры являются механизмами межпроцессного взаимодействия (вы бы использовали взаимные исключения между разными потоками в одном процессе).

0 голосов
/ 08 января 2009

То, что вы ищете, называется блокировкой. Процесс B должен блокировать вызов из процесса A, а процесс A должен блокировать вызов из процесса B. Если процесс заблокирован (ожидает вызова от другого процесса), он бездействует в фоновом режиме и активируется только при получении сообщение.

Выберите , вероятно, функция, которую вы ищете.

0 голосов
/ 08 января 2009

Вам действительно нужно остановить процесс (выйти из него) и перезапустить его, или вы просто хотите, чтобы он дождался какого-либо события?

Если последнее, вы должны прочитать IPC и синхронизировать процесс (например, семафоры, мьютексы).

Если первое, посмотрите в исходном коде что-то вроде init в linux.

...