cmultithreadingpipeforkposix-select

C - not reaching proper end with fork(), pipe(), select(), execl(), and write()


I am using forks and pipes to find the amount of 1s and 0s in a string inside of a file. However, I am never reaching the proper end to my program that tallys up the ones and zeroes. It's a pretty small amount of code so here is the whole program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>

int main (int argc, char** argv)
{
        int leftR, rightR;
        char *string;
        long size;
        int recursion = 0;
        if (argc == 3)
        {
                string = argv[1];
                size = strlen(string);
                printf("the string is %s\n", string);
                if (size <= 2)
                {

                        int bitCounter[2];
                        bitCounter[0] = 0;
                        bitCounter[1] = 0;
                        int i;
                        for (i=0; i < size; i++)
                        {
                                if (string[i]=='0')
                                {
                                        bitCounter[0]++;
                                }
                                else
                                {
                                        bitCounter[1]++;
                                }
                        }
                        write(STDOUT_FILENO, &bitCounter, sizeof(int)*2);
                        printf("read bits, sending back %d ones and %d zeroes\n", bitCounter[1], bitCounter[0]);
                        return 0;
                }
                else
                {
                        recursion = 1;
                }
        }
        if (argc == 2 || recursion)
        {
                char *data;
                if (!recursion)
                {
                        FILE* filePointer;
                        if ((filePointer = fopen(argv[1], "r")) == NULL)
                        {
                                perror("file didn't work");
                        }
                        fseek(filePointer, 0, SEEK_END);
                        size = ftell(filePointer);
                        fseek(filePointer, 0, SEEK_SET);
                    data = malloc(size+1);
                        fread(data, size, 1, filePointer);
                        fclose(filePointer);
                }
                else
                {
                        data = malloc(size+1);
                        data = string;
                }

                char *right;
                char *left = malloc((size/2)+1);
                if (size%2 == 0)
                {
                        right = malloc(size/2 + 1);
                }
                else
                {
                        right = malloc(size/2 + 2);
                }

                memcpy(left, data, size/2);

                if (size%2 == 0)
                {
                        memcpy(right, (size/2) + data, size/2);
                }
                else
                {
                        memcpy(right, (size/2) + data, (size/2) + 1);
                }

                int pidLeft, pidRight;
                int leftPipe[2];
                int rightPipe[2];

                pipe(leftPipe);
                pipe(rightPipe);

                fd_set readF;

                FD_ZERO(&readF);
                FD_SET(leftPipe[0], &readF);
                FD_SET(rightPipe[0], &readF);

                pidLeft = fork();
                if (pidLeft > 0)
                {
                        pidRight = fork();
                        if (pidRight > 0)
                        {
                                struct timeval timer;
                                timer.tv_sec = 3;
                                timer.tv_usec = 0;

                                close(rightPipe[1]);
                                close(leftPipe[1]);
                                dup2(leftPipe[0], STDOUT_FILENO);
                                dup2(rightPipe[0], STDOUT_FILENO);

                                select(2, &readF, NULL, NULL, &timer);
                                read(leftPipe[0], &leftR, sizeof(int)*2);
                                read(rightPipe[0], &rightR, sizeof(int)*2);
                                printf("going back to parent.\n");
                        }
                        else if (pidRight == 0)
                        {
                                close(rightPipe[0]);
                                execl("my_program", "my_program", right, "y",  NULL);
                                printf("recursion start\n");
                                exit(1);
                        }
                }
                else if (pidLeft == 0)
                {
                        close(leftPipe[0]);
                        execl("my_program", "my_program", left, "y", NULL);
                        printf("start recursion LEFT\n");
                        exit(1);
                }
                else
                {
                        fprintf(stderr, "something went wrong! No fork!\n");
                }
        }
        else
        {
                fprintf(stderr, "Please input file name properly\n");
                exit(1);
        }
    int zeroes = leftR + rightR;
    int* numOnes[2];
    numOnes[0] = &leftR + sizeof(int);
    numOnes[1] = &rightR + sizeof(int);
    int ones = (int) *numOnes[0] + (int) *numOnes[1];
    printf("0's: %d\n1's: %d\n", zeroes, ones);
    return 0;

}

However, the output never reaches my desired end that adds everything up:

the string is 01010▒z
the string is 010
the string is 0
read bits, sending back 0 ones and 1 zeroes
the string is 10▒z
the string is 10
read bits, sending back 1 ones and 1 zeroes
the string is ▒z
read bits, sending back 2 ones and 0 zeroes
the string is 10
read bits, sending back 1 ones and 1 zeroes
the string is 10100
z
the string is 101
the string is 1
read bits, sending back 1 ones and 0 zeroes
the string is 01
read bits, sending back 1 ones and 1 zeroes
the string is 00
z
the string is 00
read bits, sending back 0 ones and 2 zeroes
the string is
z
read bits, sending back 2 ones and 0 zeroes
(140) Admin $

A couple quick points to understand the code a little easier:

I guess a couple easy questions to start off would be:


Solution

  • This is one of the tougher nuts to crack. The code is doing an obscure job and is written rather obscurely. As far as I can tell, it is supposed to be called my_program, and is due to be invoked with a file name as a single argument. The process will then open the file, read its content into two arrays (left and right), without bothering to ensure that they are strings (no null termination). The process then forks twice. One then execs itself with the left (non-)string as an argument and the information that it is left, and one execs itself with the right (non-)string as an argument and the information that it is right. The parent process messes around with select() for no very good reason, and without checking the return value. It then calls read to get information on the two pipes. These reads would block until there's data ready, so the select() really doesn't help at all. (I'm still trying to work out where the recursion bit fits in.) Also, most of the system calls are not error checked.

    However, given that outline operation (read file, split, children exec and do half each and report back), I would be writing something like this:

    #include <errno.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    static void err_exit(char const *fmt, ...);
    
    int main(int argc, char * *argv)
    {
        int l_data[2]  = { -1, -1 };
        int r_data[2] = { -1, -1 };
        char *string;
        size_t size;
        char *arg0 = argv[0];
    
        if (argc != 2 && argc != 3)
            err_exit("Usage: %s file\n", argv[0]);
    
        if (argc == 3)
        {
            /* Child process */
            string = argv[1];
            size = strlen(string);
            fprintf(stderr, "%d: the string is %s\n", (int)getpid(), string);
            if (size <= 2)
            {
                int bitCounter[2];
                bitCounter[0] = 0;
                bitCounter[1] = 0;
                for (size_t i = 0; i < size; i++)
                {
                    if (string[i] == '0')
                        bitCounter[0]++;
                    else if (string[i] == '1')
                        bitCounter[1]++;
                }
                if (write(STDOUT_FILENO, bitCounter, sizeof(int)*2) != sizeof(int)*2)
                    err_exit("%d: failed to write on standard output\n",
                            (int)getpid());
                fprintf(stderr, "%d: read bits, sending back %d ones and %d zeroes\n",
                       (int)getpid(), bitCounter[1], bitCounter[0]);
                exit(0);
            }
            fprintf(stderr, "%d: doing recursion - string too big (%zu)\n",
                    (int)getpid(), size);
        }
    
        char *data = string;
        if (argc == 2)
        {
            FILE *filePointer;
            if ((filePointer = fopen(argv[1], "r")) == NULL)
            {
                perror("file didn't work");
                exit(1);
            }
            fseek(filePointer, 0, SEEK_END);
            size = ftell(filePointer);
            fseek(filePointer, 0, SEEK_SET);
            data = malloc(size+1);
            fread(data, size, 1, filePointer);
            data[size] = '\0';
            if (data[size-1] == '\n')
                data[--size] = '\0';
            fclose(filePointer);
            fprintf(stderr, "%d: data <<%s>>\n", (int)getpid(), data);
        }
    
        size_t l_size = size/2;
        size_t r_size = size - l_size;
        char *left = malloc(l_size+1);
        char *right = malloc(r_size+1);
    
        memcpy(left, data, l_size);
        left[l_size] = '\0';
        memcpy(right, data + l_size, r_size);
        right[r_size] = '\0';
    
        int l_pid, r_pid;
        int l_pipe[2] = { -1, -1 };
        int r_pipe[2] = { -1, -1 };
    
        if (pipe(l_pipe) != 0 || pipe(r_pipe) != 0)
            err_exit("%d: Failed to create pipes\n", (int)getpid());
    
        fprintf(stderr, "%d: forking (l_size = %zu, r_size = %zu)\n",
                (int)getpid(), l_size, r_size);
        l_pid = fork();
        if (l_pid < 0)
            err_exit("%d: Failed to fork() left child\n", (int)getpid());
        else if (l_pid == 0)
        {
            dup2(l_pipe[1], STDOUT_FILENO);
            close(l_pipe[0]);
            close(l_pipe[1]);
            close(r_pipe[0]);
            close(r_pipe[1]);
            fprintf(stderr, "%d: left execing with string <<%s>>\n", (int)getpid(), left);
            execl(arg0, arg0, left, "y", NULL);
            err_exit("%d: failed to start recursion LEFT\n", (int)getpid());
        }
        else if ((r_pid = fork()) < 0)
            err_exit("%d: Failed to fork() right child\n", (int)getpid());
        else if (r_pid == 0)
        {
            dup2(r_pipe[1], STDOUT_FILENO);
            close(l_pipe[0]);
            close(l_pipe[1]);
            close(r_pipe[0]);
            close(r_pipe[1]);
            fprintf(stderr, "%d: right execing with string <<%s>>\n", (int)getpid(), right);
            execl(arg0, arg0, right, "y",  NULL);
            err_exit("%d: failed to start recursion RIGHT\n", (int)getpid());
        }
        else
        {
            /* Parent process */
            int nbytes;
            close(r_pipe[1]);
            close(l_pipe[1]);
            if ((nbytes = read(l_pipe[0], l_data, sizeof(int)*2)) != sizeof(int)*2)
                err_exit("%d: Read left pipe failed (%d)\n", (int)getpid(), nbytes);
            if ((nbytes = read(r_pipe[0], r_data, sizeof(int)*2)) != sizeof(int)*2)
                err_exit("%d: Read right pipe failed (%d)\n", (int)getpid(), nbytes);
            close(l_pipe[0]);
            close(r_pipe[0]);
        }
    
        int zeroes = l_data[0] + r_data[0];
        int ones   = l_data[1] + r_data[1];
        if (argc == 3)
        {
            int data[2] = { zeroes, ones };
            if (write(STDOUT_FILENO, data, sizeof(data)) != sizeof(data))
                err_exit("%d: failed to read binary data from stdin\n", (int)getpid());
            fprintf(stderr, "%d: binary write to stdout OK\n", (int)getpid());
        }
    
        fprintf(stderr, "%d: 0's = %d, 1's = %d\n", (int)getpid(), zeroes, ones);
        return 0;
    }
    
    static void err_exit(char const *fmt, ...)
    {
        int errnum = errno;
        va_list args;
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
        if (errnum != 0)
            fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
        exit(1);
    }
    

    Given an input file:

    0101010111111
    

    the output is:

    11070: data <<0101010111111>>
    11070: forking (l_size = 6, r_size = 7)
    11073: right execing with string <<0111111>>
    11072: left execing with string <<010101>>
    11072: the string is 010101
    11072: doing recursion - string too big (6)
    11072: forking (l_size = 3, r_size = 3)
    11073: the string is 0111111
    11073: doing recursion - string too big (7)
    11073: forking (l_size = 3, r_size = 4)
    11074: left execing with string <<010>>
    11075: right execing with string <<101>>
    11076: left execing with string <<011>>
    11077: right execing with string <<1111>>
    11074: the string is 010
    11074: doing recursion - string too big (3)
    11074: forking (l_size = 1, r_size = 2)
    11078: left execing with string <<0>>
    11076: the string is 011
    11076: doing recursion - string too big (3)
    11076: forking (l_size = 1, r_size = 2)
    11079: right execing with string <<10>>
    11075: the string is 101
    11075: doing recursion - string too big (3)
    11075: forking (l_size = 1, r_size = 2)
    11080: left execing with string <<0>>
    11077: the string is 1111
    11077: doing recursion - string too big (4)
    11077: forking (l_size = 2, r_size = 2)
    11082: right execing with string <<11>>
    11081: left execing with string <<1>>
    11083: right execing with string <<01>>
    11084: left execing with string <<11>>
    11085: right execing with string <<11>>
    11079: the string is 10
    11078: the string is 0
    11079: read bits, sending back 1 ones and 1 zeroes
    11078: read bits, sending back 0 ones and 1 zeroes
    11074: binary write to stdout OK
    11074: 0's = 2, 1's = 1
    11082: the string is 11
    11082: read bits, sending back 2 ones and 0 zeroes
    11080: the string is 0
    11080: read bits, sending back 0 ones and 1 zeroes
    11076: binary write to stdout OK
    11076: 0's = 1, 1's = 2
    11081: the string is 1
    11081: read bits, sending back 1 ones and 0 zeroes
    11084: the string is 11
    11084: read bits, sending back 2 ones and 0 zeroes
    11083: the string is 01
    11083: read bits, sending back 1 ones and 1 zeroes
    11075: binary write to stdout OK
    11075: 0's = 1, 1's = 2
    11072: binary write to stdout OK
    11072: 0's = 3, 1's = 3
    11085: the string is 11
    11085: read bits, sending back 2 ones and 0 zeroes
    11077: binary write to stdout OK
    11077: 0's = 0, 1's = 4
    11073: binary write to stdout OK
    11073: 0's = 1, 1's = 6
    11070: 0's = 4, 1's = 9