In my homework I should explain what is happening in the following code:
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int x = 1;
if(fork() == 0){// child
printf("printf1: x=%d\n", ++x);// add then print
}
printf("printf2: x=%d\n", --x);
exit(0);
}
It's pretty straightforward and easy to understand. Most of the time I get the following output:
printf2: x=0
printf1: x=2
printf2: x=1
This means that the parent process was completed before the child and the child became a zombie process.
But sometimes I get the following output:
printf1: x=2
printf2: x=1
After printing that the program freezes (It does not print anything and does not stop).
I am running the program on Ubuntu.
Any explanation will be appreciated.
You have 3 processes writing to your terminal: parent, child and the shell interpreter. The parent process and the shell have "syncronized" output, but the child process may interleave its output with either of those. What you may perceive as a hanged process, may actually only be mangled output.
When you think it has hanged, you may try to enter a command and press enter...
Related
While reading through some articles about fork() in C, I saw this example (code below) which I couldn't understand:
Understanding problem:
We only run "if" or "else" and not both. But since the child and parent processes run "simultaneoustly", in this example we see that we went through "if" and "else" both!
Eventhough it's similtaneous, it isn't in reality, it depends on which one of the processes will get the CPU first (right?).
What makes everything "weirder" is that we might first go through the "else" and then through the "if". How is this possible ?
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void forkexample()
{
// child process because return value zero
if (fork() == 0)
printf("Hello from Child!\n");
// parent process because return value non-zero.
else
printf("Hello from Parent!\n");
}
int main()
{
forkexample();
return 0;
}
Possible Outputs are:
Hello from Child!
Hello from Parent!
(or)
Hello from Parent!
Hello from Child!
Remember that the fork function actually returns twice: once to the parent with the child's pid and once to the child with 0.
At that point you have two independent processes running the same code and printing to the same terminal. And since you have two processes, the kernel is free to schedule them in any way it sees fit, meaning the output of either process can appear in any order.
fork is a system call, which does following (in abstraction, actual implementation likely to differ on the modern systems):
Creates a copy of the process image in memory and sets it up as a process with it's own PID
In one image (the child) returns 0
In second image (the parent) returns the PID of the new process
Schedules execution of both images as usual
As a result, on the multi-core system, both parent and a child are going to be executing independently, and absent other synchronization, in no way related to each other.
And if you observe the output of those two processes, you will find out that output from those can come in any shape or form - you can have output from the parent preceding output from the child, succeeding it or being interleaved with it - it is unpredictable what you will end up seeing.
My code:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
int main(){
fork();
printf("Show me the new line before the pid child dies \n");
return 0;
}
Output:
>Show me the new line before the pid child dies Show me the new line before the pid child dies"\n
>
My expected result would be to show the '\n' as part of the string and to have two separate lines, as below:
>string1 \n
>string2 \n
>
but I am getting the following:
>string1 string2 \n
>
I tried with the fflush(stdout) but didn't make a difference.
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
int main(){
fork();
printf("Show me the new line before the pid child dies \n");
fflush(stdout);
return 0;
}
Same output:
>"Show me the new line before the pid child dies Show me the new line before the pid child dies"
>
How do I make it so I get the following output:
>"Show me the new line before the pid child dies \n"
> Show me the new line before the pid child dies \n"
>
Update:
If I run it as below, it shows me the correct lines (as I wanted):
Command on terminal / powershell
.\f.exe > f.out
Output:
1 Show me the new line before the pid child dies
2 Show me the new line before the pid child dies
3
I am changing my question then: Can I actually get the exact same output shown on my terminal (vs code / powershell), as I am getting on my f.out file?
I'm running MS Visual Studio on Windows.
Probably you got a racing condition, after the call to fork() both the parent thread and the child thread are trying to print simultaneously. The behavior in this case is undefined. The whole idea of homework is probably to teach you that. You should use some sort of inter thread communication, or simply wait in the parent process (or in child it does not matter).
if (fork()) sleep(1);
Update:
After carefully reading your output line I realised that your exercise is probably to learn to wait in the parent process until the child dies, this is achieved like this:
if (fork() != 0) wait(NULL);
Typical concurrency problem. Enforce correct multi-threaded behavior and eliminate race conditions with mutex locks and the like.
Per POSIX and ISO, stdout buffers output until it hits a newline or explicitly flushes, which takes time and the race condition bites you. If you're redirecting to a file, std behaves differently. The newline gets into the buffer but doesn't kick off a flush, while the explicit fflush will force it, so printf concludes and the children don't bump each other until the middle of fflush but your buffer is set up as you want by then. You probably shouldn't depend on that timing happening like that.
I'm having trouble understanding how to do basic piping in C. I looked at a couple other questions on this topic, and either they were for subtly different issues, or I'm so far off the mark on this subject I couldn't understand why the answers are good for my problem.
This program below is just a simple test I made, where I'm trying to get behaviour equivalent to typing "ls | grep a" into my shell. (I have a homework assignment where I have to build a shell that can handle piping, but this is my first step towards understanding pipes to even attempt the homework). I get the correct output, but the terminal prompt ends up appearing before the output, making it look like it did not properly terminate. Since this is connected to a shell homework, I'm worried that will impact the grade (and it just feels wrong to let it look like that anyway). Any advice?
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
int fdpipe[2];
pipe(fdpipe);
int f1 = fork();
if(f1 == 0)
{
close(fdpipe[1]);
dup2(fdpipe[0],0);
close(fdpipe[0]);
execlp("/bin/grep","grep","a",NULL);
}
else
{
close(fdpipe[0]);
dup2(fdpipe[1],1);
close(fdpipe[1]);
execlp("/bin/ls","ls",NULL);
wait(NULL);
}
return 0;
}
Here's an example of my terminal output.
1067: ls
a.out test.c test.cc
NathanE: ~/Desktop/playground
1068: ./a.out
NathanE: ~/Desktop/playground
1069: a.out
(The beginning of this line is where my cursor is)
What I'm expecting would be:
1067: ls
a.out test.c test.cc
NathanE: ~/Desktop/playground
1068: ./a.out
a.out
NathanE: ~/Desktop/playground
1069: (my cursor would go here)
The child process runs grep, while the parent replaces itself with ls. The wait(NULL) does nothing, because successful exec*() never return.
Because the control returns to the shell immediately after ls completes, the shell can display the next prompt before grep completes.
There are two approaches you can use to avoid this:
fork() both child processes, and wait() for them
Replace the process itself with the last process in the pipe chain
Either will ensure that control is returned to the shell only after the last process in the pipe chain completes.
I've run the following code :
#include <stdio.h>
#include <sys/types.h>
int main()
{
fork();
fork();
fork();
printf("hello\n");
return 0;
}
After printing the word "Hello" 8 times, the program is not exiting. What is the reason behind this?
This is an accidentally interesting way to calculate 2^3. The first fork makes the second fork happen twice and they each make the third fork happen twice and all 8 children run printf! The 8 processes do exit, but your prompt is lost in the noise.
As the commenters implied, you are fundamentally misunderstanding what fork() is and what it is doing. But along the way you made an interesting toy, so bravo!
In bash, when I type ls and hit enter, the binary ls will run and I will return to shell prompt again without doing anything from my side.
However this program, written in C will block:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t other = fork();
// other will be 0 for the child process
// other will be the childs process' value in the parent process.
switch(other) {
case 0:
printf("%s %i\n", "I am the child process!", other);
execl("/bin/ls","ls",NULL);
return 0;
default:
printf("%s %i\n", "I am the parent process!", other);
return 1;
}
}
Why?
The output is as follows:
Korays-MacBook-Pro:~ koraytugay$ ./a.out
I am the parent process! 40309
I am the child process! 0
Korays-MacBook-Pro:~ koraytugay$ AndroidStudioProjects Movies happyko koray.i
Applications Music hello.c koray.o
ClionProjects Pictures hello.sh koray.s
Code Public innbound mssql
Desktop TheElementsFiles innbound-pf nono.txt
Documents VirtualBox VMs innbound_usage.log svn-key
Downloads a.out k.txt tugay.c
IdeaProjects asm.asm klinnck webtoolkit
Koray.class asm.hack klinnck-pf
Koray.java cexamples koray.a
Library fifa.sql koray.c
At this point I will need to hit Enter so that I return to bash prompt. Why?
At this point I will need to hit ENTER so that I return to bash prompt.
Actually, you're already back to the prompt, you just did not realize it.
To elaborate, the problem you facing here is, the parent does not wait for the child to exit and returns beforehand the child finishes execution. So, the shell prompt comes back, and then the output from the chlid process (output of ls) gets printed on the output.
If you notice properly, You've already got the prompt back, and your output appears later.
Korays-MacBook-Pro:~ koraytugay$ ./a.out
I am the parent process! 40309
I am the child process! 0
****Korays-MacBook-Pro:~ koraytugay$***** AndroidStudioProjects Movies happyko koray.i
Applications Music hello.c koray.o
ClionProjects Pictures hello.sh koray.s
Code Public innbound mssql
Desktop TheElementsFiles innbound-p
Above, please note the **** marked line. There, you got your shell prompt back.
At this point I will need to hit Enter so that I return to bash.
Except no, you're already in bash. But all that ls output after the prompt makes it seem that you are not. Go ahead, try another command.