【问题标题】:C++ Managing ProcessesC++ 管理进程
【发布时间】:2012-03-18 06:35:09
【问题描述】:

我有一个创建 10 个子进程的进程,它们都共享一个内存段。 10个子进程可以同时运行,但是我想让父亲在这段时间睡觉。当孩子们完成他们去睡觉。当所有的孩子都完成后,父亲进行一些计算并唤醒孩子们再玩一轮(他再次入睡,直到所有孩子都完成了他们的计算)。我想我可以在一个以 check_children_finished() 为条件的 while 循环中暂停()。孩子们知道通过 kill() 向父母发出信号,他们有他的 pid(作为参数发送给他们),实际上父亲也有孩子的 pid(存储在共享内存中)这是一些代码 -


父代码:


#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>

#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <string>
#include <sstream>
#include <iostream>

using namespace std;

static void sig_usr(int);
static volatile sig_atomic_t *sharedArray;
static int segmentId; 
int number_of_children = 10;

bool check_children_finished(){
  for (int i=1; i<number_of_children; i++)
    if (sharedArray[i*2] == -1)
      return false;
  return true;
}

void sig_usr (int signo)
{
  /*
  // This is signals handler (when a signal fires this is what runs)
  // we expect SIGUSR1 from the child process
  if(signo == SIGUSR1)
    cout << "(parent: " << (int)getpid() << ") --- caught SIGUSR1" << endl;
  if (signo == SIGUSR2){
    cout << "(parent: " << (int)getpid() << ") --- caught SIGUSR2" << endl;
  }else
    perror("unexpected signal fired");
  //  return;
  */
}


int main()
{

  // size of the shared memory array
  int arrSize = 100;
  const int shareSize = sizeof(sig_atomic_t) * (arrSize);
  /* Allocate shared memory segment */  
  segmentId = shmget(IPC_PRIVATE, shareSize, S_IRUSR | S_IWUSR); 
  sharedArray = (sig_atomic_t *) shmat(segmentId, NULL, 0);

  // feel shared memory with -1
  for (int i=0; i<arrSize; i++)
    sharedArray[i] = -1;

  // binding SIGUSR1 & SIGUSR2 to sig_usr method
  // we need to override SIGUSR2 because of group signaling
  signal(SIGUSR1, sig_usr);
  //  signal(SIGUSR2,sig_usr);

  fprintf(stderr, "\n (parnet) myPid=%d ; segId=%d\n",(int)getpid(), segmentId);
  sharedArray[0] = 123;
  int kids = 0; // this is the number of child processes
  // we send to the child (as shell parameters) the parent pid, shared segment address , and index to the shared memory(this is where he will write his pid and in index+1 the heuristic value)
  char* kidsParams[5];

  // takes care of param[0]=command to run
  string exec_line = "./child";
  kidsParams[0] = new char[exec_line.size()+1];
  memcpy(kidsParams[0], exec_line.c_str(), exec_line.size());

  // takes care of param[1]=parent pid
  kidsParams[1] = new char[100]; // = malloc(100*sizeof(char));
  sprintf( kidsParams[1],"%d",(int)getpid());

  // takes care of param[2]=shared mem segment address
  kidsParams[2] = new char[100];
  sprintf( kidsParams[2],"%d",segmentId);

  // takes care of param[3]=the child private index in shared mem
  kidsParams[3] = new char[100];

  kidsParams[4] = NULL;  // needed as end of array
  int index = 0;
  for(; kids<number_of_children; kids++) {
    sprintf( kidsParams[3],"%d",index);
    index+=2;
    pid_t childpid = fork();
    if(childpid==0){
      execv(kidsParams[0],kidsParams);
    }
   }
  cout << "(parent) --- just finished creating " << number_of_children << " kids" << endl;
  cout << "(parent) entering to while {...} pause" << endl;
  for (int i=0; i<number_of_children; i++)
    cout << "[" << i << "] = " << sharedArray[i];
  cout << endl;
  // going to sleep --- here I want while loop with conditioning that all children finished
  while ( ! check_children_finished() ) {
    cout << "(parent) now will signal the group" << endl;
    // killpg sends signal to the group (all the children). note that the group has the same pid as the father
    killpg(getpid(),SIGUSR2);
    cout << "(parent) just finished signaling the group" << endl;
    pause();
    for (int i=0; i<number_of_children; i++)
       cout << "[" << i << "] = " << sharedArray[i];
    cout << endl;
  }
  cout << "(parent) exited the while{...} paused" << endl;


  // removes shared memory
  //  shmdt (sharedArray);  
  //  shmctl (segmentId, IPC_RMID, NULL);  
  // note that all children must also shmctl (...IPC_RMID...);

}

这是一个子代码:


(same includes...)
using namespace std;

// declare the function proptotype (needed in signal function)
static void sig_usr(int);

// handles the signal (what happens when the signal fires) --- here I want to solve the search problem
void sig_usr (int signo)
{
  /*
  if(signo == SIGUSR1){
    cout << "(child: " << (int)getpid() << ") --- caught SIGUSR1" << endl;
  }else if(signo == SIGUSR2){
    cout << "(child: " << (int)getpid() << ") --- caught SIGUSR2" << endl;
  }else
    perror("eerrrr");
  //  return;
  */

}

int main(int argc, char** argv){
  // binding the signal to the handler
  signal(SIGUSR2,sig_usr);
  int segmentId;  
  volatile sig_atomic_t *sharedArray ;
  int myIndex;
  int myData =  5; 
  int parentPid;
  // read parameters
  parentPid = atoi(argv[1]);
  segmentId = atoi(argv[2]);
  myIndex = atoi(argv[3]);

  // declare a pointer to the shared memory
  sharedArray = (sig_atomic_t *) shmat(segmentId, NULL, 0);
  sharedArray[myIndex] =(int)getpid();
  sharedArray[myIndex+1] = myData;
  //  fprintf(stderr, "My Group Pid(child): %d\n",(int)getpgrp());
  cout << "(child: " << (int)getpid() << ") --- going to sleep" << endl;
  pause();
  cout << "(child: " << (int)getpid() << ") --- I woke up" << endl;
  //calc data

  //fprintf(stderr, "My Pid(child): %d\n",(int)getpid());
  //fprintf(stderr, "I got %d (child)\n",sharedArray[0] );


  // this signals the father
  kill(parentPid,SIGUSR1);
  cout << "fired SIGUSR1"<< endl;
}

这是一个典型的输出:


 (parnet) myPid=3104 ; segId=22872080
(parent) --- just finished creating 10 kids
(parent) entering to while {...} pause
[0] = 123[1] = -1[2] = -1[3] = -1[4] = -1[5] = -1[6] = -1[7] = -1[8] = -1[9] = -1
(parent) now will signal the group
User defined signal 2

有时我会得到类似的东西:


 (parnet) myPid=3126 ; segId=22937618
(child: 3129) --- going to sleep
(child: 3127) --- going to sleep
(parent) --- just finished creating 10 kids
(parent) entering to while {...} pause
[0] = 3127[1] = 5[2] = 3128[3] = 5[4] = 3129[5] = 5[6] = -1[7] = -1[8] = -1[9] = -1
(parent) now will signal the group
User defined signal 2
(child: 3127) --- I woke up
fired SIGUSR1
(child: 3128) --- going to sleep
(child: 3132) --- going to sleep
(child: 3129) --- I woke up
fired SIGUSR1

谁能提出解决方案? 谢谢! -- 里隆

【问题讨论】:

    标签: c++ linux process signals shared-memory


    【解决方案1】:

    我建议在监控进程和其他工作进程之间使用管道,并使用简单的文本协议进行控制通信。

    (因为管道上的文本协议比信号更可靠 - 可以“丢失”或“合并”)

    所以,在分叉工人之前,我会调用(例如 10 次,或者如果你想要两种方式,或者可能 2*10 次)pipe(2) 来创建控制通信。然后你可以使用ppoll(2)(或者只是poll)来复用管道。

    但是您是否考虑过使用现有框架,例如Open-MPI(一个实现 MPI = "消息传递接口")。这并不意味着放弃使用共享内存段(只需使用 MPI 进行控制和同步问题)。或者也许使用OpenMP 或只是p-threads

    MPI 非常适用于高性能数值计算,许多超级计算机都有它(使用专用硬件实现非常快速的消息传递)。

    (当然,超级计算机是集群,因此您实际上并没有在数千个内核之间共享内存;您可以通过在代码中仅使用 MPI 来利用它们...)支持>

    (您也可以通过 OpenCL 使用 GPGPU 进行调查)

    【讨论】:

    • 您好 Basile,感谢您的快速回复!我一定会看看 MPI。关于管道---这是一个阻塞命令吗?通过管道与共享内存进行通信之间是否存在显着的性能差异?
    • pipe 是系统调用,而不是命令。而且它运行得很快,你应该在before fork 和早期初始化时使用它。我建议将它用于“控制”通信,像以前一样保持共享内存(用于公共共享数据)。然后使用poll进行多路复用,不阻塞。
    【解决方案2】:

    同步进程的常用方法是使用信号量。

    SysV 使用 semctl()、semop() 和 semget() 提供了更复杂的信号量操作。 POSIX 信号量(更简单)使用 sem_open()、sem_close()、sem_post() 和 sem_wait()。

    无论您使用哪种方法,在启动任何子进程之前在主进程中创建信号量,并在退出主进程时在所有子进程都被收割后删除信号量。

    每个孩子都应该在信号量上执行一个 sem_open()(不带 O_CREAT),然后在一个 sem_wait() 处执行阻塞,前提是您已正确初始化信号量。当 sem_wait() 返回时,执行触及共享内存的代码,然后调用 sem_post()。这应该为您的共享内存段提供独占共享访问权限。

    使用信号似乎是个坏主意。我当然从未听说过这样做。

    根据我自己的经验,我有一个单独的进程来创建所有 SysV 信号量并将信号量 ID 复制到共享内存段中的合适位置。这就是所有过程所做的,然后它退出了。信号量是持久的——然后我会从命令行启动其他进程,这些进程会打开信号量(没有 O_CREAT)并执行 sem_wait()。所以我没有使用 fork/exec 来启动子进程。从命令行分离进程似乎更简单。

    我不确定进程子进程是否会像继承文件描述符那样继承信号量 ID

    【讨论】:

    • 感谢您的帮助!以下是修订版(根据您的建议)。
    【解决方案3】:

    确实,使用信号量使事情变得更简单一些,并且最终可以正常工作。请注意,您必须使用 -pthread 标志进行编译(例如 g++ -Wall -pthread chikd.c -o parent)。 不过我确实有一个小问题,有人知道我是否可以做一些比 sleep(1) 更好的事情吗? (父代码,第 109 行)。

    父代码:

    #include <csignal>
    #include <cstdio>
    #include <cstdlib>
    #include <sys/types.h>
    #include <sys/ipc.h> 
    #include <sys/shm.h> 
    #include <sys/stat.h>
    #include <unistd.h>
    #include <cstring>
    #include <string>
    #include <sstream>
    #include <iostream>
    #include <semaphore.h>
    #include <fcntl.h>
    
    using namespace std;
    
    char SEM_NAME[] = "lir";
    
    static void sig_usr(int);
    static sig_atomic_t *sharedArray; // static volatile sig_atomic_t *sharedArray;
    static int segmentId; 
    
    int number_of_children = 10;
    int child_init_ctrl = 0;
    int child_finish_ctrl = 1;
    int number_of_children_ctrl = 2;
    int indexes_start_ctrl = 3;
    // each child has its own : PID,DATA,ADDRESS_TO_HIS_INIT_STATE
    int data_start = indexes_start_ctrl + number_of_children*3;
    
    int getMyStartIndex(){
      return -1;
    }
    
    void sig_usr (int signo) {}
    
    int main() {
    
      // create&init *new* semaphore
      sem_t *mutex;
      mutex = sem_open (SEM_NAME,O_CREAT,0644,1);
      if(mutex == SEM_FAILED) {
        perror("unable to create semaphore");
        sem_unlink(SEM_NAME);
        exit(-1);
      }
    
      // size of the shared memory array
      int arrSize = 100;
      const int shareSize = sizeof(sig_atomic_t) * (arrSize);
      /* Allocate shared memory segment */  
      segmentId = shmget(IPC_PRIVATE, shareSize, S_IRUSR | S_IWUSR); 
      sharedArray = (sig_atomic_t *) shmat(segmentId, NULL, 0);
    
      // fill shared memory with -1
      for (int i=0; i<arrSize; i++)
        sharedArray[i] = -1;
    
      // binding SIGUSR1 & SIGUSR2 to sig_usr method
      // we need to override SIGUSR2 because of group signaling
      signal(SIGUSR1, sig_usr);
      signal(SIGUSR2,sig_usr);
    
      sem_wait(mutex);
      sharedArray[child_init_ctrl] = 0;
      sharedArray[child_finish_ctrl] = 0;
      sharedArray[number_of_children_ctrl] = number_of_children;
      sem_post(mutex);
    
      // we send to the child (as shell parameters) the parent pid, shared segment address , and index to the shared memory(this is where he will write his pid and in index+1 the     heuristic value)
      char* kidsParams[6];
    
      // takes care of param[0]=command to run
      string exec_line = "./child";
      kidsParams[0] = new char[exec_line.size()+1];
      memcpy(kidsParams[0], exec_line.c_str(), exec_line.size());
    
      // takes care of param[1]=parent pid
      kidsParams[1] = new char[100]; // = malloc(100*sizeof(char));
      sprintf( kidsParams[1],"%d",(int)getpid());
    
      // takes care of param[2]=shared mem segment address
      kidsParams[2] = new char[100];
      sprintf( kidsParams[2],"%d",segmentId);
    
      // takes care of param[3]=the child private index in shared mem
      kidsParams[3] = new char[100];
    
      // takes care of param[4]=the child's first index of start state
      kidsParams[4] = new char[100]; // = malloc(100*sizeof(char));
      sprintf( kidsParams[4],"%d",getMyStartIndex());
    
      kidsParams[5] = NULL;  // needed as end of array
    
      // creates the child processes (and update child private index)
      int index = 3;
      for(int kids=0; kids<number_of_children; kids++) {
        sprintf( kidsParams[3],"%d",index);
        index+=3;
        pid_t childpid = fork();
        if(childpid==0){
          execv(kidsParams[0],kidsParams);
        }
      }
    
      while ( sharedArray[child_init_ctrl] != number_of_children ) {
        // waiting on the CPU !!! or going to sleep. is there a better solution?
        sleep(1);
      }
    
      // killpg sends signal to the group (all the children). note that the group has the same     pid as the father
      killpg (getpid(),SIGUSR2);
    
      // going to sleep --- here I want while loop with conditioning that all children finished
      bool all_child_finished_flag = false;
      while ( all_child_finished_flag == false ) {
        sem_wait(mutex);
        if (sharedArray[child_finish_ctrl] == number_of_children)
          all_child_finished_flag = true;
        sem_post(mutex);
        if (all_child_finished_flag == false)
          pause();
      }   
    
    
      // removes shared memory and delete semaphore
      shmdt (sharedArray);  
      shmctl (segmentId, IPC_RMID, NULL);  
      // note that all children must also shmctl (...IPC_RMID...);
      // removes the mutex
      sem_close(mutex);
      sem_unlink(SEM_NAME);
    
    }
    

    子代码:

    // declare the function proptotype (needed in signal function)
    static void sig_usr(int);
    int child_init_ctrl = 0;
    int child_finish_ctrl = 1;
    int number_of_children_ctrl = 2;
    int number_of_children=0;
    
    // handles the signal (what happens when the signal fires)
    void sig_usr (int signo) {}
    
    char SEM_NAME[] = "lir";
    
    int main(int argc, char** argv) {
        (same includes...)
    
          // create&init *existing* semaphore
          sem_t *mutex;
          mutex = sem_open (SEM_NAME, 0, 0644, 0);
          if(mutex == SEM_FAILED) {
            perror("reader:unable to execute semaphore");
            sem_close(mutex);
            exit(-1);
          }
    
          // binding the signal to the handler
          signal(SIGUSR2,sig_usr);
    
          volatile sig_atomic_t *sharedArray ;
          int myData=0;
          // read parameters
          int parentPid = atoi(argv[1]);
          int segmentId = atoi(argv[2]);
          int myIndex = atoi(argv[3]);
          int myStartState = atoi(argv[4]);
    
          // declare a pointer to the shared memory
          sharedArray = (sig_atomic_t *) shmat(segmentId, NULL, 0);
          sharedArray[myIndex] =(int)getpid();
          sharedArray[myIndex+1] = myData;
          number_of_children = sharedArray[number_of_children_ctrl];
    
          sem_wait(mutex);
          sharedArray[child_init_ctrl]++;
          sem_post(mutex);
    
          pause();
    
          // do some "calculations"
          sharedArray[myIndex+1] = myIndex;
          for (int i=0; i<10; i++)
            sharedArray[myIndex+1] += i;
    
          // this signals the father
          bool should_i_fire = false;
          sem_wait(mutex);
          sharedArray[child_finish_ctrl]++;
          if (sharedArray[child_finish_ctrl] == number_of_children)
            should_i_fire = true;
          sem_post(mutex);
          if (should_i_fire == true)
            kill(parentPid,SIGUSR1);
        }
    

    最好, -- 里隆

    【讨论】:

      猜你喜欢
      • 2010-11-07
      • 1970-01-01
      • 2022-01-21
      • 2015-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多