|
The return of syslets
[Posted May 30, 2007 by corbet]
Things have been quiet on the syslet/threadlet/fibril
front for some time. Part of the reason for that, it would seem, is that
Ingo Molnar has been busy with the completely fair scheduler work and has
not been able to get back to this other little project. This work is not
dead, though; instead it has been picked up by Zach Brown (who came up with
the original "fibril" concept). Zach has released an updated patch bringing this
work back to the foreground. He has not made a whole lot of changes to the
syslet code - yet - but that does not mean that the patch is uninteresting.
Zach's motivation for this work, remember, was to make it easier to
implement and maintain proper asynchronous I/O (AIO) support in the kernel. His
current work continues toward that goal:
For the time being I'm focusing on simplifying the mechanisms that
support the sys_io_*() interface so I never ever have to debug
fs/aio.c (also known as chewing glass to those of us with the
scars) again.
In particular, one
part of the new syslet patch is a replacement for the
io_submit() system call, which is the core of the current AIO
implementation. Rather than start the I/O and return, the new
io_submit() uses the syslet mechanism, eliminating a lot of
special-purpose AIO code in the process. Zach's stated goal is to
get rid of the internal kiocb structure altogether. The current
code is more of a proof of concept, though, with a lot of details yet to
fill in. Some benchmarks have been posted,
though, as Zach says,『They haven't wildly regressed, that's about as much as can be said
with confidence so far.』 It is worth noting that, with this patch,
the kernel is able to do asynchronous buffered I/O through
io_submit(), something which the mainline has never yet supported.
The biggest area of discussion, though, has been over Jeff Garzik's
suggestion that the kevent code should be integrated with syslets. Some
people like the idea, but others, including
Ingo, think that kevents do not provide any sort of demonstrable
improvement over the current epoll interface. Ulrich Drepper, the glibc
maintainer, disagreed with that assessment,
saying that the kevent interface was a step in the right direction if it
does not perform any better.
The reasoning behind that point of view is worth a look. The use of the
epoll interface requires the creation of a file descriptor. That is fine
when applications use epoll directly, but it can be problematic if glibc is
trying to poll for events (I/O completions, say) that the application does
not see directly.
There is a single space for file descriptors, and applications often think
they know what should be done with every descriptor in that space. If
glibc starts creating its own private file descriptors, it will find itself
at the mercy of any application which closes random descriptors, uses
dup() without care, etc. So there is no way for glibc to use file
descriptors independently from the application.
Possible solutions exist, such as giving glibc a set of private, hidden
descriptors. But Ulrich would rather just go with a memory-based interface
which avoids the problem altogether. And Linus would rather not create any new interfaces at
all. All told, it has the feel of an unfinished discussion; we'll be
seeing it again.
(Log in to post comments)
I must say, that FD argument seems very silly... Already, libc can (and does) create hidden file descriptors behind an app's back for various other reasons... (Eg: syslog()... And, libc isn't alone in doing it: eg. xlib talking to the X server...) This is hardly an unexpected thing to happen from an app's point of view... Any app thinking they have sole domain over all FDs, and no lib will ever create any behind its back, is a totally broken app, which is unlikely to work in normal usage anywhere... So, I can't see any merit at all to that sort of argument against an FD-based API... Plus, an FD-based API fits naturally with the standard Unixy way of doing things, and is easy for most Unix coders to grasp, so again I'm not seeing any valid argument against it based on it being FD-based...
I agree. |
----------------|
Any app thinking they have sole domain over all FDs, and no lib will ever create any behind its back, is a totally broken app,
Correct.
which is unlikely to work in normal usage anywhere...
Unfortunately, this is not the case. They *do* work in normal usage almost everywhere. That's why they survive, because they don't break in the presence of two or three "unknown" descriptors. But when glibc starts chewing up many descriptors in a hidden/unexpected way, those apps will break. But guess who gets the blame? "My app works everywhere except with glibc on Linux 2.6.25, so it must be glibc/Linux which is broken." There's a whole long history of this kind of thing, and a whole long history of vendors (in a very general sense that includes free software developers) accommodating this kind of lossage. For example, why does C99 have the abomination "long long", even though 64 bit code could easily be accomodated by char/short/int/long? Because far too many people wrote code that assumed "long" was 32 bits, and the C compiler vendors didn't want to break that. (Well, and wanting to avoid breaking existing ABIs, which also
seems outside the purview of a language standard, and could have been dealt with in better ways.) Who got screwed? Those who could read the C89 standard, and made no assumptions about "long", except what was *promised* in the C89 standard: "long is the largest integer type".
But I'm not bitter.
Unfortunately, you really have to go further than that to have a reasonable chance. Old systems don't have those types defined, or have them defined elsewhere than <stdint.h>. So you really have to use local types which you laboriously define to whatever types, long long or whatever, work on that system. I distribute some software used on a wide variety of systems, some quite old, and this has been a nightmare for me. The inability to test for the existence of type at compile time, or redefine one, is the worst part.
It was wishful thinking of the original C designers that a vague type like "the longest integer available" would be useful. In practice, you almost always need a certain number of bits. Because such types were not provided, programmers did what they had to do: assume long or int is 32 bits.
This might help: Instant C99.
Yes, typedefs should be testable in the preprocessor. You certainly won't get any argument from me on that point :-) But for stdint, you can check __STDC_VERSION__ to determine whether or not to use your local version or the implmentation provided version.
A key point is that even if you do have to create your own defs, at least
name them after the stdint.h types, so that you can later switch without pain and not require other people looking at your code to learn yet another set of typedef names.
You have to do substantially more work if you want to do that, because you have to make sure nobody else defines the type. If you just do something as simple as checking __STDC_VERSION__, you can't then do a typedef of uint32_t, because it might be defined even though the environment is not totally C99.
And if it's part of an external interface, you surely have no right to define as generic a name as uint32_t. It could easily conflict with header files from other projects that had the same idea.
so that you can later switch without pain
The "switching" that I think is most important is where someone extracts your code for use in a specific environment where uint32_t is known to be defined. That's why I do all that extra work to be able to use uint32_t (and I don't claim that I've got it right yet) instead of a private name for the same thing.
except what was *promised* in the C89 standard: "long is the
largest integer type".
Or like GCC promised that "long long" is twice as long as "long", and
broke the promise when they ported GCC to the first 64 bit architecture
(MIPS). Now, if you are lucky, you can use typedef int int128_t
__attribute__((__mode__(TI))); to create a real 128 bit type on some
64 bit platforms.
There are only two choices: Sanity or backward compatibility with
idiots. The idiots are the majority, they always win.
Asynchronous buffered I/O
Drool....
|
|