Reputation: 75
can somebody explain why the parent process is always finished completely before the while loop in the child process is started even if I let the parent sleep.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
int i = 10000, pid = fork();
if (pid == 0) {
while(i > 50) {
if(i%100==0) {
sleep(20);
}
printf("Child: %d\n", i);
i--;
}
} else {
while(i < 15000) {
if(i%50==0) {
sleep(50);
}
printf("Parent: %d\n", i);
i++;
}
}
exit(EXIT_SUCCESS);
}
The output looks like this:
Parent: ..
Parent: ..
Parent: ..
until Parent is finished, then the same for Child.
Can the reason be that I'm testing on a single core CPU? Would the result change if I had a multi core setup? The sleep(50) is definitely working - since it takes ages until the script is finished - why does the CPU not switch the processes? Are there situations like for example during a while loop where the process has "more" or exclusive rights to the CPU?
Thanks for help. :)
Upvotes: 5
Views: 2817
Reputation:
Are there situations like for example during a while loop where the process has "more" or exclusive rights to the CPU?
Well, that's not defined, but it would be insane.
I can't reproduce your problem. Reducing the sleep times to 2
and 5
seconds (so I don't have to wait forever), the child gets unblocked first here, as one would expect. (Debian 8, Linux 3.16.1-ck1 [BFS scheduler, non-standard] on AMD64)
I'd say your scheduler behaves very weird and might be just broken. But that being said, it's never a good idea to rely on any specific behavior of a scheduler. Always assume it's broken and insane as hell -- if your code allows a specific sequence of execution, there will be a scheduler insane enough to choose it.(*)
Therefore, use synchronization primitives (semaphores
and mutexes
for example have shared versions for usage with different processes -- you could also just use pipes
in some scenarios) whenever you need to rely on some synchronization.
edit: adding two examples for synchronizing the processes.
First a version (ab)using pipe
s:
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
int i = 10000;
int parent_done[2];
int child_done[2];
char dummy[1] = { 0 };
int pid;
pipe(parent_done);
pipe(child_done);
/* stdio buffering would lead to intermingled output */
setvbuf(stdout, 0, _IONBF, 0);
pid = fork();
if (pid == 0) {
close(parent_done[1]);
close(child_done[0]);
while(i > 50) {
if(i%100==0) {
if (i < 10000) write(child_done[1], dummy, 1);
read(parent_done[0], dummy, 1);
}
printf("Child: %d\n", i);
i--;
}
} else {
close(parent_done[0]);
close(child_done[1]);
while(i < 15000) {
if(i%50==0) {
write(parent_done[1], dummy, 1);
read(child_done[0], dummy, 1);
}
printf("Parent: %d\n", i);
i++;
}
}
exit(EXIT_SUCCESS);
}
Then the same using POSIX semaphores (which is IMHO much cleaner because semaphores are meant to be used for synchronization):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <unistd.h>
struct semaphores
{
sem_t child_done;
sem_t parent_done;
};
int main(int argc, char **argv) {
int i = 10000;
int pid;
/* map shared memory for the semaphores */
struct semaphores *sems = mmap(0, sizeof(*sems), PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
/* initialize both semaphores as "shared" and with an initial count
* of 0 */
sem_init(&sems->parent_done, 1, 0);
sem_init(&sems->child_done, 1, 0);
/* stdio buffering would lead to intermingled output */
setvbuf(stdout, 0, _IONBF, 0);
pid = fork();
if (pid == 0) {
while(i > 50) {
if(i%100==0) {
if (i < 10000) sem_post(&sems->child_done);
sem_wait(&sems->parent_done);
}
printf("Child: %d\n", i);
i--;
}
sem_post(&sems->child_done);
} else {
while(i < 15000) {
if(i%50==0) {
sem_post(&sems->parent_done);
sem_wait(&sems->child_done);
}
printf("Parent: %d\n", i);
i++;
}
sem_post(&sems->parent_done);
}
exit(EXIT_SUCCESS);
}
Windows has a different API for semaphores, see Semaphore Objects on MSDN
(*) edit2 fits here: While creating the examples, I noticed stdio
buffering gets in the way without sleeping. So maybe it's not even your scheduler behaving badly but just an implementation of stdio
with very unpredictable behavior on when to flush buffers. That's just wild guessing of course. What you have to know is: All FILE
handles in C are buffered by the stdio
part of the C library. This includes the predefined stdin
, stdout
and stderr
handles. Consequence is that what you see in your output doesn't necessarily reflect the sequence in which different threads or processes created that output. Unless, of course, you disable buffering completely like done in my example snippets.
Upvotes: 3