The vast majority of PHP code — WordPress plugins, Laravel packages, every PDO::query() and file_get_contents() ever written — uses blocking I/O. Swoole’s coroutines can’t help with code that doesn’t yield. FrankenPHP and RoadRunner use the same worker-count-limited model as fpm.
Qbix takes a different approach: run many workers. The server loads your entire framework into a parent process, then calls pcntl_fork() to create workers. The kernel marks the parent’s pages copy-on-write. Workers share every loaded class — they only pay for pages they actually write to during the request.
A WordPress-like request dirties 30 pages = 120KB on Linux. So 200MB doesn’t buy 4 workers (like fpm) — it buys thousands. Each one blocks on its database query, and that’s fine. Blocking I/O doesn’t matter when you have enough workers. COW is what makes “enough workers” cost 47MB instead of 16GB.
This is pure userland PHP. No kernel module, no C extension, no custom allocator. Just pcntl_fork() after loading everything, and the OS does the rest.
We proposed this for PHP core as switch_global_context(). While that works its way through the RFC process, the server does it in userland today.