0

First of all, yes this is a laboratory activity in my class, but I have already submitted and defended this exercise. What I would like to know is if there is another way, a more efficient way to write this code.

What we were tasked to do was to write a code that creates a process, which creates a child, which in turn creates another child and finally creates a another child.

*edit: I separated and numbered the requirements for better readability :)

  1. The last child would show the current processes running in the system.

  2. Its parent would then ask for a word then create a file using the input by the user.

  3. Its parent would then ask for a word or a phrase, then find it within the libraries in your machine (Let's say I typed hi, it should find and list the files containing hi & its directory. The position of the word hi should not matter)

  4. Lastly, the main parent would just print its parent id.

Here is my complete code for that matter:

int main(void){ 
    char fileName[30];
    char phrase[30];
    int pid = fork();
    int fd[2];
    pipe(fd);
    if(pid==0){
        printf ("CHILD1: I am the 1st child\n");
        printf ("CHILD1: ID is %d \n", getpid());
        printf ("CHILD1: Parent ID is %d \n", getppid());
        int pid2 = fork();
        if(pid2==0){
            printf ("\t CHILD2: I am the 2nd child\n");
            printf ("\t CHILD2: ID is %d \n", getpid());
            printf ("\t CHILD2: Parent ID is %d \n", getppid());
            int pid3 = fork();      
                if(pid3==0){
                    printf ("\t\t CHILD3: I am the 3rd child\n");
                    printf ("\t\t CHILD3: ID is %d \n", getpid());
                    printf ("\t\t CHILD3: Parent ID is %d \n", getppid());
                    execlp ("/usr/bin/top", "top", NULL);
                }else if(pid3==-1){
                    printf ("ID is %d", getpid());
                    printf ("error");
                    exit(1);
                }else{
                    wait(NULL);
                    printf ("\t CHILD2: Enter a filename: ");
                    scanf ("%s", fileName);
                    printf ("\t CHILD2: %s was succcessfully created!\n", fileName);
                    execlp ("/bin/touch", "touch", fileName, NULL); 
                }
        }else if(pid2==-1){
            printf ("ID is %d", getpid());
            printf ("error");
            exit(1);
        }else{
            wait(NULL);
            int pid4 = fork();
                if(pid4 > 0) {
                    printf ("CHILD1: Enter a pharse: ");
                    scanf ("%s", phrase);
                    close(fd[1]);
                    close(STDIN_FILENO);
                    dup2(fd[0],STDIN_FILENO);
                    execlp ("/bin/grep", "grep", phrase, NULL);
                }else if (pid4 == 0) {
                    close(fd[0]);
                    close(STDOUT_FILENO);
                    dup2(fd[1],STDOUT_FILENO);
                    execlp ("/usr/bin/find", "find", NULL);
                }else {
                    printf ("error");
                }
        }       
    }else if(pid==-1){
        printf ("ID is %d", getpid());
        printf ("error");
        exit(1);
    }else{
        wait(NULL);
        printf ("PARENT: I am the parent\n");
        printf ("PARENT: ID is %d \n", getpid());
    }
}
  • What do you mean by efficient? Fewer lines of code? IMO the code seems perfectly reasonable given the requirements. You could push the error condition code and "CHILD" printfs into separate functions. – cronburg Mar 10 '14 at 02:58

1 Answers1

0

Your code seems reasonably efficient in terms of run-time and number of lines, but a single main function, deeply nested, does few favors to the humans who would try to read and understand what you've done.

Consider another idiom: one that's more modular, where the ancestry of processes is (IMHO) easier to follow by a reviewer. (I also invoke ps instead of top so that there's no need for keyboard interaction in that step.)

Is my approach more "efficient"? Arguably no, although I prefer the more direct coding of this child-per-function approach. The chaining of parent and child process is artificial, but of course so is your assignment.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

/*
 * run the passed function in a child process, and return
 * from this function only if the child process runs and
 * exits with status of 0.
 */
static void
run_func_in_child(void (*f)())
{
    int status;
    pid_t pid = fork();

    switch (pid) {
    case -1:
        perror("fork");
        exit(1);
    case 0: /* child */
        (*f)();
        break;
    default: /* parent */
        if (waitpid(pid, &status, 0) == -1) {
             perror("waitpid");
             exit(1);
        }
        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
             return;
        }
        fprintf(stderr, "child did not exit cleanly\n");
        exit(1);
    }
}

/*
 * scanf would be simpler, but let's protect against buffer overruns
 */
static void
get_rsp(const char *prompt, char *buf, size_t blen)
{
    int bl;

    printf("%s: ", prompt);
    fflush(stdout);

    if (fgets(buf, blen, stdin) == NULL) {
        if (ferror(stdin)) {
            perror("read");
        }
        exit(1);
    }
    bl = strlen(buf);
    if (bl > 0 && buf[bl - 1] == '\n') {
        buf[bl - 1] = '\0';
    }
}

static void
child_4()
{
    execlp("/usr/bin/ps", "ps", "-www", "-e", "f", NULL);

    perror("exec /usr/bin/ps");
    exit(1);
}

static void
child_3()
{
    char buf[256];
    int fd;

    run_func_in_child(child_4);

    get_rsp("File name", buf, sizeof buf);

    if (access(buf, F_OK) == 0) {
        fprintf(stderr, "%s already exists\n", buf);
        exit(1);
    }

    if ((fd = creat(buf, 0644)) == -1) {
        perror("creat");
        exit(1);
    }
    close(fd);

    printf("Created empty file %s\n", buf);

    exit(0);
}

static void
child_2()
{
    char buf[80];
    int fd[2];
    pid_t pid;

    run_func_in_child(child_3);

    get_rsp("Phrase", buf, sizeof buf);

    if (pipe(fd) == -1) {
        perror("pipe");
        exit(1);
    }

    pid = fork();

    switch (pid) {
    case -1:
        perror("fork");
        exit(1);
    case 0:
        /* no explicit wait for this child-of-child
         * process, but when its parent (the grep) exits,
         * init becomes the parent, and does the wait
         */
        dup2(fd[1], 1);
        close(fd[0]);
        close(fd[1]);
        execlp("/usr/bin/find", "find", NULL);
        perror("exec of find");
        exit(1);
    default:
        dup2(fd[0], 0);
        close(fd[0]);
        close(fd[1]);
        execlp("/usr/bin/grep", "grep", buf, NULL);
        perror("exec of grep");
        exit(1);
    }
}

static void
child_1()
{
    run_func_in_child(child_2);

    printf("Child 1: pid is %d; ppid is %d\n", getpid(), getppid());

    exit(0);
}

int
main(int ac, char *av[])
{
    run_func_in_child(child_1);

    return 0;
}
sjnarv
  • 2,334
  • 16
  • 13