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!
Related
I'm trying to write a C program that restarts itself once. My approach was to fork and in the child process call execve. I thought I would see Hello twice printed. This here prints main twice, but even so if I comment out execve, so I assume I'm not correctly using execve here. The binary is called "restartOnce".
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(int argc, char *argv[]) {
pid_t pid = fork();
printf("Main.\n");
if (pid == 0) {
char *argv = "hello"; // To silence gcc compiler warning.
char *env = "world"; // To silence gcc compiler warning.
execve("restartOnce", &argv, &env);
} else {
wait(NULL);
printf("Done.\n");
}
return 0;
}
I'm not completely sure what you're trying to accomplish, but I see two things to attend to. We have to get the execve() call right, plus we have to avoid a fork bomb, and this code should do it.
There are lots of ways to avoid a fork bomb, but I chose to set something in the environment, and when the child sees this environment variable, it will know not to continue.
So execve() requires its own argv, which is an array of pointers to individual strings (with a NULL pointer at the end), and it's entirely legit to pass the same parameter you got from main.
The third parameter is like argv, but it's a list of environment variables instead of command line arguments. It has things like TERM=vt100, PATH=/bin/..., and many others. Though you can fetch environment variables individually with getenv("PATH"), Unix/Linux systems provide an argv-like variable environ.
This variable is declared in <unistd.h> but requires the _GNU_SOURCE macro to be defined to expose it.
This done, you can call safely call execve this way:
#define _GNU_SOURCE
#include <unistd.h>
...
execve("restartOnce", argv, environ);
To avoid a fork bomb, one of the first thing the program does is look for the environment variable FORKBOMB - if this is set, we're in the child process and should stop forking other children, perhaps doing something else instead (the real work of the program?)
But if we're in the parent - no variable seen - we actively set the variable by putting it in our own environment that's seen by the child processes: putenv("FORKBOMB=no");
A minor addition: the very first thing the program does is report that it's started up, and it provides its own process ID just so you know what it's doing.
Maybe I misunderstand the problem, but this is how I'd solve what I imagine you're asking:
#define _GNU_SOURCE // for "environ"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(int argc, char *argv[])
{
printf("Main in pid %d.\n", getpid() );
if (getenv("FORKBOMB") != 0)
{
printf("**We're in the second child, no more forking\n");
exit(0);
}
putenv("FORKBOMB=no");
pid_t pid = fork();
if (pid == 0) {
execve("restartOnce", argv, environ);
} else {
wait(NULL);
printf("Done.\n");
}
return 0;
}
There are quite a few other ways to avoid the fork bomb, but this is as good as any to get the idea across, and others might chime in with their favorites.
EDIT But as I think about this, I'm pretty sure this should not be necessary no matter what you're trying to accomplish unless this is just a learning exercise.
The two things I can imagine are:
1) You need to detach from the parent process so you can run in the background, like a daemon process. In that case, you don't have to exec, just do the daemon work in the child process.
2) If the program has altered itself - perhaps downloaded an updated version of itself, then it does have to call exec to get the new executable, but a fork() would not be required.
So I'm not sure we (or at least I) know what you're trying to accomplish.
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...
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.
Studying for finals, I got stuck on this problem. They ask how many times the following code executes printf:
#include "csapp.h"
void doit() {
Fork();
Fork();
printf("hello\n");
return;
}
int main()
{
doit();
printf("hello\n");
exit(0);
}
The solutions say that it printf executes 8 times, but I cannot figure out why. I've been trying to draw the picture of what's going on in the code, but my pictures make it seem like it only execute 4 times.
2 forks - Four processes. Each process has two printfs with hello (one in main and one in doit - hence 8.
Can you help me with this?
How many times are executed the programs "exam" and "students" invoked by execl? I think the correct answer is 8 runtimes for program "exam" and 0 for "students", because in the two first forks will be created 3 child processes, after that in loop the first fork() will create more 4 processes, since the three children already created also will run this code, thereafter we have a exec that will replace the current code of the 7 processes created and of the actual program and run it(program "exam") 8 times. My reasoning is correct?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
int main(){
int i;
pid_t
pid=fork();
pid=fork();
for(i=0;i<5;i++){
pid=fork();
execlp("exam","exam",NULL);
if(pid==0){
break;
}
}
execlp("students", "students","sistcomp",NULL);
return 0;
}
Theoretically, you are right. Let me draw a diagram to explain:
+---1 ...
|
+---+---2 ...
|
----+---+---3 ...
fork()|
+---4+---- execlp("exam","exam",NULL);
fork() |
+---- execlp("exam","exam",NULL);
^
after the first two fork()
you see, after 2 fork(), we get 4 process. Take the No.4 as an example, it enter for loop and fork() again, then we get another child process here, this child process and its father will exec execlp("exam","exam",NULL); as you see this will replace the current code.The same is true for No.1, No.2 and No.3.
So, it will be 8 runtimes for program "exam" and 0 for "students".
But, when you run this code, runtimes for program "exam" may be 7 or 6, it may be caused by Copy-on-write(I am not pretty sure about this)
PS:
It is a good practice to use execlp like this:
if (pid == 0)
execlp("exam","exam",NULL);
or
if (pid != 0)
execlp("exam","exam",NULL);