Синхронизация между дочерними и родительскими процессами c - PullRequest
0 голосов
/ 24 января 2019

Я пытаюсь реализовать это:

Создать многопроцессную программу на C, которая выполняет следующие действия:

Процесс P генерирует два дочерних процесса P1 и P2.Два сына P1 и P2 выполняют неопределенный цикл, в котором каждую секунду генерируют случайное целое число от 0 до 100. При каждом розыгрыше дети сообщают числа, сгенерированные родительским процессом P, который предусматривает их добавление, печатая их наэкран и хранение их в одном файле.Процесс P1 должен обрабатывать сигнал прерывания SIGINT.В частности, при поступлении этого сигнала P1 должен отобразить предупреждающее сообщение «Процесс P1 занят!».Программа завершается родительским процессом P, когда он проверяет, что сумма чисел, полученная от дочерних процессов, принимает значение 100.

Теперь мне нужна помощь ссинхронизация между детьми и родителями.Я пытаюсь использовать семафоры, но это выглядит невозможным.что я могу использовать для их синхронизации?Сигналы?как?

    #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#define READ 0
#define WRITE 1


void handler(int sig){

    printf("process 1 is busy\n");


}


void codeprocess1(int pd[], sem_t *sem1){

    int i = 0;
    int numgenerated;


    while( i = 0){
      signal(SIGUSR1, handler);
       numgenerated = rand()%101;
       close(pd[READ]);
       write(pd[WRITE], &numgenerated, sizeof(int));
       sleep(1);

       sem_wait(sem1);
    }
}

void codeprocess2(int pd[], sem_t *sem2){
    int i = 0;
    int numgenerated;



    while( i = 0){

     numgenerated = rand()%101;
     close(pd[READ]);

     write(pd[WRITE], &numgenerated, sizeof(int));

     sleep(1);

     sem_wait(sem2);
    }
}


int main(){


 pid_t pid1, pid2;
 int sum, numread1, numread2, pipe1[2], pipe2[2];

    sem_t *sem2 = sem_open("semaph2", O_CREAT | O_EXCL, 1, 0);
    sem_t *sem1 = sem_open("semaph1", O_CREAT | O_EXCL, 1, 0);


 if(pipe(pipe1)<0){
     exit(1);

 }

 if(pipe(pipe2)<0){
     exit(1);

 }


 pid1 = fork();
 switch(pid1){

     case -1:

       exit(1);

     case 0:

       codeprocess1(pipe1, sem1);
       break;

     default:

        pid2= fork();
        switch( pid2){

       case -1:
          exit(1);

       case 0:

          codeprocess2(pipe2, sem2);
          break;

         default:

           while(sum!=1000){
           close(pipe1[WRITE]);

           read(pipe1[READ], &numread1, sizeof(int));
            close(pipe2[WRITE]);
           read(pipe2[READ], &numread2, sizeof(int));
           sum = sum + numread1 + numread2;
           printf("%d\n", sum);
           sem_post(sem1);
           sem_post(sem2);
       }

          kill(0, SIGKILL);
     }
   }

}

Ответы [ 2 ]

0 голосов
/ 24 января 2019

В вашем вопросе действительно много всего происходит.

Как написано в ответе @Sigismondo, вы путаете многопоточность с многопроцессорным программированием. У них разные способы связи.

Для упрощения потоки совместно используют одну и ту же память, поэтому поток может видеть, например, значения глобальных переменных, таких как мьютекс семафоров и т. Д.: Если поток изменяет его, это повлияет на другой поток.

В многопроцессорном режиме, когда вы fork(), новый процесс генерируется с собственным пространством памяти. Сразу после fork() значения переменных почти одинаковы (кроме pid, ppid и т. Д.), Но они находятся в другом пространстве памяти: если у вас есть блок кода, выполняемый только одним процессом, его изменение не будет воздействовать на переменные (семафоры в вашей программе) другого процесса.

В вашем случае: во-первых, если дочерние процессы выполняют одни и те же вещи (т. Е. Генерируют случайное число), зачем вам разные функции? Разве вы не можете сделать что-то вроде:

#include<stdlib.h>
int generateRand()
{
     n = rand() % 100 + 1; //should be random in [1, 100]
}

СИГНАЛЫ ОБРАБОТКИ

Процесс P1 должен обрабатывать сигнал прерывания SIGINT. В частности, на При поступлении этого сигнала P1 должно отображаться предупреждающее сообщение «P1 Процесс занят! ". Программа завершается родительским процессом P, когда он проверяет, что сумма чисел, которые он получил от дочерний процесс, принимает значение 100.

Это действительно неясно, по моему мнению. Родитель должен поймать сигнал SIGINT. Что должны делать дети? Из того, что вы говорите, кажется, что они не должны поймать этот сигнал. В этом случае вы должны взглянуть на маски сигналов: в основном вы должны заблокировать сигнал в родительском элементе, вызвать fork() s и затем вернуть исходную маску. Теперь вы должны пойти глубже, но что-то вроде этого ( здесь )

sigset_t *parent_mask, *child_mask
//get the current mask 
if (int res =  sigprocmask (0, NULL, child_mask)<0)
    printf("some error\n");
//make the mask block the signal
if (int res =  sigaddset(child_mask, SIGINT)<0)
    printf("some error in sigaddset \n");
// block the signal with the new mask
if (int res =  sigprocmask (SIG_SETMASK, child_mask, parent_mask)<0)
    printf("some error\n");
//do your forks: children will inherit the current mask and will not catch SIGINT
...
fork()
...
fork()
....
//set back the original mask so the parent catches SIGINT
if (int res =  sigprocmask (SIG_SETMASK, parent_mask, NULL)<0)
    printf("some error\n");

Это мой ответ , хотя для многопоточности должно быть немного понятнее.

ОБРАБОТКА СИГНАЛА

Почему вы регистрируете обработчик сигнала в codeprocess1(int pd[])? Я совсем не понимаю. И почему SIGUSR1?

Вы должны сделать это в родительском (до или после fork() s не должно меняться, так как сигнал заблокирован для детей: это зависит от того, хотите ли вы, чтобы пользователь вышел из программы перед запуском forks() или нет: в первом случае зарегистрируйте обработчик сигнала после fork(), в противном случае поместите его в начало main(). В обоих случаях вы должны сделать:

signal(SIGINT, handler); 

Теперь ядро ​​вашей программы: для сообщения вашей программы вы можете использовать pipe() блокирующим образом вместе с файловыми дескрипторами: отметьте здесь .

Вам нужны два файловых дескриптора (по одному на дочерний процесс и закрытие конца (чтение / запись), не используемого процессом). Рассмотрим один дочерний процесс:

int p = fork();
int fd1[2]; //file descriptor for child1
int fd2[2]; //file descriptor for child2

if (p>0)//parent
{
    close(fd1[1]);//close writing end
    int n;
    read(fd1[0], &n, sizeof(n));
    //you might to call the other fork here and redo the same stuff
    int p2 = fork();
    if (p2>0)
    {
         close(fd2[1]);//close writing end
         int n2;
         read(fd2[0], &n2, sizeof(n2));
         sum = n2+n1
         if (sum==100 && exit = 1)
         {
             kill(p, SIGKILL);
             kill(p2, SIGKILL);
         }
    }
}
else if(p==0)//child
{
    close(fd1[0]);//close read end
    int rand_n = generateRand();//or whaterver the name
    wrote(fd1[1], &rand_n, sizeof(rand_n));


}

Условие выхода зависит как от значения суммы (100), так и от того, что были нажаты CTRL + C. Первое очевидно в коде выше. Для последнего вы можете объявить глобальную переменную (я использовал exit), что если 0 CTRL + C не была нажата, если 1 она была нажата. Это значение проверяется в условии выхода из кода выше. Ваш обработчик должен будет написать эту переменную:

//global variable here
int exit = 0;

void handler(int signo)
{
    print("Parent busy doing stuff\n");
    exit =1;
}

Заметьте, что одна вещь exit написана родителем, поскольку она написана ТОЛЬКО в обработчике, который вызывается только родителем, и читается в части кода, выполняемой только родителем: дети читают ее значение всегда будет 0 для них.

Поскольку ваш вопрос слишком общий, я попытался дать некоторые подсказки: в моем коде могут быть ошибки, поскольку я не пробовал. Вы должны учиться самостоятельно. Если вы приведете минимальный рабочий пример, я постараюсь помочь.

0 голосов
/ 24 января 2019

Я сообщаю здесь соответствующую часть справочной страницы sem_overview(7):

   POSIX  semaphores come in two forms: named semaphores and unnamed sema‐
   phores.

   Named semaphores
          A named semaphore is identified by a name of the form /somename;
          that  is,  a  null-terminated  string of up to NAME_MAX-4 (i.e.,
          251) characters consisting of an initial slash, followed by  one
          or  more  characters,  none of which are slashes.  Two processes
          can operate on the same named semaphore by passing the same name
          to sem_open(3).

          The  sem_open(3) function creates a new named semaphore or opens
          an existing named  semaphore.   After  the  semaphore  has  been
          opened, it can be operated on using sem_post(3) and sem_wait(3).
          When a process has finished using  the  semaphore,  it  can  use
          sem_close(3)  to  close  the semaphore.  When all processes have
          finished using the semaphore, it can be removed from the  system
          using sem_unlink(3).

   Unnamed semaphores (memory-based semaphores)
          An  unnamed  semaphore  does not have a name.  Instead the sema‐
          phore is placed in a region of memory  that  is  shared  between
          multiple  threads  (a  thread-shared  semaphore) or processes (a
          process-shared semaphore).  A thread-shared semaphore is  placed
          in  an  area  of memory shared between the threads of a process,
          for example, a global variable.  A process-shared semaphore must
          be  placed  in  a  shared memory region (e.g., a System V shared
          memory segment created using shmget(2), or a POSIX shared memory
          object built created using shm_open(3)).

          Before  being  used,  an  unnamed  semaphore must be initialized
          using sem_init(3).  It can then be operated on using sem_post(3)
          and  sem_wait(3).  When the semaphore is no longer required, and
          before the memory in which it is  located  is  deallocated,  the
          semaphore should be destroyed using sem_destroy(3).

Вы пытаетесь использовать безымянные семафоры в стандартной памяти. Но они предназначены только для синхронизации потоков, а не процессов.

Я предлагаю использовать либо именованные семафоры (что должно быть проще), либо неназванные семафоры, поддерживаемые разделяемой памятью (получите его с помощью shmget() или shm_open(), затем используйте его с sem_init() - родительский и разветвленный процессы должны используйте тот же сегмент общей памяти, чтобы иметь доступ к семафору внутри процесса).

Фактически, в вашем коде sem1 и sem2, инициализированные в основном процессе, не будут распространяться на разветвленные процессы: они имеют независимые области памяти и адреса и не могут использоваться совместно.

После редактирования относительно семафоров возникает много проблем:

  • самое логическое неверное: вы не можете передать указатель одного процесса другому процессу: адреса не являются общими. Каждый процесс должен независимо открывать семафор и использовать его со своим собственным обработчиком.
  • while (i=0) ... ой, попробуйте скомпилировать с -Wall.
  • Вы не проверяли код возврата sem_open(), сбой при ошибке errno = 13 (EACCESS)
  • Вы не правильно установили разрешение семафора ... это (вроде) файл. Обратите внимание, что как только вы создадите его с неправильными разрешениями, он останется там, и вы не сможете создать его снова с тем же именем (пока вы не перезагрузите систему). Вы можете увидеть их с помощью: ls -l /dev/shm, и в конечном итоге просто удалить их с помощью rm.
  • Вы запрашивали O_EXCL, то есть монопольный доступ к одному процессу, это не то, что вам нужно. См man 2 open.
  • имя семафора должно начинаться с /, см. man sem_overview

Вот пересмотренный код, некоторые комментарии в строке:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#include <errno.h>

#define READ 0
#define WRITE 1

#define SEM1_NAME "/semaph_1a"
#define SEM2_NAME "/semaph_2a"


void handler(int sig) {
  printf("process 1 is busy\n");
}

void codeprocess1(int pd[]) {
  int i = 0;
  int numgenerated;

  // each process must open the handle to the same named semaphore.
  // they cannot share a local memory address.
  sem_t *my_sem = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
  if (my_sem==SEM_FAILED) {
    printf("semaphore creation failed, errno=%d\n", errno);
    exit(1);
  }

  // the seed for the two children must be different or they will be generating the same
  // sequence of random numbers. 
  srand(3333);

  while(i == 0) {
    signal(SIGUSR1, handler);
    numgenerated = rand()%101;
    // close(pd[READ]);
    write(pd[WRITE], &numgenerated, sizeof(int));
    sleep(1);

    sem_wait(my_sem);
  }
}

void codeprocess2(int pd[]){
  int i = 0;
  int numgenerated;

  sem_t *my_sem = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
  if (my_sem==SEM_FAILED) {
    printf("semaphore creation failed, errno=%d\n", errno);
    exit(1);
  }

  srand(1111);

  while(i == 0) {
    numgenerated = rand()%101;
    // close(pd[READ]);
    write(pd[WRITE], &numgenerated, sizeof(int));
    sleep(1);
    sem_wait(my_sem);
  }
}


int main(){
  pid_t pid1, pid2;
  int sum, numread1, numread2, pipe1[2], pipe2[2];


  // O_EXCL removed
  // the mode flag must be set to 0777 for example, not "1".
  // return value check added
  sem_t *sem1 = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
  if (sem1==SEM_FAILED) {
    printf("semaphore sem1 creation failed, errno=%d\n", errno);
    exit(1);
  }

  sem_t *sem2 = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
  if (sem2==SEM_FAILED) {
    printf("semaphore sem2 creation failed, errno=%d\n", errno);
    exit(1);
  }

  if (pipe(pipe1) < 0 ) {
    exit(1);
  }

  if (pipe(pipe2) < 0) {
    exit(1); 
  }

  pid1 = fork();
  switch(pid1){

  case -1:
    exit(1);

  case 0:
    codeprocess1(pipe1);
    break;

  default:

    pid2= fork();
    switch( pid2) {      
    case -1:
      exit(1);
    case 0:      
      codeprocess2(pipe2);
      break;      
    default:      
      // 100, not 1000
      while (sum != 100) {
    // all the "close()" calls  are commented out
    // close(pipe1[WRITE]);
    read(pipe1[READ], &numread1, sizeof(int));
    // close(pipe2[WRITE]);
    read(pipe2[READ], &numread2, sizeof(int));
    // sum must not be incremented
    sum = numread1 + numread2;
    printf("%d\n", sum);
    sem_post(sem1);
    sem_post(sem2);
      }

      kill(0, SIGKILL);
    }
  }  
}
...