繁体   English   中英

子进程与父进程之间的同步c

[英]Synchronization between childs and parent processes c

我试图实现这一点:

制作一个执行以下操作的C多进程程序:

进程P生成两个子进程P1和P2。 两个儿子P1和P2执行一个不确定的周期,在该周期中,每秒生成0到100之间的一个随机整数。在每次绘制时,孩子们交流由父P进程生成的数字,该数字将它们相加并打印在屏幕并将它们存储在一个文件中。 进程P1必须处理SIGINT中断信号。 特别是,在此信号到达时,P1必须显示警告消息“ P1 process busy!”。 当程序验证从子进程接收到的数字的总和取值为100时,该程序将由父P进程终止。

现在,我需要一些有关孩子与父母之间同步的帮助。 我试图使用信号量,但看起来似乎不可能。 我可以使用什么来同步它们? 信号? 怎么样?

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

}

我在这里报告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()一起使用sem_init() -父进程和分叉进程必须使用相同的信号共享内存段以访问进程间信号量)。

实际上,在您的代码sem1sem2 ,在主进程中初始化的代码不会传播到分支的进程:它们具有独立的内存区域和地址,并且不能共享。

编辑后,关于信号量,存在许多问题:

  • 在逻辑上最错误的是:您不能将一个进程的指针传递给另一进程:地址不共享。 每个进程都必须独立打开信号量,并将其与自己的处理程序一起使用。
  • while (i=0) ... ouch,尝试使用-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);
    }
  }  
}

您的问题确实有很多事情要做。

如答案@Sigismondo中所述,您正在将多线程与多进程编程混淆。 他们有不同的沟通方式。

为了简化起见,线程共享相同的内存,因此一个线程可以查看全局变量的值,例如信号量互斥量等:如果一个线程对其进行了修改,则另一个线程将受到影响。

fork()多处理中,将使用其自己的内存空间生成一个新进程。 fork()之后,变量值几乎相同(除了pidppid等),但是它们位于不同的内存空间中:如果只有一个进程执行了代码块,则对其进行修改不会影响变量(程序中的信号灯)。

在您的情况下:首先,如果子进程执行相同的操作(即生成随机数),为什么您必须使用不同的功能? 你不能做这样的事情:

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

处理信号

进程P1必须处理SIGINT中断信号。 特别是,在此信号到达时,P1必须显示警告消息“ P1 process busy!”。 当程序验证从子进程接收到的数字的总和取值为100时,该程序将由父P进程终止。

我认为这还不清楚。 父级应捕获SIGINT信号。 孩子们应该怎么办? 从您所说的看来,他们似乎不应该抓住这个信号。 在这种情况下,您必须看一下信号掩码:基本上,您必须在父级中阻塞信号,调用fork() ,然后放回原始掩码。 现在您应该更深入一些,但是像这样( 此处

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()之前或之后不应更改,因为该信号已被子级阻止:这取决于您是否希望用户在启动forks()之前退出程序:in第一种情况在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。

由于您的问题过于笼统,因此我尝试给出一些提示:由于我没有尝试过,所以我的代码中可能存在错误。 你应该学习自己的。 如果您将提供一个最小的工作示例,我将尽力提供帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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