How can I create an unknown number of child processes?

I need my code to be able to read n lines from stdin (each line will be a command from the Linux terminal), give it to a child process and execute it. Then when that child process ends, the program must read text again and execute another child process so that there are always n child processes running. I have tried in various ways but it always only reads one line even if I write several through stdin it executes only one child process and waits for more standard input. N must be between the values ​​1 and 8.

Standard input must be read in blocks of 16 bytes, and each command cannot be larger than 128 bytes.

This is the code I have:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>

#define BUF_SIZE 16

struct ordenes{
    int num;
    char *args[60];
};

typedef struct ordenes * ordenes;

void error(char * argv, int num_exit){
    fprintf(stderr,"Uso: %s [-p NUMPROC]\n",argv);
    fprintf(stderr,"Lee de la entrada estándar una sencuencia de líneas conteniendo órdenes para ser ejecutadas y lanza cada una de dichas órdenes en un proceso diferente.\n");
    fprintf(stderr,"-p NUMPROC     Número de procesos en ejecución de forma simultánea (1 <= NUMPROC <= 8)\n");
    exit(num_exit);
}

ordenes leer(){
    char * buf;
    if ((buf = (char *) malloc(BUF_SIZE * sizeof(char))) == NULL){
        perror("malloc()");
        exit(EXIT_FAILURE);
    }
    const char espacio[1] = " ";
    const char salto_linea[2] = "\n";
    char *token;
    ordenes orden = malloc(sizeof(orden));
    char *line = (char *) malloc(sizeof(char)*128);
    while((read(STDIN_FILENO,buf,BUF_SIZE))>0){
        strcpy(line,buf);
    }
    
    if(sizeof(line)>128){
        fprintf(stderr,"Error: Tamaño de línea mayor que 128.");
        exit(EXIT_FAILURE);
    }
    
    orden->num=0;
    token = strtok(line, espacio);
    orden->args[orden->num] = token;
    while(token != NULL){
        token = strtok(NULL, espacio);
        if(token!=NULL) {
            orden->num+=1;
            orden->args[orden->num] = token;
        }
    }
    orden->num+=1;
    orden->args[orden->num] = NULL;
    return orden;
}

int ejecutar_proceso(ordenes orden){
    pid_t pid;
    int status;
    switch(pid = fork()){
        case -1:
            perror("fork()");
            exit(EXIT_FAILURE);
            break;
        case 0:
            execvp(orden->args[0],orden->args);
            fprintf(stderr,"execvp() failed\n");
            exit(EXIT_FAILURE);
            break;
        default:
            if(waitpid(pid,&status,0) == -1){
                perror("wait()");
                free(orden);
                exit(EXIT_FAILURE);
            }
            free(orden);
            break;
    }
    return 1;
}

int main(int argc, char *argv[]){

    
    char *arg1;
    char *arg2;
    int opt;
    int num_procesos=1;
    int num=num_procesos;
    

    if(argc > 3){
        error(argv[0],EXIT_FAILURE);
    }

    while((opt = getopt(argc,argv,"p")) != -1){

        switch(opt){
            case 'p':
                if(argv[2]==NULL){
                    error(argv[0],EXIT_FAILURE);
                }
                num_procesos = atoi(argv[2]);
                if(num_procesos < 1 || num_procesos > 8){
                    fprintf(stderr,"Error: el número de procesos en ejecución tiene que estar entre 1 y 8.");
                    error(argv[0],EXIT_FAILURE);
                }
                break;
            default:
                num_procesos = 1;
                break;
        }

    }
    
    int i=0;
    while(i==0){
        if(num_procesos>0){
            ordenes orden = leer();
            num_procesos-=1;
            num_procesos += ejecutar_proceso(orden);
        }
    }

    
    return EXIT_SUCCESS;
}

The Makefile that should be able to compile it is the following:

    CFLAGS=-Wall -ggdb3 -Werror -Wno-unused -std=c11 CC=gcc TARGETS=$(patsubst %.c,%,$(wildcard *.c))

all: $(TARGETS)

clean:
        -rm -rf $(TARGETS)

.PHONY: clean all

And for example it should be able to pass tests like: echo -e "ls -l\nls -l" | ./mycode -p 1

Upvotes: 0

Views: 75

Answers (1)

Nate Eldredge
Nate Eldredge

Reputation: 57997

In ejecutar_proceso the parent always calls waitpid immediately after fork, so it will not proceed until the child it just created has terminated.

If you want to immediately go on and create more processes, then don't call waitpid yet. Create all n processes first. Then enter a loop where you call waitpid, and each time it returns, call fork() to create a new process to replace the one that just exited.

Upvotes: 1

Related Questions