skip to Main Content

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 $in";
        sleep(1);
        print "In child $in";
        exit($i);
    } else {
        print "Parent: forked $in";
    }
}

while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo "Child $status completedn";
}

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 – nproc prints "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.

2

Answers


  1. Chosen as BEST ANSWER

    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.

    php -dxdebug.remote_enable=0 
        -dxdebug.default_enable=0 
        -dxdebug.remote_autostart=0 
        script.php
    

  2. Running this on a native Linux box (i3-6100)….

    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 3 completed
    Child 4 completed
    Child 5 completed
    Child 1 completed
    Child 2 completed
    

    I would have been surprised if it behaved exactly as you predicted. When a process forks, it creates 2 runnable processes….as to which gets scheduled first….with the CFS they are likely to stay associated with the same core….but it will still be effectively random. And PHP does have a lot of work to do (including stuff which will result in yielding the CPU) when it starts – even though the fork should mean that most of this can be shortcutted. Then there is the sequence in which buffers get flushed to the tty.

    Having said all that, your results are a very long way from your expectation. This appears to be a result of the MS-Windows scheduler (which IME is very lumpy).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search