1

Just had a question about using fork(). Let's say we decide to fork() 3 times, thus having a total of 8 processes.

int main() {
 fork();
 fork();
 fork();
 printf("First print statement.\n");
 printf("Second print statement.\n");
 return 0;
}

In this case, I know that we have 8 different processes, namely A, B, C, D, E, F, G, H. Now, I know they can run in any order and so A could run first, but then E could run, then B, and so on. My question is, without using wait(NULL), and just having the code like this, does a process HAVE to finish running before another one runs? So, does the output have to be

// Process A
First print statement.
Second print statement.

// Process E
First print statement.
Second print statement.

// Process B
First print statement.
Second print statement.

// etc...

Or can we have something like:

// Process A
First print statement.
Second print statement.

// Process B
First print statement.

// Process E
First print statement.

// Process D
First print statement.

// Process B
Second print statement.

// Process D
Second print statement.

// etc...
nalabof679
  • 59
  • 1
  • 6
  • 3
    All the processes run concurrently. Multiple CPUs and timesharing allow them to be interspersed in any way. – Barmar Oct 13 '22 at 20:55
  • Got it, and even if I added a `wait()`, the only thing it waits for is the parent process (or child process) to finish, and then it can go at anytime after that by that logic. So the 2nd output is correct? – nalabof679 Oct 13 '22 at 20:56
  • 1
    Just think about it. When you run a command in the background from the shell, the shell keeps running and allows you to run other commands, without waiting for the background process to finish. – Barmar Oct 13 '22 at 20:59

4 Answers4

1

The processes run concurrently.

wait() will wait for the next process or waitpid() for a particular process. It's a synchronization primitive and allows a parent process to obtain a status of the terminating child.

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • So let's say `B` is a child process forked off of `A`, and I make `B` wait() for A to complete. Does B HAVE to follow right after A is done, or can it run anytime after A, maybe even after C D E F etc. – nalabof679 Oct 13 '22 at 20:59
  • 2
    @nalabof679 You can only wait for a child. A can wait for B, but B can't wait for A. – Barmar Oct 13 '22 at 20:59
  • Ah got it, sorry for the misconception. So from taking into your other comment, I assume that it doesn't have to follow immedietely, but it can go perhaps after `C` and `E`. – nalabof679 Oct 13 '22 at 21:01
  • Note: https://stackoverflow.com/questions/23586682/how-to-use-printf-in-multiple-threads – Allan Wind Oct 13 '22 at 21:02
  • How many cores do you have? If you don't have enough work the OS may not spread your processes to different cores as it be will faster to interleave on maybe just a single core. – Allan Wind Oct 13 '22 at 21:08
  • 8 cores - so in reality, all combinations should be possible as long as any parent process waiting for a child process finishes executes AFTER it? So in reality, it shouldn't just be 2 different outputs. That point about the OS not putting a process on different cores makes sense – nalabof679 Oct 13 '22 at 21:18
1

No, it does not necessarily HAVE to finish running before another one takes the CPU. It depends on CPU scheduling.

1

Now, I know they can run in any order and so A could run first, but then E could run, then B, and so on.

Couching it in terms of the processes running in any order is not wrong, exactly, but it seems to be giving you an incorrect impression. Immediately after a process successfully fork()s, the parent and child are both running, at the same time. What can vary is when the instructions in each process are scheduled for execution on a CPU, and thus, when any particular behavior of either process manifests relative to the behaviors of the other. Just like for any other concurrently running processes.

My question is, without using wait(NULL), and just having the code like this, does a process HAVE to finish running before another one runs?

No. Roughly speaking, all live processes are eligible to get CPU time, and the system tries to spread the available CPU time around to all of them as best it can. If you have multiple processing units available, as is common these days, two or more of the processes in your group might execute instructions at literally the same time. No matter how many processing units you have, any given process is usually limited to a certain amount of CPU time before it is preempted to allow other processes to run. wait()ing has little to do with any of that.

Output like your second example is possible.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

The output can be in any order, with the only restriction that, for each process, the first statement (the first printf()) should be executed before the second. The actual output normally shows like some processes have some kind of preference, but this is a consequence of the way the kernel arranges to schedule them, and the actual number of cores, and contexts switches each codre is allowed to do. You can observe a completely different approach, if you redirect stdout to a file, or pipe it to another process, as in this case, stdout turns into total buffering and every process complete output will happen together (as the write(2) system call is not done, but until the process calls exit(2) after returning of main(), and then the full buffer is output in one system call. But this is related on how stdout flushes it's buffers (on a tty output device it flushes output at each \n char, while on a file or a pipe, it flushes only if buffer gets completely full ---which never happens with the amount of data you are printing) and not on scheduling behaviour of the kernel.

In your case, the eight processes just block at the first write(2) (well, the eight not, only seven, the other got the lock and is executing the write system call) of the first process that calls printf(3), as while the write is being performed (in the system), the output device inode is locked in the kernel, and no one can enter to write(2) on a locked inode, so is is very probable that, when the process returns from write(2) it starts it's second write(2) (in the second call to printf(3), and getting the lock again, if no other process has had a chance to get the lock in the time between both system calls). But there's no reason for the order to be different, or in a loadad system, for the kernel to decide to allow other different process to run before the one that was running before. Think that a process is not preempted, but only when returning from kernel mode, and only if other process has acquired more priority (something that happens slowly) and is free to run (this will probably not happen to the other processes that are blocked to get the inode of the output device, and this requires them to be scheduled)

The only case in which interspersed output can be observed, is that, while the process with the lock is about to return to user mode, the kernel schedules one of the other processes that have been awaken from the inode lock, and gets the lock, but if this process is not preempted (in a multi processor system) it is very difficult for that processor to get the inode lock before the one that was running already (there's a very small window for that to happen, but it can be possible)

This is the output I get (on FreeBSD, with a CoreDuo processor ---two cores, I've modified your example to show the pids of the authors of the printf() calls to show that there's a big chance that the process that print):

$ pru
[33781]: First print statement.
[33782]: First print statement.
[33787]: First print statement.
[33787]: Second print statement.
[33784]: First print statement.
[33784]: Second print statement.
[33788]: First print statement.
[33788]: Second print statement.
[33785]: First print statement.
[33785]: Second print statement.
[33786]: First print statement.
[33786]: Second print statement.
[33783]: First print statement.
[33783]: Second print statement.
[33782]: Second print statement.
[33781]: Second print statement.
$ _
Luis Colorado
  • 10,974
  • 1
  • 16
  • 31