I understand how fork() works at a high level and the pcntl_fork() wrapper, but for some reason in my environment PHP only runs two child processes at a time. Take for example this simple code:
<?php
for ($i = 1; $i <= 5; ++$i) {
$pid = pcntl_fork();
if ($pid === -1) {
print "Could not fork!\n";
exit(1);
}
if (!$pid) {
print "-In child $i\n";
sleep(1);
print "In child $i\n";
exit($i);
} else {
print "Parent: forked $i\n";
}
}
while (pcntl_waitpid(0, $status) != -1) {
$status = pcntl_wexitstatus($status);
echo "Child $status completed\n";
}
The output I expect is something like this with total time around 1 second:
Parent: forked 1
-In child 1
Parent: forked 2
-In child 2
Parent: forked 3
-In child 3
Parent: forked 4
-In child 4
Parent: forked 5
-In child 5
In child 1
In child 2
In child 3
In child 4
In child 5
Child 1 completed
Child 2 completed
Child 3 completed
Child 4 completed
Child 5 completed
But what I actually get is this, with total execution time around 3.5 seconds:
Parent: forked 1
Parent: forked 2
Parent: forked 3
Parent: forked 4
Parent: forked 5
-In child 2
-In child 1
In child 2
Child 2 completed
-In child 3
In child 1
Child 1 completed
-In child 4
In child 3
Child 3 completed
-In child 5
In child 4
Child 4 completed
In child 5
Child 5 completed
So it appears that only two child processes are actually running at any given time. I can't find any explanation for this behavior...
When running the test on a production system which is Docker on a native Linux host I get the expected result, but why can't I reproduce it with the exact same container on my WSL2 host?
System Information
- I'm running this test in a very recent version of Docker Desktop via WSL2 on Windows 11.
- My system has 12 CPU cores (20 with hyperthreading -
nprocprints "20" from both the WSL2 hostand from inside the docker container). - I have not changed the defaults in Docker Desktop or WSL config or used any resource control flags on the Docker container so CPU should not be restricted in any way.
So after running the code with the expected result on a simplified Docker image with only PHP and pcntl installed, I discovered the behavior is related to XDebug.
I had PhpStorm open and listening for debug connections (with no breakpoints set) so I believe what was happening was each child process was connecting to the debugger and the debugger can only handle two connections at a time. Either that or it has something to do with the connection established by the parent process being closed in a child process.
So if you are using pcntl and XDebug together, expect weird results.. Turning off the debugger in my IDE or disabling XDebug via PHP CLI yields the expected results.