简体   繁体   中英

C program asking for user input multiple times

Writing a very basic C program that demonstrates shared memory. The program needs to keep calculating the sum of two inputs until the user does a ^C (not shown here).

What I have so far is that my parent captures user input, then tells the child to calculate the sum, which the parent then prints the sum (as the assignment suggests).

Attached is my code.

// Fork the process                                                   
while(1) {                                                            
    // Declare a shared memory integer array                          
    int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);                                                                        
    int x;                                                            
    int y;                                                            
    shared_ints[3] = 0;                                               

    if((pid = fork()) < 0) {                                          
        printf("Fork error \n");                                      
        exit(-1);                                                     
    } else if(pid > 0 && shared_ints[3] == 0) { // Parent code        
        // Ask user for first input                                   
        printf("Enter your first number:");                           
        scanf("%d", &x);                                              
        printf("\n");                                                 

        // Ask user for second input                                  
        printf("Enter your second number:");                          
        scanf("%d", &y);                                              
        printf("\n");                                                 

        // Assign first value to shared memory                        
        shared_ints[0] = x;                                           

        // Assign second value to shared memory                       
        shared_ints[1] = y;                                           

        // Tell child that it can compute the sum now                 
        shared_ints[3] = 1;                                           

        // Trap parent in a while-loop while the child                
        // computes the sum                                           
        while(shared_ints[3] == 1);                                   

        // Child has completed calculating the sum and                
        // the parent can print the sum                               
        if(shared_ints[3] == 2) {                                     
            printf("The sum is: %d", shared_ints[2]);                 
            printf("\n");                                             
        }                                                             
    } else { // Child code                                            
        // Wait for parent to accept input values                     
        while(shared_ints[3] == 0);                                   

        // Calculate the sum                                          
        shared_ints[2] = shared_ints[0] + shared_ints[1];             

        // Tell parent sum has been calculated                        
        shared_ints[3] = 2;                                           
    }                                                                 
}

The sum calculation works until I go to the fourth-iteration of the sum calculation (this is the output):

Created shared memory object /my_shared_memory
Shared memory segment allocated correctly (16 bytes).
Enter your first number:1
Enter your second number:2
The sum is: 3
Enter your first number:Enter your first number:3
Enter your second number:4
The sum is: 7
Enter your first number:Enter your first number:5
Enter your second number:6
The sum is: 11
Enter your first number:Enter your first number:7
Enter your second number:8
Enter your second number:9
Enter your second number:

An interesting bug I see is that after I print the sum, the program asks for the first input twice as suggested here: Enter your first number:Enter your first number:3 , and then in the fourth sum iteration, it asks for the second number several times before calculating the sum.

The trouble is that you're forking on each iteration of the outer while loop, so the first time around, you have one child, then you have a child that thinks it has grown up and is a parent (as well as the parent that knows it is a parent). And it gets worse as you continue.

Another problem is that you're allocating shared memory on each iteration of the outer while loop. I'm not sure it's exactly a memory leak, but it probably isn't a good idea.

To fix:

  • Move the mmap() and the fork() outside the while (1) loop.
  • The parent process will have a loop — in its own function, not all inside main() , please — that reads from the user, transfers to the child, reads the result from the child, and continues.
  • The child process will have a loop — also in its own function, please — that reads from the parent, calculates, and continues.
  • Make sure the child knows how to exit when the parent exits.
  • Ideally, use a synchronization mechanism other than busy waiting. A couple of mutexes or a couple of semaphores would be good. The processes would wait to be told there's work for them to do, rather than spinning their wheels (very fast) while waiting for the next task.

More or less working code

This uses a header "stderr.h" and functions err_setarg0() and err_syserr() from code I wrote because error reporting is crucial and these make it easy. You should be able to find code on SO from me with working versions of that header and those functions.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "stderr.h"

static void be_childish(int *shared_ints);
static void be_parental(int *shared_ints);

int main(int argc, char **argv)
{
    const char *file = "fidget";
    int shared_seg_size = 4 * sizeof(int);

    err_setarg0(argv[0]);
    if (argc > 1)
        file = argv[1];

    int fd = open(file, O_RDWR|O_CREAT, 0600);
    if (fd < 0)
        err_syserr("Failed to open file %s for read/write\n", file);
    /* Assume sizeof(int) == 4 */
    if (write(fd, "abcdefghijklmnop", shared_seg_size) != shared_seg_size)
        err_syserr("Failed to write %d bytes to %s\n", shared_seg_size, file);
    int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_ints == 0)
        err_syserr("Failed to mmap file %s\n", file);

    shared_ints[3] = 0;

    int pid;
    if ((pid = fork()) == -1)
        err_syserr("Failed to fork\n");
    else if (pid  == 0)
        be_childish(shared_ints);
    else
        be_parental(shared_ints);
    return 0;
}

static void be_childish(int *shared_ints)
{
    while (1)
    {
        // Wait for parent to generate input values
        while (shared_ints[3] == 0 || shared_ints[3] == 2)
        {
            printf("Child: %d\n", shared_ints[3]);
            usleep(500000);
        }

        if (shared_ints[3] != 1)
        {
            printf("Child: exiting\n");
            return;
        }

        // Calculate the sum
        shared_ints[2] = shared_ints[0] + shared_ints[1];

        printf("Child: calculated %d + %d = %d\n", shared_ints[0], shared_ints[1], shared_ints[2]);

        // Tell parent sum has been calculated
        shared_ints[3] = 2;
    }
}

static void be_parental(int *shared_ints)
{
    while (1)
    {
        int x;
        int y;
        // Ask user for first input
        printf("Enter your first number:");
        if (scanf("%d", &x) != 1)
        {
            printf("Parent: exiting\n");
            shared_ints[3] = -1;    /* Tell child to exit */
            return;
        }
        printf("\n");

        // Ask user for second input
        printf("Enter your second number:");
        if (scanf("%d", &y) != 1)
        {
            printf("Parent: exiting\n");
            shared_ints[3] = -1;    /* Tell child to exit */
            return;
        }
        printf("\n");

        // Assign first value to shared memory
        shared_ints[0] = x;

        // Assign second value to shared memory
        shared_ints[1] = y;

        // Tell child that it can compute the sum now
        shared_ints[3] = 1;

        // Trap parent in a while-loop while the child
        // computes the sum
        while (shared_ints[3] == 1)
        {
            printf("Parent: %d\n", shared_ints[3]);
            usleep(500000);
        }

        // Child has completed calculating the sum and
        // the parent can print the sum
        if (shared_ints[3] == 2)
        {
            printf("The sum is: %d", shared_ints[2]);
            printf("\n");
        }
        else
        {
            printf("Parent: unexpected control %d - exiting\n", shared_ints[2]);
            shared_ints[3] = -1;    /* Tell child to exit */
            return;
        }
    }
}

This code has debugging in the busy-wait loops, and a delay of 0.5 seconds on each read cycle. You can tweak that to suit yourself, and lose the output once your sure it's working for you, too. Note that the code destroys a file fidget unless you specify an alternative name to be created/destroyed. It does not remove the file as it exits; it probably should.

Sample run

$ ./dualproc
Enter your first number:Child: 0
1

Enter your second number:Child: 0
2

Parent: 1
Child: calculated 1 + 2 = 3
Child: 2
The sum is: 3
Enter your first number:Child: 2
Child: 2
3

Enter your second number:Child: 2
4

Parent: 1
Child: calculated 3 + 4 = 7
Child: 2
The sum is: 7
Enter your first number:Child: 2
q
Parent: exiting
$ Child: exiting

$ 

The parent exited before the child did; no big surprise there. You could upgrade the code to use wait() to wait for the child to exit if you preferred.

NOTE: There is a problem here in a corner case where the parent quits and the child is still waiting for the parents input value - can happen if the user breaks the program when shared_ints[3] != 0

To demonstrate @Jonathan Leffler's excellent answer, here's my suggestion for a fix:

    /* These are better declared outside the while loop */
    // Declare a shared memory integer array                          
    int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);                                                                        
    int x;                                                            
    int y;                                                            

// Fork the process                                                   
while(1) {                                                            

    shared_ints[3] = 0; 
    if((pid = fork()) < 0) {                                          
        printf("Fork error \n");                                      
        exit(-1);                                                     
    } else if(pid > 0 && shared_ints[3] == 0) { // Parent code        
        // Ask user for first input                                   
        printf("Enter your first number:");                           
        scanf("%d", &x);                                              
        printf("\n");                                                 

        // Ask user for second input                                  
        printf("Enter your second number:");                          
        scanf("%d", &y);                                              
        printf("\n");                                                 

        // Assign first value to shared memory                        
        shared_ints[0] = x;                                           

        // Assign second value to shared memory                       
        shared_ints[1] = y;                                           

        // Tell child that it can compute the sum now                 
        shared_ints[3] = 1;                                           

        // Trap parent in a while-loop while the child                
        // computes the sum                                           
        while(shared_ints[3] == 1);                                   

        // Child has completed calculating the sum and                
        // the parent can print the sum                               
        if(shared_ints[3] == 2) {                                     
            printf("The sum is: %d", shared_ints[2]);                 
            printf("\n");                                             
        }                                                             
    } else { // Child code                                            
        // Wait for parent to accept input values                     
        while(shared_ints[3] == 0);                                   

        // Calculate the sum                                          
        shared_ints[2] = shared_ints[0] + shared_ints[1];             

        // Tell parent sum has been calculated                        
        shared_ints[3] = 2;   
        /* Child process should finish, so it doesn't fork
           and in the upper while loop. this is a fast way to do it */
        exit(0);                            
    }                                                                 
}

At each iteration of the big while loop, the parent and child process are calling fork() in this line:

if((pid = fork()) < 0) {                                          

You are repeatedly spawning more child processes after every iteration and can be creating unexpected interactions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM