Midipix: Posix for Windows
midipix.org
midipix.org
For one or the other reason, fork() has become in many discussion of posix on windows something of a fetish. The interface surely has its place, and figuring out how to efficiently implement it took a huge amount of effort, yet the vast majority of applications do not truly need it. Matter of the fact is that even on linux, where fork(2) is natively supported, the sequence fork+execve is more costly than clone+execve (where clone's flags are CLONE_VM && !CLONE_THREAD). For additional reference, see for instance the implementation of posix_spawn in musl libc (http://git.musl-libc.org/cgit/musl/tree/src/process/posix_sp...).
If high performance of the posix layer were not possible the project would have not existed. Among the factors that make high performance possible are 1) direct use of kernel interfaces (aka the Native API, where most of the runtime layer is written as a user-space driver), 2) utf-8 as the primary supported multibyte encoding as a foundational concept rather than an afterthought, and 3) tls implementation that matches in speed the native tls facility.
As for fork: believe me, I'd be happy if, as as side effect of this work, more applications used posix_spawn. But Cygwin can also support an efficient posix_spawn implementation: https://github.com/dcolascione/cygspawn
A lot of the complexity actually comes from mapping POSIX filesystem semantics to Unix ones. stat(2) in Cygwin is ungodly expensive for this reason. Your layer won't be able to avoid this work without providing fewer features.
https://twitter.com/RichFelker/status/602313644026761216
There are still plenty of applications that use fork semantically, to keep the same process image in the child, which benefit somewhat from a fast fork. But most places where fork affects performance now are things that are already pessimized by using fork+exec instead of posix_spawn: the shell, make, cgi, etc. (GCC still uses vfork, but GNU make recently switched from vfork to fork because of vfork-related bugs.) Regardless of how fast or slow fork is on midipix (but I expect it to be fairly fast, much faster than cygwin), they would benefit a lot more from just switching to using posix_spawn.
how about this one?
fopen + mmap + unlink
Afaik windows APIs forbid deleting mmaped files.
open rw, set length, mmap from two 2 processes to have shared memory, unlink & close.
expected behavior is to have a chunk of shared memory without the tempfile in the directory tree
Here's how it works:
Parent initializes a space in the Cygwin process table for child. Parent creates child suspended using Win32 CreateProcess call, giving the same path it was invoked with itself. Parent calls setjmp to save its own context and then sets a pointer to this in the Cygwin shared memory area (shared among all Cygwin tasks). Parent fills in the child's .data and .bss subsections by copying from its own address space into the suspended child's address space. Parent then starts the child. Parent waits on mutex for child to get to safe point. Child starts and discovers if has been forked and then longjumps using the saved jump buffer. Child sets mutex parent is waiting on and then blocks on another mutex waiting for parent to fill in its stack and heap. Parent notices child is in safe area, copies stack and heap from itself into child, releases the mutex the child is waiting on and returns from the fork call. Child wakes from blocking on mutex, recreates any mmapped areas passed to it via shared area and then returns from fork itself.
EDIT: Oh, I'd skipped the part above "here's how it works"... that just says exactly the same thing I said above.
I believe I had trouble executing any instructions in the new process at all. If you can make it work I'd like to see your code, otherwise I'm skeptical.
Anyways the cygwin guys claim to have forked processes with ZwCreateProcess, but just had problems with getting it to work with the win32 subsystem: http://www.cygwin.com/ml/cygwin-developers/2011-04/msg00034....
Also this guy seems to have managed to do it: http://stackoverflow.com/questions/10657699/cant-use-createp..., but it hang when he called a win32 function (CreateProcess) from the child.
Keep in mind that the whole _point_ of Cygwin is to interact with the Windows world. If you want a POSIX sandbox, you can use a VM with fewer headaches and better performance.
For me, Cygwin has always been pretty much native-speed, its only an API translation layer.
Keep in mind that in addition to the build steps, it's common to break out to sed, grep, and shell to get basic string operations done due to extremely limited capability of make itself. On cygwin, this is slow for large projects.
Cygwin's forking is also temperamental.
https://cygwin.com/cygwin-ug-net/highlights.html#ov-hi-proce...
"In summary, current Windows implementations make it impossible to implement a perfectly reliable fork, and occasional fork failures are inevitable."
I wonder how the authors of midipix propose to resolve the listed issues.
One way to really see the latter is to try to create a git clone using the --reference option against a repo on a mapped drive; msysgit using the drive letter is OK, but cygwin git against the same repo via /cygdrive/mapped is very slow.
Edit: by 'everything that works' I mean everything that interacts with the terminal in a standard way. There are some command line utilities which output to the terminal some way other than stdout, making them very difficult to use in cygwin.
Running scripts?
Also, try enumerating the files in a large directory hierarchy and compare it with native Windows speed (or Linux speed), it's not even comparable.
Personally tho, speed has always been sufficient for me to never notice. I use it most days, but I don't do comparisons.
It's not a question of a 40% speed difference, more like a > 400% speed difference last time I checked.
It's really a high-level design decision: Applications originally designed for *nix will use fork(), while those for Windows will use something else - maybe threads, maybe CreateProcess().
Which leaves the question how much POSIX can be implemented on top of WinRT.
Similarly not all POSIX calls are allowed inside Mac OS X App Sandbox.
And if we restrain ourselves to POSIX there is little more than command line applications, TCP/IP headless servers and Motif GUIs.
Microsoft would still let you run 16-bit DOS apps on Windows if Intel hadn't dropped compatibility from their 64-bit processors. Both of us will be dead and buried before Win32 goes away.
> Similarly not all POSIX calls are allowed inside Mac OS X App Sandbox.
Except that WinRT as well as OS X Sandboxed App are all unpopular and therefore unlikely to replace the regular apps anywhere near in the future.
For me POSIX is a kind of unofficial C runtime.
Now with systems moving beyond C, POSIX matters much less.
To be transparent, I am sort of worried that the authors have told us in this thread that they think the number of applications that "really need" `fork` is small, and that the discussions about POSIX subsystems on Windows are overindexed on discussions about `fork` without justification.
The truth of the matter is that the semantics of `fork` infect every API that creates process state. Every library, every syscall that creates process state will have a clear answer for what happens when a process that is using that bit of process state calls `fork`. This includes file and socket management, IPC stuff, threads, signals, and so on. To make `fork` work properly on Windows, you absolutely need to replicate what UNIX does here, or you will break a lot of apps, because make no mistake, a lot of apps depend on these semantics to work correctly. `fork` is not just a function that occasionally gets called and sometimes needs to be fast for people who are calling it. It is core to the semantics of the POSIX API, and if you don't treat it as such you are in for a bad time later.
Perhaps more worrying, though, is that there is a serious impedence mismatch between the UNIX and Windows models of asynchrony. Particularly in the case of sockets and signals, this difference is immense, and I am extremely skeptical we will find a good (or even close to production-worthy) solution in usermode. Maybe these good folks have found something I have missed; to me the approach just seems doomed.
And of course, this is all just the start of the problem. It is a much longer trudge to solve the "real" problem, which is immense. The original Interix POSIX subsystem in NT 3.5 (I'm told) had a `fork` that was just barely enough to pass the 1003.1-1990 validation suite, and fell over quickly if you pushed much harder. But they didn't have to mess with the `fork` implementation to turn that 67kloc into the more robust and conformant SFU 3.x; it was mostly a long tail of things extrernal to fork that just needed to be ironed out.
On top of that, to really have `fork` interop, you would likely need to redefine every Win32 API so it did the "right thing" under UNIXy things like signal delivery and fork, and that is a truly, terrifyingly tall order.
The first scenario, in the spirit of traditional unix, consists of the sequence fork+execve. This scenraio is supported in midipix, yet requires that the (optional) subsystem performs execve on behalf of the forkee. As noted earlier, if all the child does is call execve, then application authors should consider switching to posix_spawn not only due to its better portability, but also for performance reasons.
The second, and arguably more interesting scenario, takes place when the child brnaches away from the parent process, performs (in parallel) a designated portion of a larger task, and then exits. This second scenario has been thoroughly tested and works as expected with respect to IPC, I/O, signal delivery, and nested forking.
In either of the above scenarios, there is no expectation that the child could interact with csrss, create or operate on GDI handles, or otherwise use the WIN32 API directly. Then again, it is expected that a process that calls fork() will not have any of the GUI libraries loaded at the time the call is made. Any limitation here, if any, has little to no relevance to existing applications that depend of fork, but only to what I fondly call the "fetishization of fork" in discussions about posix-to-windows portability. Applications that truly depend on fork were written with neither WIN32 nor GDI in mind, and therefore will not break due to such windows-specific interfaces not being available (as an aside, an X11 application calling fork() from within its event loop is an equally bad design regardless of whether the operating system supports that or not).
Looking forward to further cooperation! I hope this addresses at least some of the questions you have raised.
In any case, I welcome a posix interface for windows, it would be a cool tool to have, especially in the case of cross-platform utilities.
Aside from that, Cygwin tries to hard to be a complete Unix environment on Windows, whereas midipix just gives you enough to use interfaces that were standardized in POSIX as a reasonable, uniform API for all operating systems to provide. Some functions go beyond that, but you don't have to use them. And even some things that are mandatory in POSIX are optional in midipix; as I understand it, you can choose at build time whether you want the overhead of being able to support tty devices (and the associated semantics like job control, signals from the controlling tty, etc.).
Cygwin works fine with multiple installations these days [https://cygwin.com/faq/faq.html#faq.using.multiple-copies]
The thing that wont work is if you try to mix and match a dll from one installation with binaries from another, but I think you can agree that that situation is fair. You just need your paths setup correctly.
Cygwin and MingW don't evoke any existing meaning, so I think they're somewhat more memorable.
Cygwin is just a DLL for API translation with an associated package manager (Though many people think its more heavyweight than that and consequently dont like it).
- It's GPL licensed, or you buy a license from RedHat; either might be too onerous for the people who develop midipix
- You can only have one cygwin1.dll in memory at a given time; If you have two cygwin using programs, they must both use exactly the same version of the cygwin1.dll; Which means that you can't just distribute a self-contained cygwin program and expect it to work.
- It's a bit clunky at the edges, with the mounts (it looks like you have a /usr directory on the root, and you can see it with "ls" or cygwin "dir" but not cmd "dir", for example; user integration is a bit clunky).
I'm not sure midipix will be better - some problems are inherent. However, cygwin was designed around Win95/NT4 deficiencies some 20 years ago. It has evolved very gracefully, but it's possible a modern version without all that legacy will work better.
but... multiple versions of Cygwin can coexist these days. It did used to be an issue, granted. tho these days quite a few programs distribute their own cygwin dll and tools.
I agree, licensing may be an issue for some people.
If you really need decent POSIX OS on Windows just use virtualiztion.
But yeah, there are a lot of people on HN who constantly post about how their work issued them a cat and they have all these tools they install on their cat to make it act more like a dog but it makes a really terrible dog anyway and so cats must be crap. Uh, it's a cat. If you need a dog, go get a dog. Or run a dog on Hyper-V if you need a dog and a cat at the same time. My favorite is when people talk about how their favorite scheme they use to get their dog to catch mice doesn't work for their cat, therefore it's evidence that cats are terrible at catching mice.
I am too, but I wonder whether it is even a relevant goal anymore. It's one of those things that has always been a topic of conversation, and has never really happened in a way that a mass of end users has adopted. Everyone who ever ran a java desktop app on Windows back in the day probably has some theory of why, but I think it was mostly because there was never really a strong need on the user side, despite the idea being so attractive to devs.
Now... I don't even know what the world of work is going to look like in ten years... Android? Windows? Linux? Mac? Tablet? Phone? Laptop? All of the above and more, I assume, and the whole interface portability thing seems to be heading down the exact same road that it headed down on the desktop, i.e. either write native or accept some lesser solution.
The Windows kernel is based on the VMS kernel, right?
And NTFS is based on the VMS filesystem?
http://en.wikipedia.org/wiki/David_cutler
I would have been happy with VMS on the PC.
Instead we got Windoze. How much of our lives has this monstrosity wasted? Just let it die.
Do daemontools' supervise and svscan need fork()?
Or UNIX became the embodiment of the concept of "run anywhere"?
Also, POSIX has a number of optional features, like realtime and queued signals. Which features exactly will be supported?
Building the cross-compiler requires a native compiler and a shell environemnt that is capable of building gcc. The build process is trivial, and thus far has been tested on several Linux flavors (just asked that this also gets tested on BSD and OSX). Building gcc in an msys/cygwin environemnt is always a bit more tricky, so we have not spent much time trying that. Instead, we are working hard to become self-hosted as soon as possible.
+ building the cross-compiler:
git clone git://midipix.org/cbb/cbb-gcc && cd cbb-gcc && ./cbb-midipix-cross-gcc.sh
yep, that's that. This will use $HOME/temp as a temporary folder, and install the toolchain to $HOME/midipix. Make sure you add $HOME/midipix/bin to your path in order to use the toolchain. As with all other gcc builds, you need to have the usual dependencies on the build system (gmp,mpfr,mpc,libelf,texinfo) and a working shell environment.
+ pre-pre-alpha, radical changes: some major changes to the toolchain are underway. If you find it hard to believe that building the cross-toolchain is that easy please go ahead and run the above commands from a nearby shell, but please also rebuild it in a day or two (look for a commit message mentioning "automatic creation of GOT entries" :))
+ as a kind reminder, cross-compiling is just for building applications; testing them can only be done with the runtime library, which is not out yet.
You can tune this a bit with fsutil and turn off last access time, 8dot3 filenames and change mftzone size and it'll be significantly faster.
ReFS is a little better in this respect. I suspect this was a hang-back from early NT versions which were tuned to larger binary blobs as a normal file statistic rather than lots of small text files.
Old but good. fsutil wraps all the registry poking for reference.
But why?
If a subset of those platforms is Linux + (Commercial) Unixes + Windows, the Windows part is going to take twice as long and cost you thrice as much to get in line with the other 2... unless you have some clever solution like the one proposed here.
Tomato, tomato!