COMS 3157 Advanced Programming

Recitation 12: fork() + exec()

In the following problems, assume fork() and execv() always succeed.

You can obtain the solutions from CLAC by checking out the following repo:

git clone ~j-hui/cs3157-pub/examples/fork-exec-puzzles

12.1 exec-demo

Consider the following program, exec-demo, which is implemented as follows:

int main(int argc, char **argv) {
    assert(argc == 3);

    int n1 = atoi(argv[1]);
    int n2 = atoi(argv[2]);

    if (n1 > 0) {
        char s1[100]; sprintf(s1, "%d", n1 - 1);  argv[1] = s1;
        char s2[100]; sprintf(s2, "%d", n2 + n1); argv[2] = s2;
        execv(*argv, argv);
        printf("%d\n", n1);
    }
    printf("%d\n", n2);
    return 0;
}

Write the output when exec-demo is run with arguments 4 and 100, i.e.:

./exec-demo 4 100

12.2 fork()ing with some numbers

Consider the following programs, which both print a fixed number of lines:

// 12.2.1
int main(void) {
    pid_t pid = fork();
    if (pid) {
        waitpid(pid, NULL, 0);
        printf("3\n");
    }
    sleep(2);
    printf("1\n");
    return 0;
}
// 12.2.2
int main(void) {
    int p1, p2, p3, x;
    p1 = fork(); sleep(1); p2 = fork(); sleep(1); p3 = fork();
    x = (p1 != 0) + (p2 != 0) + (p3 != 0);
    printf("%d\n", x);
    return 0;
}
// 12.2.3
int main(int argc, char **argv) {
    if (argc != 2) {
        char *a[] = { argv[0], "1", NULL };
        execv(*a, a);
    }

    int n = atoi(argv[1]);
    if (n > 3)
        return 0;

    sleep(1);

    printf("%d\n", n);
    sprintf(argv[1], "%d", n + 1);

    pid_t pid = fork();
    execv(*argv, argv);
    if (pid) {
        waitpid(pid, NULL, 0);
        printf("%d\n", 0);
    }

    return 0;
}

Each program is run with no arguments; for each program, answer the following:

Specify UNPREDICTABLE if the answer may change from run to run.

12.3 fffork()

Write the output of the following C program:

void print_char(char c) {
    fwrite(&c, 1, 1, stderr);
}

static int i = 3;

void fffork(char **a) {
    if (*a) {
        pid_t pid = fork();
        if (pid == 0) {
            // child process
            i--;
            fffork(a + 1);
            print_char(a[0][i]);
        } else {
            // parent process
            waitpid(pid, NULL, 0); // no status & no options
            print_char('0');
            print_char('\n');
        }
    }
}

int main(void) {
    char *a[4] = { "123", "456", "789", NULL };
    fffork(a);
    return 0;
}

Also draw a diagram of all fffork() processes, at the moment when the first character is printed on screen. Draw your diagram using the same style as the f option of the ps command, i.e., as a process tree.

Label each process with process IDs. Assume that the first fffork process’s process ID is 2000, and that each subsequent fffork processes increments the process ID by 1.

For each process, indicate the values of the variable i and what the pointer a is pointing to at that moment.

12.4 eeexec()

Consider the following C program, eeexec.c:

void eeexec(char *cmd, int n) {
    if (n > 0) {
        printf("A %d\n", n);

        eeexec(cmd, n - 1);

        char buf[100];
        sprintf(buf, "%d", n - 1);

        char *a[3] = { cmd, buf, NULL };
        execv(*a, a);

        printf("B %d\n", n);
    }
}

int main(int argc, char **argv) {
    assert(argc == 2);
    eeexec(argv[0], atoi(argv[1]));
    printf("C %s\n", argv[1]);
    return 0;
}

The program is built and run as follows:

$ gcc -o eeexec eeexec.c

$ ./eeexec 4

Specify UNPREDICTABLE if the answer may change from run to run; specify HANG if the program runs forever without terminating.