Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2004-103
$eventId
ID of event: wwdc2004
$eventContentId
ID of session without event part: 103
$eventShortId
Shortened ID of event: wwdc04
$year
Year of session: 2004
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC04 • Session 103

Mac OS X Kernel Programming

OS Foundation • 1:01:03

The Mac OS X Kernel changed significantly in order to provide better SMP performance, fine-grained locking, and support for 64-bit applications. In order to support those changes, and to provide a stable and efficient environment, Apple has formalized the APIs used by Kernel Extensions. These formal APIs, known as Kernel Programming Interfaces (KPIs), will be better defined and supported from release to release of the kernel. This session describes the changes that motivated the creation of KPIs, their effects on some kernel programming techniques, the introduction of KPIs into the kernel, and how they will continue to evolve.

Speakers: Simon Patience, Dean Reece

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good afternoon. Welcome to the afternoon session. This is actually my second WWDC presentation. So, relatively new to Apple in that respect, and one of the growing group of Unix professionals that we're finding in the CoreOS group at Apple, which is very good to see. A very strong Unix background in the group. So today, we're going to be talking about kernel programming, writing kernel extensions for Mac OS X, and in particular, what we've done in Tiger and how that's going to be affecting you.

So we've made some major changes to the kernel in Tiger for a number of good reasons. And to be perfectly honest, most KEKs are going to be unaffected by these changes. But there are a small number that will be affected. And what we'll be doing, talking today about, is how these changes affect those KEXs. There will be source changes needed to be made to these KEKs, and we'll cover what they are and what you need to do.

So what you'll learn today is why these changes to the kernel are necessary, which KEXs are affected, what changes are needed, techniques you'll need to apply so that your KEXs will work correctly, How kernel interfaces are managed, the lifecycle of a kernel interface, and generally why what we've done is a good thing.

So just so that you know what we're talking about today, this is what I would consider to be basically the Core OS, the core of the operating system, the Unix part of OS X. And what we're talking about today is pretty much the BSD file system networking area and the changes that we've made in this space.

So let me discuss a bit about the problem that we've been facing. So here we are, back at the kernel and the core system, and we have your KEXT. So you load your KEXT into the kernel. This particular one is a networking KEXT, but it's true of others.

And there are Parts of the KEXT that have got functional references, and these references are fine. We can tell what they are. These are unresolved symbols in your KEXT, and we can understand those. But some of them are really unsustainable from our perspective. They're reaching into parts of the system that we really consider to be an implementation detail, as opposed to what we would consider to be a supportable interface.

In addition, there are a whole set of what I would call detectable data references. These are things where you've referenced certain data structures, and the symbols for those are therefore unresolved in your text, and we can see that you're touching those. But some of these are also what we would consider to be unsustainable. They're pointing into data structures that we really consider to be private or an internal part of the implementation, as opposed to anything that you should be using as a programming interface.

But worse still is that there's a whole set of indirect data references. This is where you've gone into a data structure, and inside that data structure there's a pointer to something, you've followed that pointer, you've picked another pointer out of there, and you've potentially gone all over the kernel. And we have no clue as to what it is that you're referencing. And clearly some of those are very unsupportable as well.

Simon Patience, Dean Reece But the problem is that we consider binary compatibility to be paramount, and yet we have no visibility in what was actually being depended on. So what has happened is that kernel development was grinding to a halt. There was lots of big features that we wanted to implement that we just couldn't do because we would almost certainly break somebody. Part of the problem was that we didn't actually know who it was that we were going to break either. So we had to make changes very, very cautiously.

The bigger problems was that new hardware support was extremely hard. And in fact, one particular example of this was when we introduced the G5, we had to obsolete the interface for called PMAP Extract, which was really not a programming interface that we would want it to export. We actually had a perfectly good one. We were using IOMemory descriptors, but not everybody used it.

So new hardware support was becoming more difficult, and we certainly saw that this was going to become increasingly more difficult as time went by. We were limited in the number of new features we could implement because of the disruption that that would have potentially to data or to implementations where we'd have to change interfaces inside the kernel.

It even got to the point where performance optimizations couldn't be done because we would have to move things around and therefore potentially break things. Some bugs were not being fixed because we couldn't add fields to data structures. In general, the code paths in the kernel were being warped, and despite all this, we'd still find that developers would occasionally break.

So, we decided that we had to bite the bullet, and Tiger is the release to make progress. We needed to desperately, or add desperately needed functionality, What we wanted to do also was to extend the I/O Kit experience where you had stable interfaces, and extend that experience out to the rest of the kernel, so that KEX could be given much stronger guarantees of binary compatibility from release to release.

As I mentioned before, I/O Kit KEX are unaffected, and that's by far and away the majority of kernel extensions. But we will be breaking binary compatibility for a very few keks. And these keks are really the ones that are dependent on the com.apple.kernel.bsd sets of symbols. And we'll talk about that more later.

So as I said, we needed to add new functionality, and so we have a new heart for Tiger's Kernel. SMP support is clearly a big thing. If you notice that all our G5s are now SMP, are all dual processors. The previous funnel architecture was just not serving them well.

So we had to radically change that. 64-bit application support is another new feature in Tiger. If you have a 64-bit processor, 64-bit applications seem to make an awful lot of sense. File System Access Control List. This is a feature that people in the enterprise space have been pushing for for quite a while.

And now we've had the opportunity to perform certain optimizations. One of the most, biggest examples of this that I can, that came up recently was that there's a certain amount of data attached to every VNode for read and write operations. Now, not all VNodes have, are read to and written to, for example, directory VNodes. And even if they are, do have IO associated with them, it's normally of one type or the other.

This, this lump of data that's attached to the VNode was actually bigger for write than it was for read, but it was present on all VNodes, and read operations are far more frequent in the system. And so we've been able to actually change the data structure, remove those, those extra pieces, extra fields, and only attach them when the VNode is in that appropriate operation.

And doing this actually saved us a megabyte of wired memory space. So this is non-trivial stuff that we're, we're dealing with. And finally, as I said, there were bugs that we could fix. The fact that we can now add a field to a data structure without potentially breaking something has actually allowed us to go in and fix some bugs that we've had hanging around for a little while.

So improved SMP support, at last. People have been asking this for a long time, especially in the server space, where they've noticed that the funnels are, servers tend to operate in one mode, so they'll be doing sort of a lot of networking operations, or a lot of file system operations, and so therefore they can't take advantage of the split funnel quite so much.

And so they've been hitting this contention on the funnel, so finally we're getting to break it out and remove it from our code, insert finer-grain locking. I use the word finer on purpose here, we're not going down and locking every last little thing, we're trying to put the granularity at the right level for efficient operation.

We've also taken the opportunity to restructure a bunch of code. As I mentioned earlier on, code was sort of kind of getting warped around because we had to tread carefully not to break binary compatibility in the first place. And now we've been able to streamline the algorithms much more successfully, which has actually led to some performance improvements. And we can reorganize the data to associate the data with the locks that are protecting them.

So in Panther, this is really what the system looked like. We had, as you went in through the user space system call entry point, we took the big kernel lock. And as you passed down through generic system call handling and perhaps the file system descriptor handling code, down through, say, file structures and into the file system, you were holding this one lock at the time, and the kernel was fundamentally single-threaded.

And you'd go right the way down to the I/O layer, and once you got into I/O Kit, then that lock was dropped and you were back into a much more parallel environment again. If you were doing networking calls, then you'd still take the same lock as you came into the kernel, and then you'd go down through those layers again into the networking side of things, where we'd drop the kernel funnel and take the networking funnel, and then the entirety of the networking stack would then be single-threaded from networking's perspective down to the device layer again. So there was a very, very coarse level of synchronization within the kernel.

This has all sorts of interesting problems, apart from the obvious parallelism problems, but it's amazing how lazy you can get when you believe that nobody else can mess around with you while you're executing, and you can start accessing random data structures without any real discipline. So what we now have is much finer grain locking in the kernel, with locking at most levels of the system being independent from each other.

The locks are either at the subsystem level or at the object level. So here's subsystem level locks where basically there's a single lock to be able to allocate M-Buffs, for example. And then there are other locks which are held actually on the individual objects. So we're actually intelligently looking at the granularity of locking that needs to be held at all layers of the system so that we don't introduce arbitrarily fine-grained locking which would increase performance overheads, or have too coarse a grain level which means you wouldn't get the parallelism that you would like. So we believe that we actually have a reasonable level of locking granularity throughout the system. Our general philosophy is that we start coarse and then move finer grain as we've determined that there are performance issues.

Another big feature, 64-bit support. In Panther, the support was somewhat limited. We gave you We also gave you access to 64-bit instructions and 64-bit registers for performance reasons. However, the usage for those tended to be in assembler leaf functions, so it wasn't universally applicable. However, it allowed certain very important mathematical operations to be optimized to take full advantage of the processor. This addresses a couple of the dimensions of 64-bit support in terms of large physical configurations and some performance benefits that you can get from using a 64-bit architecture.

In Panther, we've decided we want to be able to leverage the full power of the G5, and so we're supporting 64-bit non-GUI Unix applications. So that basically means we're providing a 64-bit lib system, and that's it. The reason we're doing this is because we're aiming at applications which tend to have a naturally large data set. So these are compute-intensive applications in general. And these tend to come from the Unix world.

what we've had to do in the kernel is add support for multiple ABI's so we have to be aware of the fact that system calls can come through either from a 32-bit application or a 64-bit application which means that they have different address bases and therefore different sized pointers and the structures within the kernel or the structures that they passed to the kernel I should say could be different formats and so we had to be able to deal with that a lot of that's been at the system call entry time but there's other places in the kernel that do that we had to make changes to support a large address space breaking the four gigabyte barrier But of course, if you have a non-GUI application, you probably want to have a GUI application to represent the results of your app. And so we've had to do 32- to 64-bit ABI interoperability. So we have done various different shared memory IPC kinds of communication paths between the apps with different ABIs.

File System Access Control Lists. Fundamentally, the changes that we've had to do is to centralize the credential checking for authorization to access to operations. Basically, eliminating the UID/GID comparisons that you've seen sprinkled throughout the system. We have new kernel interfaces for these checks that are, I hesitate to say capability-based because it's not a capability system, but effectively, given an object and your credentials, this is the operation that you'd like to apply to this object.

Do I have permission to do so, or do I have the authorization to do so? And we've also enabled native file system support for ACLs through change in the VNode interfaces. So... Given the changes to the kernel that we've made, what are the changes that you will have to do for kernel programming for your kegs? So we discussed this last year. We've added a whole set of formal kernel programming interfaces in Tiger.

I/O Kit already had its own set of formal interfaces, so I/O Kit KECs are basically unaffected by this. The KEX types that we're supporting in the BSD side of things are file systems, network extensions, and filters. There's probably a few more details, but that's fundamentally the set of KEX that we're supporting through these KPIs. One of the big things is that there's no more open and indiscriminate access to all of kernel code and data being supported by these formal interfaces.

Now we understand that there are some KEKs for various reasons might want to get access to the whole of the kernel. We would advise that not to be in product code, simply because these KEKs, if they do get kernel access, or access to the whole of kernel symbols, will be tied to exactly one kernel version. So whenever we update the kernel, those KEKs will not load. However, if you use the KPIs, you're guaranteed release to release binary compatibility, and of course, source compatibility.

So what do the KPIs do? They basically encapsulate all the kernel objects that want to be accessed by Kernel Extensions. We've made all the data structures opaque so that you don't have to go and access the data structures directly, which means that these data structures are now invisible to you, and we can change their organization. We can add fields and do whatever. However, you can still get access to the information contained in these data structures by using accesses.

Perhaps more importantly is that the locking that we've implemented for synchronization for these various objects is completely hidden behind the KPIs. All the objects are reference counted, so when you access an object in the kernel, you have to formally look it up, basically, do the operations that you wish to, and then release it. Not doing this will cause leakage in reference accounting that will cause problems and bugs in the system later on.

So, basically what I'm saying is that the semantic model for a lot of the objects within, or the data structures in the kernel has changed because of this reference counting and because of locking. So, don't be fooled into thinking that you can just take, you know, the Veno pointer that you used to have and do all the operations that you used to and just sort of recast the pointers that you were getting back from the lookups, because this won't work.

We've also gone through and changed names of various interfaces, data structures within the kernel. And we've done this intentionally. It's not just to annoy people. It's basically to give you a strong signal that the semantics of that object has changed, and you have to re-look at that code and change your code to reflect the semantics of those objects.

As I was mentioning before, you can actually use all of the kernel. And if you don't use those KPIs, and you decide to go off and get a hold of a data structure that is encapsulated within the KPI, you can do that. However, you must understand and correctly honor all of kernel locking and the changes that we make to that over time. So this is really not a great idea. However, using the KPIs, the kernel implementation of those objects does not affect you. And so therefore, whatever we do will still remain compatible for you.

So the other thing that is going to affect you is that we've removed the funnel significantly from our code. The funnel in the seed is certainly still there. There's large parts of the system that are still protected by it, but that's being removed as we progress. So you should be aware of the fact that what you're seeing is development work in mid-cycle.

But the goal is to remove the funnel from Apple code before we ship Tiger. What this means is that the kernel is no longer single-threaded, and as it passes through kernel extension code, there could be multiple threads passing through this now. So, your KEKs need to protect their own data.

If you have data inside it, you have to be aware of the fact that there could be multiple threads trying to access this, and so therefore, if you need any atomicity, you're going to have to do this. And to do that, we have a rich set of synchronization primitives: spin locks, mutexes, reader/writer locks, and some atomic operations for you to use. By and large, we think that the mutex is actually going to be the one that you're going to be using most.

We also recognize that it might be difficult for some KEKs to become multi-threaded. Multi-threading is supporting SMP, and locking is actually not a trivial exercise, as we found out. So, there may be times where you actually want to be able to load and still be protected by the funnel. And so we do have single-threaded funnel executions supported. For example, as you pass through the VNode layer and going into the file system, we will take a funnel operation that will allow your code to be single-threaded.

So because we've added all these lovely synchronization primitives for you to use, it would be rather unfair not to help you debug them because locking is a tricky business. So we've added a whole bunch of things to help you debug your code in the face of deadlocks and so forth. So we have a bunch of things for lock debugging for mutexes.

As I said, we expect that mostly you'll be using mutexes in your code. So for mutex locks, you can get the current owner for the mutex, and if you've got a read/write lock, if it's held exclusive, you'll get the owner for that. The PC of the last time that the lock was held, so you know where in your code that lock was taken, and who it was that took it, as in the thread. Also, there's an option where you can actually get the backtrace of that last lock for eight frames.

This will indicate the path that you got down there because frequently, you know, you've taken the lock in some sort of generic function. You have no idea who it was that's called it, and it was called in one of 40 different places. So we give you the backtrace option to find out exactly the path that you took to get to that lock.

We also have some runtime checks to help prevent you from getting into trouble. We assert that you actually own the lock when you unlock it. We don't allow one thread over here to go off and lock something, and then effectively hand the object off and be unlocked by somebody else. That's generally considered to be very bad style, and leads to a lot of almost undebuggable situations. And so we just assert that you have to own the lock to unlock it.

If you've taken the lock exclusive, we detect that you've just taken it again. That's almost certainly not what you really meant to do, because that implies that there's two code paths that want to use this lock in an exclusive mode at the same time, even if it's the same thread. And so we detect that. We make sure that in the interfaces that require synchronization locks that you've actually passed the right lock type in, and you're not getting confused and giving a simple lock where a mutex is needed.

And this one is again a stylistic thing in some respects. That we, if you take a lock and you need to go to sleep, we assert that you actually don't hold any resources that prevent you from going to sleep. In particular, that means another lock, basically. If you're going to sleep for long times in the system, then you shouldn't be holding kernel resources. If you're waiting for I.O. or something of that nature, if it's a non-bounded sleep, then you shouldn't be holding kernel resources because that will lead to deadlock. We also have some log statistics being kept.

My light's gone off here. Is it working? Okay. Sorry about that. So lock statistics, sorry about that. So how long the lock has been held for? Average and maximum lengths of time. Clearly you don't want to hold locks for very large periods of time because that will increase the likelihood of contention. We also count how many times the lock was contended. When you take mutexes, we hang around and look if the lock holder is still active on another CPU.

If the lock is held by somebody on another CPU, then we don't immediately go to sleep. We sort of wait for a bit just to see if they're going to be holding the lock for a short period of time, and then you'll immediately jump in as soon as they release it. So we differentiate between contention and actually sleeping on the lock.

If the lock is held by somebody on another CPU, then we don't immediately go to sleep. We sort of wait for a bit just to see if they're going to be holding the lock for a short period of time, and then you'll immediately jump in as soon as they release it.

So we differentiate between contention and actually sleeping on the lock. So for 64-bits, in the kernel, We thought about how best to address this issue, the fact that you've got two different kinds of address spaces out there. So we decided that we would represent user addresses in the kernel as 64-bits, regardless of whether it's a 64-bit process or a 32-bit process. So the kernel data type for a user address is 64-bits.

Whenever you actually access the user address space, the accesses needed that you use to access it have been changed. Their signature has changed because of this. So if you use things like copy in and copy out, or UI or move, then those interfaces have also changed. Whenever you actually access the user address space, the accesses needed that you use to access it have been changed. Their signature has changed because of this. So if you use things like copy in and copy out, or UI or move, then those interfaces have also changed.

So for file system ACLs, as I mentioned earlier on, we have a new authorization model with, and have eliminated all the UID/GID comparisons in the kernel, and have used this new centralized credential manager, sorry, centralized authorization service called KAUF. In addition, we have new credential management interfaces. You can't cons up your own credentials and hand them off to the system because these things are now reference counted, so you have to create them and manipulate them through KPIs.

We have, as I mentioned, this sort of authorization model, which is a capability checking interface. Am I capable of performing this operation on this object? And this is actually extendable. We have pluggable authorization policies that you can insert into what are called scopes, which gives you the ability to contribute to the decision-making as to whether somebody can perform this operation on this set of objects. This was covered in the Fastest in ACL talk yesterday, and I hope people caught it. But failing that, if you would like to catch that on your DVDs, it will be explained in a lot more detail.

So we also have added native support for file system ACLs. And if your file system does support ACLs, then there are new VN Ops to be able to plug into to be able to export those. For file systems that don't support native ACLs, if they do support extended attributes, we have new VNOps4 to support extended attributes natively. And if you have those, then we actually store the ACLs in the extended attributes.

If your file system doesn't support either, then we store extended attributes in Apple double files, the .underbar files, and then ACLs are then stored in the extended attributes in that way. So even if you don't have any of these features, we'll still expose them to the user by smoke and mirrors, basically.

But this gives you the opportunity of taking existing file systems from other Unix systems that already have these advanced features, and now being able to support them natively within OS X. So at this point, I'd like to welcome Dean Reece, who's the manager of the I/O Kit team, to talk about how access to kernel interfaces is managed within the system.

Good afternoon. Okay, so first I'd like to talk a little bit about some extras that you're getting with your -- I guess it's available on the website, the Apple Developer Connection website content for this page, for this session. We've got several interesting new tidbits for you to help with your kex development for Tiger.

In no particular order, we've got a plugin for FireWire debugging. Basically, this allows you to do the two-machine GDB debugging through a FireWire connection. Functionally, it's similar to the Ethernet debugging that you can already do. It's got a few advantages, one being that it's available much earlier during boot and sleep/wake. So if you're trying to debug a sleep/wake problem, you have earlier access.

Also, we've got a new command called kexsim for GDB. If you're connected to another machine, and you have Kernel Extensions loaded, and you like to generate symbols for those, rather than having to generate them manually, you can run kexsim and point it at a folder on your local machine that has all the binaries.

Yeah, we definitely appreciate having this internally as well. So basically, it will generate symbols for all loaded kernel extensions in a single command. We've also got a Perl script, a little utility called Kextract. Now, this is something, it's a bit of an experiment, but as Simon said earlier, we have a situation where we have a lot of KEXT developers out here doing really cool development, but we don't have a good understanding of what all you depend on, what specific APIs you're using and why.

So this is sort of the beginnings of a feedback mechanism. And what Kextract does is you run it against your kernel extension, and it basically extracts a whole lot of information. Basically, it's looking at the P list, and it's looking at the undefined symbols in your binary. And it formats that into a little text file that you can send to Apple by way of Radar Web, or Bug Web. And we're going to keep these in a database that will allow us to search.

Anytime we want to make a change, or if we're just curious who's using an API, we'll be able to find any kernel extension using it. We'll be able to trace it back to the developer who submitted it. This is purely voluntary. We're going to be using this, again, it's a bit of an experiment.

It's not a certification program. There's no guarantees made. But this will be a good way for you to let us know what it is you're using, and it'll be a good way for us to find out and possibly contact you about upcoming changes. So I think this could be very useful.

So we've also got a new kernel available on the website. This is probably the single largest piece of the download. But the kernel that actually shipped on the TigerSeed still has a few KPI bugs in it that we wanted to get fixed so that you could play around with it.

But it was a little late in the TigerSeed development cycle for us to be putting kernel changes in for something that wasn't critical path for the release. So if you're going to be doing KPI development or kex development, you're going to be using the new KPIs, you should install this kernel. You should only install this kernel on top of the WWDC seed. And there's a readme in there that has instructions for how to install it.

We've also got some documentation and examples in the image. The examples are works in progress. The two file systems that we have there are WebDAVFS and MSDOSFS. These are the source code for the actual binaries that shipped on the tiger seed, so you're looking at the same thing that we actually send in to get built.

And as I said, they're works in progress. These are not in their final form, but they do work using the KPIs and it'll give you something to look at, sort of a starting point. We've also got a couple other examples that are not shipping code. They're just specific, tight examples of one or two KPIs, one for filters and one for a socket filter and one for an IP filter.

The documentation that's on the site is a collection of HeaderDoc and some RTF files, sort of porting guides, to help you, one, go through and actually examine the KPIs and their intent and their parameters and so on. And the porting guide is a little bit more of a tutorial, basically trying to walk you through the process. And it was actually written as an experience that we went through doing -- porting our own file systems and such to the KPIs. Obviously, as this whole KPI process moves forward, we'll make more information available, but this should be enough to get you started.

Okay, to talk about Kernel Extensions a little bit. We're making use of something called Interface KEXTs. Now, fundamentally, they look and feel like any other Kernel Extension, but there are a few special things about them. We actually introduced this concept in Panther, and you might remember me talking about them last year, but we didn't really use them until now.

The best way to think of them is they're a library mechanism for the KPIs. The KPIs, as you know it, are built into the kernel, so they don't really need a KEXT, but we're using the KEXT mechanism as a way of managing and versioning the interfaces. So they really only contain linkage. They don't contain any implementation whatsoever. I'll actually show you some diagrams in a bit that show you how the linkage works, but fundamentally, it's just a big symbol table wrapped in a KEXT bundle.

So what will happen is in the past, your kernel extensions linked against the kernel. And for Tiger and going forward, they're going to link against these interface keks instead. And that will have the advantage of, of course, allowing us to have multiple versions of the same API set or same KPI set coexisting on the system. And of course, the keks mechanism already has versioning built into it, so that will allow you to express the version that you need, and will automatically link up to the right thing.

So it really gives us a mechanism to control when we introduce a new KPI set, how it evolves over its lifecycle, and then when it comes time in the distant future to obsolete it, it gives us a way to do that. I like to think of it as sort of a conveyor belt of interfaces that, you know, we can set to be several years long. But we'll introduce an interface and keep it around as long as it makes sense and is supportable.

All right, so in our previous operating systems, any time you linked against any of these five OS Bundle libraries or CFBundle identifiers, you were getting the whole kernel. Now what we had done is we subdivided the kernel into four basic areas because we knew that We knew that there were these logical divisions within the code base itself and the way people were using them, but we didn't really create a linkage difference because, hey, you were linking against the kernel.

So to talk about what each of them are a little bit, I think it's fairly obvious if you look at the header structure which one of these an API or KPI is actually being supported by. Obviously, anything that is part of the I/O Kit header sub-tree is part of I/O Kit itself, and so com apple kernel I/O Kit is the appropriate one there. BSD and Mach similarly have their own name spaces and their own header spaces. Libkern is something we created as a common library for all of the things that the three other main areas of the kernel needed to share.

Some of the primitives, libc type stuff, some memory moving, copying, stuff like that, atomic operations. Some of the things that you might think of as BSD-ish, like, you know, B copy is not part of BSD. That's actually part of libkern. And we'll be providing more guidance on exactly which one is where coming up in information very soon.

Okay, so anyway, for all versions of the kernel prior to 8.0, when you link against any of these, you're getting the whole kernel. So this is the diagram. I actually have several animations coming up around this to kind of show you how this design is evolving. But fundamentally, we have the kernel with these four partitions inside of it, logically being treated as a single thing. And the kernel extension that you see here in orange is basically a pure I/O Kit text in that it only depends on I/O Kit and libkern. The libkern is allowed because it's common to all.

Now the problem comes in because you're linking against the whole kernel. If you access a symbol that's considered part of BSD, let's say, copy in, or maybe you're using the devfs to create a device node to communicate with the utility. Those things are not part of libkern or I/O Kit, and as a result, you have an undeclared dependency there. We can't detect the -- we can't detect it by looking at your plist because you haven't declared it, but you're getting it because you're linking against the whole kernel.

So what we've done for Tiger is to basically take this entire collection of interfaces, symbols, and group it together into one interface text. We call it the compatibility text. Okay. And all existing kernel extensions that link against existing versions of the kernel will link against this in Tiger.

But what we've done is taken the implementation out of it and moved it off into a separate place. So the kernel continues to be the implementation, as it always has been, but when you link against any of those four areas, you're getting this compatibility text. And of course, all of its symbols are being resolved by the kernel. Now the new KPIs, the things Simon has talked about and other sessions WWDC will be talking about, they're introducing new interfaces in the kernel. Those in the final version of Tiger will not be in the compatibility text.

They will be in new interface libraries, interface keks. So again, we have the same four. We've changed the name to represent the fact that these are sustainable kernel programming interfaces. So com.apple.kernel.kpi. You're not linking against the whole kernel, you're linking against a single interface. Okay, so in this case, if you wanted to have a driver load against all of those, you could, but you would have to list BSD as an explicit reference. Otherwise, your text would not load because of undefined symbols.

Now, another thing I need to point out is we really kind of have old world and new world here, and the two coexist on the system fine, but there's no crossover allowed. In other words, you can't write a new style text that then goes and tries to pick up one or two old symbols that have been deprecated. You either need to run against the old symbols, or you need to run against the new sets.

The other aspect of this is, as Simon had mentioned earlier, we have a lot of symbols that were exported that we believe people may be using, we don't know for sure, but they're implementation innards, they're things that we don't believe Kernel Extension should be getting at. And in a lot of cases, they cannot continue to exist. Changes to the architecture of the kernel, semantic changes, make it impossible.

And so what's happened is with Tiger, a certain number of interfaces, symbols that have been there in the past are no longer there in the compatibility text. That will break some drivers right there. The failure mode that you'll see if you encounter this is an undefined symbol when you try to load your kernel extension. And the only way to remedy this is to fix your code so that it doesn't need that interface, or to port over to the new KPIs, which obviously we would prefer.

And as the system evolves, we have made a commitment to continue supporting the KPIs, but you can expect as we vary further and further from the older kernel, we're going to have more and more breakage of interfaces that were never sustainable in the first place. So you're going to see those symbols starting to slowly erode.

But again, we're not doing this because we want to force people onto new interfaces. We're only taking them out because we have to for performance enhancements, for new features, and so on. So we obviously want to provide you a way to move your product forward, and that would be the KPIs.

Okay, so to sum up, as I had said, we'll only remove the symbols that we need to. The whole point of a compatibility interface, KEXT, is to be compatible, right? So if you have a KEXT that you've shipped previously, we would like to see it be able to load against that. The reality is if your KEXT has dependency on BSD, it probably won't.

If you're doing anything interesting with BSD, you're probably hitting on a symbol that's no longer there, and we'll need to move forward to the KPIs. If you're writing an I/O Kit KEXT, there's a very good chance that you won't break, because you're probably not using the BSD innards in that case. But again, until we know exactly what symbols you're using, it's very hard for us to tell.

Okay, so to help the users get through this transition, what we've realized is we don't really have a good mechanism to talk to users about specific kernel extensions. Giving them a path name is probably not a very good user experience. So what we've done is we've defined two new properties that we'd ask you to add into your kernel extensions that are OS bundle product name, and basically it should be anything that you would use to identify your product.

If somebody calls your support line and says, hey, I've got your product, what do you need to know to identify it? We also realized that there are cases where you have a whole suite of kecks working together, and from the customer's perspective, it's a single product. Well, if we have problems with two or three of those, we really don't want to put three panels up to the user and say, you know, this one and this one and this one.

What we're going to do is examine these properties, and if this property is identical in multiple kecks, we'll treat it as a single product, and we will only talk to the user, with it being a singular thing. Obviously, we're not going to scare the user. We're not going to put panels up unless the product cannot be loaded for whatever reason, but we'd like to be able to talk to them in a way that's a little cleaner.

We also realize that you might have a fixed kernel extension available, and we'd like to make it easy for the user to get at it. So if you can add the OS Bundle product URL, which again is an optional property, then If we wind up presenting a UI to the user, we might consider including this as a shortcut so that the user can go right to a downloads page or a product support page.

To be honest, we don't know yet exactly how we're going to use these properties. We know that these are there to be presented to the user in dialogues. We'll probably have Apple System Profiler display them, but we wanted to get this message out for WWDC. We don't have the use nailed down yet. But put it in, and the next time you ship a kernel extension, make sure you've got these in there.

[Transcript missing]

On an unrelated note, I'd like to talk a little bit about I/O Kit and 64-bit. As the I/O Kit manager, I wanted to make this information available. We don't have a full I/O Kit session because the core of I/O Kit has remained largely the same with Tiger. Most of our energies have been focused elsewhere with the MP work and other things.

But the kernel virtual address space remains 32-bit in Tiger. So for I/O Kit kernel extensions, pointers are still 32 bits. Now, drivers can get to greater than four gigabytes of memory, just like they did in Panther updates that supported the G5. And you do that by calling I/O Memory Descriptor. And it has various accessors. Of course, you need to call Prepare.

And what that does is that creates a mapping between the physical memory, which may be a large address, and what we call a bus virtual address. Basically, it's a 32-bit address that can be passed around to your device. You can also, for PIO mode, you can get directly to read bytes and write bytes, and that actually reads physical memory directly. So that's how you get the physical 64-bit address space.

Now for applications talking to I/O Kit, they can go through any of the POSIX APIs that have been made available as part of the 64-bit app support. So, you know, standard open/close, read/write I/O control. So that'll work fine for networking interfaces, for the storage interfaces, and serial as well.

All of those use POSIX. What it will not support, though, is direct driver access through user clients. And the reason here is that the I/O Kit user client model and libkern and everything else about ferrying data directly between an app and an I/O Kit driver is all based on CF, and CF is not being made available for 64-bit applications at this time.

So stay tuned. We might be able to do this in the future, but for now, we're focusing more on the 64-bit apps that are working on large data sets, and generally they're not also scanning the bus for USB devices. All right, with that, I'd like to hand the podium back over to Simon to continue with the presentation. Thank you.

Simon Patience, Dean Reece Thank you very much, Dean. So I'd like to briefly touch on now is the lifecycle of a kernel interface. So, what are the objectives behind having KPIs? Well, we want a set of well-defined interfaces. And for that, for Apple, what that means is that we can focus on making sure that we guarantee binary compatibility from release to release. So, for developers, what that means is that you now have a stable environment from release to release that you can program your solutions to.

We want to have a well-understood path for change between Apple and developers. This allows Apple to plan for kernel evolution so that we know that we can change things, we can make plans to add new features, and we'll know how we can phase this in. And it also sends a signal to developers that they know when to start to migrate from one interface to another.

And we'd like this to be predictable. We are not going to change KPIs arbitrarily or gratuitously just because we happen to think that the third argument should go somewhere else or whatever it is. The only reason to change a kernel programming interface is because something is forcing our hand, because there's new hardware that is being shipped, or the technology changes in hardware all the time. There may be new, who knows, processors.

[Transcript missing]

So we wanted a lifecycle that really didn't give you any surprises, and so we've based the model on the API lifecycle for applications.

So we basically have four modes within the lifecycle. Start off being supported, which means that the interface is fully source and binary compatible from release to release. This is going to be the state when Tiger ships of probably every single interface that we have in the KPI set.

If we find that when we're looking forward in our Our development, we tend to be in engineering, we are looking a couple of years out. And we see that there's going to be problems in a given interface. For hardware reasons or whatever, then we will mark an interface as being deprecated.

What this means is that when you build your KEXT, the compiler will start to generate warnings on its use, just letting you know that you're using an interface that we think we're going to be removing. The interface is still supported in terms of it will still work, but we're just basically giving you a positive heads-up saying, you know, this interface is at risk. And what we're reserving the right to do is to remove the interface in the following major release.

After that, we're looking at obsoleting the interface, at which point you will no longer be able to build your KEXT using that interface. We'll effectively remove the definitions from the header files, and there'll be nothing for you to compile against. But the binary compatibility is still assured. You'll still be able to take your KEXT and use it on a system.

And then finally, when we've got to the point where we've had to implement the given feature, and we've had to change the kernel, the interface will become unsupported, which means that the implementation behind that interface is no longer in the kernel, and therefore the KECs can no longer load.

So what do we need you to do? Well, initially we'd like you, or immediately, we'd like you to adopt the OS Bundle product name and the OS Bundle product URL to identify your KEXT. If we talk to the user, the correlation between a KEXT, "The name as we see it, which is a path name or something of that nature, and the actual user perceived functionality that it provides is really not evident.

And so this is a way in which the user can get a sensible interaction between the system and your product actually, so that they can be identified more reasonably." We'd like you to download the session extra material. It's on connect.apple.com under session 103, which is this session. And look at all the stuff that we put in there for you, all the additional documentation.

But more specifically, in the short term, we would like you to run Kextract on your KEXT, and to send the report that Kextract generates for you to Apple via RadarWeb. This will allow us to be able to see, before we even get to Tiger, what problems we may be creating, and whether we can actually alleviate some problems for people. So I do very strongly urge you to do that. I think it will benefit both of us.

Another very important thing to do is, once you've downloaded the extra material, is to review the interfaces. There is full HeaderDoc for all the KPIs. And you should bear in mind when you look at these, is that these seed interfaces are evolving. We're porting our own kernel extensions as we speak. We've done some, we haven't done all. So as we continue porting, we'll be spotting some limitations or some things that are missing or awkwardness in some of the interfaces. And so we will be changing things, hopefully not radically, but just fine-tuning as we go.

So please review those interfaces. Have a look at the examples that we have in those extras, both the file system ones and the networking ones, depending on what your text is doing, and see what changes we've had to make. I apologize for some of the others. We have fixed bugs in there, so there'll be some extraneous changes, not strictly related to KPIs, but it should be pretty obvious what changes are there for the purpose of using the well-defined interfaces. faces versus not.

And finally, well not finally, then I'd like to encourage you to actually try to port your text using the KPIs. It's very important that we understand your experience with these interfaces, because we want to be able to ensure that it will be able to fully support your functionality.

And finally, we would like you to give feedback. There's the [email protected] male alias which we are all sitting and watching and we would like to know your experience is both good and bad in other words if you found that moving your KEXT over was simple then we'd really like to understand which sets of KEXT were not having problems and if you find that there are some things missing or there's some awkwardness we would be very interested in that too because we will do our best to make this experience as easy as we can for you.

So, more information, the contact at Apple is Jason Yeo. He's the Mac OS Technology Manager. There's his email. Please let him know of questions that you may have or any feedback. Importantly for us in engineering, the KPI feedback mail list. I encourage you to let us know your experiences with using the KPIs.

There's some additional documentation you can find on the DVD referring to the network kernel extensions, API references. There's some 64-bit transition guides, a kernel programming document, There's some additional documentation for Kernel Extension. This is on the ADC home website, going down through Darwin, some descriptions of libkern, and there's another document on network kernel extensions.