【问题标题】:Parent process needs to wait for child to finnish setup父进程需要等待子进程完成设置
【发布时间】:2013-12-17 05:50:12
【问题描述】:

大家好,所以我的代码应该创建无限数量的进程,每个进程都运行自己的程序并通信抛出的管道..

这是以递归方式完成的,因此第一个父级创建一个管道,然后是一个子级。在此之后,子级执行相同的操作,创建一个 pipe2 和 child2 等......

问题是父进程不想等待所有子进程被创建以及当我尝试插入时

waitpid(childpid, NULL, 0);

它有点等待永远......所以要么我希望最后一个孩子发送某种 STOP WAIT 信号,要么我想要另一种方法来解决这个问题!

代码如下:

/* workForce.c
 * 
 * Program created by ----Secret-----
 * 
 * Input has to be the programs you wish the workForce to execute with following parameters seperated by '0's
 * 
 * example input:
 * printenv 0 grep L 0 sort
 * 
 * Will result in the command: printenv | grep L | sort | chosen PAGER
 * 
 * The program will pipe each unit of the workForce to the next unit untill it reaches the last unit where
 * it will printout the result to STDOUT.
 */
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define PIPE_READ_SIDE ( 0 )
#define PIPE_WRITE_SIDE ( 1 )

pid_t childpid; /* för child-processens PID vid fork() */

/*
 * The generateWorkforce method automaticly creates it's own workforce with one process per taskset
 * Each unit in the work force will execute the parameters provided
 * As long as it's not the end of the workforce the unit will pipe its output to the next unit
 */
void generateWorkforce(int taskpointer, int totaltasks, int taskset[], char* tasks[]){
/* Create pipe then fork */
int pipe_filedesc[ 2 ];
int return_value;
return_value = pipe( pipe_filedesc );
if( -1 == return_value ) {perror( "Cannot create pipe" ); exit( 1 );}

childpid = fork();

if( 0 == childpid )
{
    /* Redirecting STDIN to now read from pipe instead and removing the pipe */
    return_value = dup2( pipe_filedesc[ PIPE_READ_SIDE], STDIN_FILENO );
    if( -1 == return_value){perror( "Cannot dup" ); exit( 1 );}     
    return_value = close( pipe_filedesc[PIPE_READ_SIDE] );
    if( -1 == return_value ){perror( "Cannot close read end" ); exit( 1 );}
    return_value = close( pipe_filedesc[ PIPE_WRITE_SIDE ] );
    if( -1 == return_value ){perror( "Cannot close write end" ); exit( 1 );}

    if(taskpointer < totaltasks-1){
        generateWorkforce((taskpointer+taskset[taskpointer]), totaltasks, taskset, tasks);
    }
    else {
        kill(getppid, SIGINT);
        execlp(tasks[taskpointer], tasks[taskpointer], (char *) 0);

        /* exec only returns if an error occured */
        perror( "Cannot exec execute[pointer]" );
        exit( 1 );
    }
}
else
{
    if( -1 == childpid ){   perror( "Cannot fork()" );  exit( 1 );} 

    /* Redirecting STDOUT to write to pipe instead and thereafter removes the pipe */
    return_value = dup2( pipe_filedesc[ PIPE_WRITE_SIDE], STDOUT_FILENO );
    if( -1 == return_value){perror( "Cannot dup" ); exit( 1 );}
    return_value = close( pipe_filedesc[ PIPE_WRITE_SIDE ] );
    if( -1 == return_value ){perror( "Cannot close write end" ); exit( 1 );}
    return_value = close( pipe_filedesc[ PIPE_READ_SIDE ] );
    if( -1 == return_value ){perror( "Cannot close read end" ); exit( 1 );}

    waitpid(childpid, NULL, 0);

    /* Will define what task to execute and how many parameters it needs */
    if(taskset[taskpointer] > 1){
        char* execute[taskset[taskpointer]+1];
        int i;
        char* executeHead = tasks[taskpointer];
        execute[0] = tasks[taskpointer];
        for(i = 1;i<taskset[taskpointer];i++){
            execute[i] = tasks[taskpointer+i];
        }

        execute[taskset[taskpointer]] = (char *) 0;

        (void) execvp( executeHead, execute );
    }
    else{
        (void) execlp( tasks[taskpointer], tasks[taskpointer], (char *) 0 );
    }

    /* exec only returns if an error occured */
    perror( "Cannot exec tasks[taskpointer]" );
    exit( 1 );

}
}

int main( int argc, char * argv[] )
{
/* The following block will determine how many tasksets was in the input */
char * separator = "0";
int i;
int j;
int tasknr = 1;
for(i = 1, j = 1;i<(argc);i++)
{
    if(!(strcmp(argv[(i)], separator ))){
        j++;
    }
    else{
        tasknr++;
    }
}

char* execute[tasknr];
int taskset[j+1];

/* Will fill the taskset array and execute array */
if(getenv("PAGER")) execute[(tasknr-1)] = getenv("PAGER");
else execute[(tasknr-1)] = "less";
taskset[j] = 1;
taskset[0] = 0;
int k = 0;
for(i = 1, j = 0;i<(argc);i++)
{
    if(!(strcmp(argv[(i)], separator ))){
        j++;
        taskset[j] = 0;
    }
    else{
        execute[k] = argv[i];
        taskset[j]++;
        k++;
    }
}

generateWorkforce(0, tasknr, taskset, execute);

exit( 0 );
}

编辑:kill(getppid, SIGINT);试图停止等待!

【问题讨论】:

  • -1 != waitpid( pid, &status, WNOHANG )
  • 更改了我为此设置的 waitpid,但结果与根本不等待一样。
  • 所以 main(starter) 进程创建了一个子进程,而子进程创建了另一个子进程——就像一个进程列表,对吗?而且您希望主进程根本不等待,对吗?
  • 我希望它等到最后一个孩子完成,但之后继续执行
  • wait() 问题无关:我发现如果进程是在循环中创建的,则更容易理解,例如,请参阅Connecting n commands with pipes in a shell?。以及基于它的代码:pipeline-three-processes.c

标签: c process pipe wait


【解决方案1】:

正如我们在 cmets 中所说,您需要使用信号量并在两个进程之间实现同步。
信号量可以是计数的,也可以是二进制的。更多关于 POSIX 信号量的信息,here

正如@J.F.Sebastian 所说,您需要创建的称为流程链,并且可以通过循环轻松创建。
您已要求使用 POSIX 信号量,但请注意,POSIX IPC 并未像 SystemV 那样在所有系统中完全实现。

一些信息:
当一个进程被分叉时,它的内存被复制到新进程的内存段中。 如果您想使用带有 sem_init 的信号量(未命名的信号量),您将不得不使用 shm_open、mmap 和 shmget 来 将信号量放在共享内存区域,以便两个进程可以正确使用它。 您可以(并且应该)阅读所有相关信息 herehere

为了简化我的解决方案,我使用了带有 sem_open 的命名信号量。 我首先创建信号量并使用value == 0 对其进行初始化,然后使用循环,所有需要的进程都被分叉。

由 pid 标识的主进程被 sem_wait 调用阻塞。 现在最后一个子进程通过sem_post 调用通知主进程它已创建。

以下是实现所有这些的代码:

#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h> 
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/file.h>
#include <errno.h>


#define PROCS_NO 3
#define chneg(r,s) { if (r < 0) {perror(s); exit(EXIT_FAILURE);}}


int main(void) 
{
    //check argument count
    //check input, for the sake of simplycity I'll use a #defined value
    pid_t childpid = 0;
    int i;
    sem_t* sem;
    pid_t mainproc = getpid();

    sem = sem_open("notify", O_CREAT | O_EXCL, 0644, 0); 
    /* name of semaphore is "notify", semaphore is reached using this name */
    chneg(sem_unlink("notify"), "sem_unlink");
    /* unlink prevents the semaphore existing forever */
    /* if a crash occurs during the execution         */

    for (i = 0; i < PROCS_NO; i++)
    {
        sleep(1);
        printf("\nAbout to create child %d\n", i);
        childpid = fork();
        if (childpid < 0) {
            perror("Fork error"); return -1;
        }
        if (childpid == 0) {
            //child process, continue the loop
        }
        else 
            break; //parent process, exit the loop
    }
    if (getpid() == mainproc) {
        //main process must wait for the last process to be created
        chneg(sem_wait(sem), "sem_wait");
        printf("\nMain Process: Just woke up\n");
        /* cleanup semaphores */
        chneg(sem_destroy (sem), "sem_destroy");
        exit (0);
    } 
    else {
        //child processes
        if (i == PROCS_NO -1 )
        {
            printf("\nLast child, going to sleep for 3\"\n");
            sleep(3);
            printf("\nJust woke up, waking up, main process as well\n");
            chneg(sem_post(sem), "sem_post");
            int semval;
            chneg(sem_getvalue(sem, &semval), "getvalue");
            printf("\nExiting with sem == %d\n", semval);
        }
        exit (0);
    }
}

【讨论】:

  • 只是一个想法,这样的工作是否相同:我创建一个标志 (boolean=false) 并执行一个 while(boolean=false) 循环,然后在最后一个孩子中,我将此布尔值设置为真的..这只是一个想法现在我会去寻找信号灯谢谢:)
  • @David:那个循环里会有什么?注意,因为进程不共享相同的内存段。每个过程都有一个。如果您希望变量在所有进程的循环中更改某些内容,那么您必须将此 var 放在共享内存段中。但是不要那样做,因为你还需要一个互斥信号量,以在读取和写入布尔变量时保护竞争条件。最好使用信号量。如果您还有什么需要,尽管问!:) 另外,如果这个答案对您有帮助,您可以点击“打勾”图标接受我的回答,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-12-28
  • 2010-11-06
相关资源
最近更新 更多