Home > OS >  why is scanf skipping large part of input from file redirect? [C]
why is scanf skipping large part of input from file redirect? [C]

Time:02-10

I have this program called simplechain.c here which basically has a program fork itself once, the child does the same and that keeps going a certain amount of times, then each process now (in reverse order due to a wait()) reads some amount of characters and prints them once they have enough:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <getopt.h>
#include <errno.h>

// functiont that returns 0 (fail) if a string has anything but numbers 1 (success) if it all numbers
int isnumber(char * input){
        int length = strlen (input);
        int i;
        for (i = 0 ; i < length; i  )
        {
                if (!isdigit(input[i]))
                {
                        return 0;
                }
        }

        return 1;
}


int main (int argc, char *argv[]) {
        pid_t childpid = 0;
        int p;
        int nprocs = 4;
        int nchars = 80;
        int sleep_time = 3;
        int niters = 1;
        int opt;

        // argument handler
        while((opt = getopt(argc, argv, "hp:c:s:i:")) != -1)
        {
                switch(opt)
                {
                case 'h':       // help info
                         exit(0);
                        break;
                case 'p':       // set the amount of processes
                        if(!isnumber(optarg)){
                                exit(-1);
                        }
                        nprocs = atoi(optarg); // store the specified number of processes
                        break;
                case 'c':       // set the amount of characters to be read
                        if(!isnumber(optarg)){
                                exit(-1);
                        }
                        nchars = atoi(optarg); // store the specified number of character per interation to be read
                        break;
                case 's':       // set the amount of sleep time per interation
                        if(!isnumber(optarg)){
                                exit(-1);
                        }
                        sleep_time = atoi(optarg); // store the specified nubmer of sleep time in seconds per interation
                        break;
                case 'i':       // set the amount of iterationc per print
                        if(!isnumber(optarg)){
                                exit(-1);
                        }
                        niters = atoi(optarg); // store the specified number of interations to run on the print statments
                        break;
                }
        }
        // first start the for loop and fork if the current process number 'p' is less than the amount of processes
        for (p = 1; p < nprocs; p  )
                if ( (childpid = fork()) )
                        break;
        //sleep for 10 seconds
        sleep(10);

        // now print the process info 'niters' times
        int i;
        for(i = 0; i < niters; i  ){
                wait(); // wait befor the prints

                fprintf(stderr, "i:%d ",p);                             // current process Number (not to be confused with ID)
                fprintf(stderr, " parent ID:%d ", (long)getppid());     // parent procsess ID
                fprintf(stderr, " child ID:%d ", (long)childpid);       // child process ID

                // now prompt the user for a certain amount (nchars) of characaters
                fprintf(stderr, "\nPlease enter %d characters pressing enter after each one: ", nchars);
                // read the input of each char and store in in the myBuf array
                char myBuf[nchars   1];
                int c;

                for(c = 0; c < nchars; c  ){
                        scanf(" %c", &myBuf[c]);
                }
                myBuf[nchars] = '\0';   // add the null terminator so that it becomes a valid string

                sleep(sleep_time);      // sleep for a certain amount of (sleep_time) seconds

                // finally print the current process ID followed by the string 'myBuf'
                fprintf(stderr, "process ID:%d;  '%s' \n", (long)getpid(), myBuf);
        }

        return 0;
}

the point of interest is the loop using scanf(). its pretty simple as it just reads for a certain amount of characters and stores those characters into myBug array. you can obviously just do stdin but i was (which works fine btw) but i want to use a file so i did a file redirect given the fallowing file

In_Congress,_July_4,_1776_The_unanimous_Declaration_of_da_thirteen_united_States_of_America,_When_in_the_Course_of_human_events,_it_becomes_necessary_for_one_people_to_dissolve_the_political_bands_which_have_connected_them_with_another,_and_to_assume_among_the_powers_of_the_earth,_the_separate_and_equal_station_to_which_the_Laws_of_Nature_and_of_Nature's_God_entitle_them,_a_decent_respect_to_the_opinions_of_mankind_requires_that_they_should_declare_the_causes_which_impel_them_to_the_separation._We_hold_these_truths_to_be_self-evident,_that_all_men_are_created_equal,_that_they_are_endowed_by_their_Creator_with_certain_unalienable_Rights,_that_among_these_are_Life,_Liberty_and_the_pursuit_of_Happiness.--That_to_secure_these_rights,_Governments_are_instituted_among_Men,_deriving_their_just_powers_from_the_consent_of_the_governed,_--That_whenever_any_Form_of_Government_becomes_destructive_of_these_ends,_it_is_the_Right_of_the_People_to_alter_or_to_abolish_it,_and_to_institute_new_Government,_laying_its_foundation_on_such_principles_and_organizing_its_powers_in_such_form,_as_to_them_shall_seem_most_likely_to_effect_their_Safety_and_Happiness._Prudence,_indeed,_will_dictate_that_Governments_long_established_should_not_be_changed_for_light_and_transient_causes;_and_accordingly_all_experience_hath_shewn,_that_mankind_are_more_disposed_to_suffer,_while_evils_are_sufferable,_than_to_right_themselves_by_abolishing_the_forms_to_which_they_are_accustomed._But_when_a_long_train_of_abuses_and_usurpations,_pursuing_invariably_the_same_Object_evinces_a_design_to_reduce_them_under_absolute_Despotism,_it_is_their_right,_it_is_their_duty,_to_throw_off_such_Government,_and_to_provide_new_Guards_for_their_future_security.--Such_has_been_the_patient_sufferance_of_these_Colonies;_and_such_is_now_the_necessity_which_constrains_them_to_alter_their_former_Systems_of_Government._The_history_of_the_present_King_of_Great_Britain_is_a_history_of_repeated_injuries_and_usurpations,_all_having_in_direct_object_the_establishment_of_an_absolute_Tyranny_over_these_States._To_prove_this,_let_Facts_be_submitted_to_a_candid_world._He_has_refused_his_Assent_to_Laws,_the_most_wholesome_and_necessary_for_the_public_good._He_has_forbidden_his_Governors_to_pass_Laws_of_immediate_and_pressing_importance,_unless_suspended_in_their_operation_till_his_Assent_should_be_obtained;_and_when_so_suspended,_he_has_utterly_neglected_to_attend_to_them._He_has_refused_to_pass_other_Laws_for_the_accommodation_of_large_districts_of_people,_unless_those_people_would_relinquish_the_right_of_Representation_in_the_Legislature,_a_right_inestimable_to_them_and_formidable_to_tyrants_only._He_has_called_together_legislative_bodies_at_places_unusual,_uncomfortable,_and_distant_from_the_depository_of_their_public_Records,_for_the_sole_purpose_of_fatiguing_them_into_compliance_with_his_measures._He_has_dissolved_Representative_Houses_repeatedly,_for_opposing_with_manly_firmness_his_invasions_on_the_rights_of_the_people._He_has_refused_for_a_long_time,_after_such_dissolutions,_to_cause_others_to_be_elected;_whereby_the_Legislative_powers,_incapable_of_Annihilation,_have_returned_to_the_People_at_large_for_their_exercise;_the_State_remaining_in_the_mean_time_exposed_to_all_the_dangers_of_invasion_from_without,_and_convulsions_within._He_has_endeavoured_to_prevent_the_population_of_these_States;_for_that_purpose_obstructing_the_Laws_for_Naturalization_of_Foreigners;_refusing_to_pass_others_to_encourage_their_migrations_hither,_and_raising_the_conditions_of_new_Appropriations_of_Lands._He_has_obstructed_the_Administration_of_Justice,_by_refusing_his_Assent_to_Laws_for_establishing_Judiciary_powers._He_has_made_Judges_dependent_on_his_Will_alone,_for_the_tenure_of_their_offices,_and_the_amount_and_payment_of_their_salaries._He_has_erected_a_multitude_of_New_Offices,_and_sent_hither_swarms_of_Officers_to_harrass_our_people,_and_eat_out_their_substance._He_has_kept_among_us,_in_times_of_peace,_Standing_Armies_without_the_Consent_of_our_legislatures._He_has_affected_to_render_the_Military_independent_of_and_superior_to_the_Civil_power._He_has_combined_with_others_to_subject_us_to_a_jurisdiction_foreign_to_our_constitution,_and_unacknowledged_by_our_laws;_giving_his_Assent_to_their_Acts_of_pretended_Legislation:_For_Quartering_large_bodies_of_armed_troops_among_us:_For_protecting_them,_by_a_mock_Trial,_from_punishment_for_any_Murders_which_they_should_commit_on_the_Inhabitants_of_these_States:_For_cutting_off_our_Trade_with_all_parts_of_the_world:_For_imposing_Taxes_on_us_without_our_Consent:_For_depriving_us_in_many_cases,_of_the_benefits_of_Trial_by_Jury:_For_transporting_us_beyond_Seas_to_be_tried_for_pretended_offences_For_abolishing_the_free_System_of_English_Laws_in_a_neighbouring_Province,_establishing_therein_an_Arbitrary_government,_and_enlarging_its_Boundaries_so_as_to_render_it_at_once_an_example_and_fit_instrument_for_introducing_the_same_absolute_rule_into_these_Colonies:For_taking_away_our_Charters,_abolishing_our_most_valuable_Laws,_and_altering_fundamentally_the_Forms_of_our_Governments:_For_suspending_our_own_Legislatures,_and_declaring_themselves_invested_with_power_to_legislate_for_us_in_all_cases_whatsoever._He_has_abdicated_Government_here,_by_declaring_us_out_of_his_Protection_and_waging_War_against_us._He_has_plundered_our_seas,_ravaged_our_Coasts,_burnt_our_towns,_and_destroyed_the_lives_of_our_people._He_is_at_this_time_transporting_large_Armies_of_foreign_Mercenaries_to_compleat_the_works_of_death,_desolation_and_tyranny,_already_begun_with_circumstances_of_Cruelty_&_perfidy_scarcely_paralleled_in_the_most_barbarous_ages,_and_totally_unworthy_the_Head_of_a_civilized_nation._He_has_constrained_our_fellow_Citizens_taken_Captive_on_the_high_Seas_to_bear_Arms_against_their_Country,_to_become_the_executioners_of_their_friends_and_Brethren,_or_to_fall_themselves_by_their_Hands._He_has_excited_domestic_insurrections_amongst_us,_and_has_endeavoured_to_bring_on_the_inhabitants_of_our_frontiers,_the_merciless_Indian_Savages,_whose_known_rule_of_warfare,_is_an_undistinguished_destruction_of_all_ages,_sexes_and_conditions._In_every_stage_of_these_Oppressions_We_have_Petitioned_for_Redress_in_the_most_humble_terms:_Our_repeated_Petitions_have_been_answered_only_by_repeated_injury._A_Prince_whose_character_is_thus_marked_by_every_act_which_may_define_a_Tyrant,_is_unfit_to_be_the_ruler_of_a_free_people._Nor_have_We_been_wanting_in_attentions_to_our_Brittish_brethren._We_have_warned_them_from_time_to_time_of_attempts_by_their_legislature_to_extend_an_unwarrantable_jurisdiction_over_us._We_have_reminded_them_of_the_circumstances_of_our_emigration_and_settlement_here._We_have_appealed_to_their_native_justice_and_magnanimity,_and_we_have_conjured_them_by_the_ties_of_our_common_kindred_to_disavow_these_usurpations,_which,_would_inevitably_interrupt_our_connections_and_correspondence._They_too_have_been_deaf_to_the_voice_of_justice_and_of_consanguinity._We_must,_therefore,_acquiesce_in_the_necessity,_which_denounces_our_Separation,_and_hold_them,_as_we_hold_the_rest_of_mankind,_Enemies_in_War,_in_Peace_Friends._e,_therefore,_the_Representatives_of_the_united_States_of_America,_in_General_Congress,_Assembled,_appealing_to_the_Supreme_Judge_of_the_world_for_the_rectitude_of_our_intentions,_do,_in_the_Name,_and_by_Authority_of_the_good_People_of_these_Colonies,_solemnly_publish_and_declare,_That_these_United_Colonies_are,_and_of_Right_ought_to_be_Free_and_Independent_States;_that_they_are_Absolved_from_all_Allegiance_to_the_British_Crown,_and_that_all_political_connection_between_them_and_the_State_of_Great_Britain,_is_and_ought_to_be_totally_dissolved;_and_that_as_Free_and_Independent_States,_they_have_full_Power_to_levy_War,_conclude_Peace,_contract_Alliances,_establish_Commerce,_and_to_do_all_other_Acts_and_Things_which_Independent_States_may_of_right_do._And_for_the_support_of_this_Declaration,_with_a_firm_reliance_on_the_protection_of_divine_Providence,_we_mutually_pledge_to_each_other_our_Lives,_our_Fortunes_and_our_sacred_Honor._We,_therefore,_the_Representatives_of_the_united_States_of_America,_in_General_Congress,_Assembled,_appealing_to_the_Supreme_Judge_of_the_world_for_the_rectitude_of_our_intentions,_do,_in_the_Name,_and_by_Authority_of_the_good_People_of_these_Colonies,_solemnly_publish_and_declare,_That_these_United_Colonies_are,_and_of_Right_ought_to_be_Free_and_Independent_States;_that_they_are_Absolved_from_all_Allegiance_to_the_British_Crown,_and_that_all_political_connection_between_them_and_the_State_of_Great_Britain,_is_and_ought_to_be_totally_dissolved;_and_that_as_Free_and_Independent_States,_they_have_full_Power_to_levy_War,_conclude_Peace,_contract_Alliances,_establish_Commerce,_and_to_do_all_other_Acts_and_Things_which_Independent_States_may_of_right_do._And_for_the_support_of_this_Declaration,_with_a_firm_reliance_on_the_protection_of_divine_Providence,_we_mutually_pledge_to_each_other_our_Lives,_our_Fortunes_and_our_sacred_Honor.

this is just a long file containing the declaration of independence

But when i run the fallowing command : $ ./myapp -p 5 -c 30 -s 1 -i 2 < dc.txt this command should cause the program to make 4 forks each looping its print statements 2 and requiring 30 characters to be read per loop i get the fallowing output :

i:5  parent ID:29725  child ID:0
Please enter 30 characters pressing enter after each one: process ID:29726;  'In_Congress,_July_4,_1776_The_'
i:5  parent ID:29725  child ID:0
Please enter 30 characters pressing enter after each one: process ID:29726;  'unanimous_Declaration_of_da_th'
i:4  parent ID:29724  child ID:29726
Please enter 30 characters pressing enter after each one: process ID:29725;  't_of_and_superior_to_the_Civil'
i:4  parent ID:29724  child ID:29726
Please enter 30 characters pressing enter after each one: process ID:29725;  '_power._He_has_combined_with_o'
i:3  parent ID:29723  child ID:29725
Please enter 30 characters pressing enter after each one: process ID:29724;  'Congress,_Assembled,_appealing'
i:3  parent ID:29723  child ID:29725
Please enter 30 characters pressing enter after each one: process ID:29724;  '_to_the_Supreme_Judge_of_the_w'
i:2  parent ID:29722  child ID:29724
Please enter 30 characters pressing enter after each one: process ID:29723;  ''
i:2  parent ID:29722  child ID:29724
Please enter 30 characters pressing enter after each one: process ID:29723;  ''
i:1  parent ID:24839  child ID:29723
Please enter 30 characters pressing enter after each one: process ID:29722;  ''
i:1  parent ID:24839  child ID:29723
Please enter 30 characters pressing enter after each one: process ID:29722;  ''

I know the file has enough characters for sure. and it works fine for the first few processes. But if you look at the actual words being read you notice a skip in the text between processes. And finally the last 2 processes don't get any characters at all. My guess is process 5 reads twice then process 4 skips later on in the text for reads twice again, process 3 does the same and by the time it gets to process 2 and 1 it already reached the end of the file. I'm not sure why it is skipping.

CodePudding user response:

The short answer is Buffered I/O.

The programs all share the same file stream. Reading from a file, the first process to read the file gets a block of data (probably 512 or 4096 bytes) which the others don't see, but the file read position for the others moves. Rinse and repeat. If you used file descriptor I/O, you wouldn't get the same buffering effect. If you read some data using file streams before you did the forking, you'd get another set of results (all showing the same data). If the input was not a file but a pipe or something else, you'd get other results again.

You could probably fix it by setting the buffer size small, or unbuffered:

setvbuf(stdin, NULL, _IONBF, 0);

before doing any input.

Here's an adaptation of your code with an extra option -u to make standard input unbuffered and another option -P to suppress prompting. Note that I had to rename the function isnumber() to is_number() to avoid a name collision with the <ctype.h> header on my Mac — the name isnumber() is reserved for the implementation.

#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static int is_number(char *input)
{
    int length = strlen(input);
    int i;
    for (i = 0; i < length; i  )
    {
        if (!isdigit(input[i]))
        {
            return 0;
        }
    }

    return 1;
}

int main(int argc, char *argv[])
{
    pid_t childpid = 0;
    int p;
    int nprocs = 4;
    int nchars = 80;
    int sleep_time = 3;
    int niters = 1;
    int buffered = 1;
    int prompt = 1;
    int opt;

    while ((opt = getopt(argc, argv, "Puhp:c:s:i:")) != -1)
    {
        switch (opt)
        {
        case 'h':
            exit(0);
            break;
        case 'p':
            if (!is_number(optarg))
            {
                exit(-1);
            }
            nprocs = atoi(optarg);
            break;
        case 'c':
            if (!is_number(optarg))
            {
                exit(-1);
            }
            nchars = atoi(optarg);
            break;
        case 's':
            if (!is_number(optarg))
            {
                exit(-1);
            }
            sleep_time = atoi(optarg);
            break;
        case 'i':
            if (!is_number(optarg))
            {
                exit(-1);
            }
            niters = atoi(optarg);
            break;
        case 'u':
            buffered = 0;
            break;
        case 'P':
            prompt = 0;
            break;
        default:
            fprintf(stderr, "Unexpected option '%c'\n", opt);
            exit(EXIT_FAILURE);
        }
    }

    if (buffered)
    {
        fprintf(stderr, "Buffered input\n");
        setvbuf(stdin, NULL, _IOFBF, 0);
    }
    else
    {
        fprintf(stderr, "Unbuffered input\n");
        setvbuf(stdin, NULL, _IONBF, 0);
    }

    for (p = 1; p < nprocs; p  )
    {
        if ((childpid = fork()))
            break;
    }

    sleep(2);

    for (int i = 0; i < niters; i  )
    {
        int status;
        int corpse;
        while ((corpse = wait(&status)) > 0)
        {
            fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
                    getpid(), corpse, status);
        }

        fprintf(stderr, "i:%d ", p);
        fprintf(stderr, " parent ID:%d ", getppid());
        fprintf(stderr, " child ID:%d\n", childpid);

        if (prompt)
        {
            fprintf(stderr, "Please enter %d characters pressing enter after each one: ",
                    nchars);
        }

        char myBuf[nchars   1];
        int c;

        for (c = 0; c < nchars; c  )
        {
            scanf(" %c", &myBuf[c]);
        }
        myBuf[nchars] = '\0';

        sleep(sleep_time);

        fprintf(stderr, "process ID: %d;  '%s'\n", getpid(), myBuf);
    }

    return 0;
}

When run on a Mac, I get the desired behaviour whether or not standard input is buffered. When run on RHEL 7.4 Linux, the buffered/unbuffered I/O matters. The source code was in rd31.c and was compiled to create rd31.

$ make rd31
gcc -std=c11 -O3 -g -Wall -Wextra -Werror -Wstrict-prototypes -Wmissing-prototypes -Wshadow -pedantic-errors rd31.c -o rd31  
$ rd31 -P -u -p 5 -c 30 -s 1 -i 2 < dec-independence
Unbuffered input
i:5  parent ID:4462  child ID:0
process ID: 4463;  'In_Congress,_July_4,_1776_The_'
i:5  parent ID:4462  child ID:0
process ID: 4463;  'unanimous_Declaration_of_da_th'
4462: child 4463 exited with status 0x0000
i:4  parent ID:4460  child ID:4463
process ID: 4462;  'irteen_united_States_of_Americ'
i:4  parent ID:4460  child ID:4463
process ID: 4462;  'a,_When_in_the_Course_of_human'
4460: child 4462 exited with status 0x0000
i:3  parent ID:4459  child ID:4462
process ID: 4460;  '_events,_it_becomes_necessary_'
i:3  parent ID:4459  child ID:4462
process ID: 4460;  'for_one_people_to_dissolve_the'
4459: child 4460 exited with status 0x0000
i:2  parent ID:4457  child ID:4460
process ID: 4459;  '_political_bands_which_have_co'
i:2  parent ID:4457  child ID:4460
process ID: 4459;  'nnected_them_with_another,_and'
4457: child 4459 exited with status 0x0000
i:1  parent ID:9082  child ID:4459
process ID: 4457;  '_to_assume_among_the_powers_of'
i:1  parent ID:9082  child ID:4459
process ID: 4457;  '_the_earth,_the_separate_and_e'
$ rd31 -P -p 5 -c 30 -s 1 -i 2 < dec-independence
Buffered input
i:5  parent ID:4491  child ID:0
process ID: 4492;  'In_Congress,_July_4,_1776_The_'
i:5  parent ID:4491  child ID:0
process ID: 4492;  'unanimous_Declaration_of_da_th'
4491: child 4492 exited with status 0x0000
i:4  parent ID:4490  child ID:4492
process ID: 4491;  't_of_and_superior_to_the_Civil'
i:4  parent ID:4490  child ID:4492
process ID: 4491;  '_power._He_has_combined_with_o'
4490: child 4491 exited with status 0x0000
i:3  parent ID:4489  child ID:4491
process ID: 4490;  'Congress,_Assembled,_appealing'
i:3  parent ID:4489  child ID:4491
process ID: 4490;  '_to_the_Supreme_Judge_of_the_w'
4489: child 4490 exited with status 0x0000
i:2  parent ID:4487  child ID:4490
process ID: 4489;  ''
i:2  parent ID:4487  child ID:4490
process ID: 4489;  ''
4487: child 4489 exited with status 0x0000
i:1  parent ID:9082  child ID:4489
process ID: 4487;  ''
i:1  parent ID:9082  child ID:4489
$
  • Related