2
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>


int main( int argc, char *argv[] ){


    FILE *fptr;
    pid_t pid;

    fptr = fopen("Shared File.txt", "a");
    pid = fork();

    if( pid > 0 ){ // parent process

        int counter = 0;

        while( counter < 10 ){
            fprintf(fptr, "a");
            ++counter;
        }
        wait(NULL);
    }
    else{

        int counter = 0;

        while( counter < 5 ){
            fprintf(fptr, "b");
            ++counter;
        }
    }

    return 0;
}

When I execute this code, the file produced by the code contains this message: bbbbbaaaaaaaaaa

Whenever I execute this code, I get same message. Why does not the processes write to file in shuffling order ?

Why does the operating system try to finish the child process at first ?

My expectation about the message is like this: baabbaaabaaabaa There is no continuous transition between the processes.

6

The scheduling between parent and child has been discussed already (at least) in When child processes are executed and How does fork system call really works.

But in this case, there's also the question of buffering within stdio. You're using fprintf() to write to a regular file. By default, stdio buffers output to regular files until enough data is written, to save on system call overhead. On x86 Linux, it usually seems to write in 4096 byte blocks, but you can't count on that, unless you set the buffering manually (see setbuf() and friends).

You can see this with a command like strace that shows the system calls a program makes.

So, while you can't make any predictions about which process runs first, in this case you can predict that the as are written consecutively, and the bs as well. You can only get bbbbbaaaaaaaaaa or aaaaaaaaaabbbbb, and which one you get is pretty much up to chance.

2

I mostly agree with @ikkachu, except

Which one you get bbbbbaaaaaaaaaa or aaaaaaaaaabbbbb is predictable. The OS waits for child to finish, because of wait(NULL), then the parent exits. As buffers are flushed on exit, the child gets to write first.

However do not rely on the buffers being predictable. Use explicit flush when needed.

  • I think I've seen some of GNU utilities, I think find in particular, that use fflush() before continuing processing next file or next argument. Definitely seems like the proper practice. – Sergiy Kolodyazhnyy May 27 '18 at 2:12
1

User ilkkachu gave a good explanation how buffering affects the output. My answer describes what would happen if you eliminate the buffering, e.g. by replacing the fprintf calls with calls to write. In this case you would get strictly alternating as and bs. This is because the call to write causes a reschedule: writing in one process blocks, giving the turn to the other process, and so on.

Let's imagine what would happen if the call to write would not block. Then we would have to consider timescales: you would get much longer runs of as and bs than just one or two at a time, because modern processors are capable of executing billions of instructions per second, but the scheduling frequency is typically something between 100 Hz and 1000 Hz. A process would be able to execute up to tens of millions of instructions before it is preempted and the other process is scheduled to run. Even taking into account the system call overhead, this would give the process time to print very long strings of consecutive as or bs.

1

ikkachu and Johan did an great job of explaining why you observed the behaviour that you did, so I slightly rewrote your program so that each process flushes the stream and sleeps for a second (so each thread has a chance to interleave differently each time), so that you can see the interleaving effect more clearly.

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
  FILE *fptr = stdout;
  pid_t pid;
  int counter = 0;

  pid = fork();

  if(pid > 0) { // parent process
    while(counter++ < 10) {
      fprintf(fptr, "aa");
      fflush(fptr);
      sleep(1);
    }
    wait(NULL);
    puts("");
  } else {
    while(counter++ < 5) {
      fprintf(fptr, "bbbb");
      fflush(fptr);
      sleep(1);
    }
  }
}

If you run this several times, you'll get different results once in a while:

~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbbbbbaaaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.