iansjack wrote:Why would you want to use Objective-C to write a kernel? You have a huge runtime to implement, handling message passing, polymorphism, and the like all for something that can be done (IMO) far more simply in C.
The language that you use to implement a kernel doesn't seem to be particularly important to me, so why not use one that works out of the box and was designed specifically for this purpose? What do you see as being the advantage of using Objective-C?
I designed the system to be a microkernel (or picokernel) that can exposes an object representation of abstracted hardware by using some Objective-C tricks:
- Distributed Objects: one process can talk to another (in this case, a device driver to a kernel) via captured and forwarded method calls on objects in an proxy class.
- Class substitution: In Objective-C, an object can change its class on the fly (!)
- Class categories: In Objective-C, an class can be "patched" by adding mew methods on-the-fy in the runtime. (!)
This is how I load drivers and how drivers work: the kernel exposes all detected hardware as objects of an abstract hardware class that does nothing more than wrapping those instructions that is only available in ring 0, and a device driver load itself by substituting all suitable exposed objects' classes and/or categorizing on them. Memory-mapped hardware can also be drived by using a special kernel-only category method on NSMutableData (binary blob) class. and injecting a class with that NSMutableData as its ivar into the hierachy. Here is the boot procedure, suuming at least 2 processor cores: (Kernel itself preempts in the same way as other threads. A trap to kernel merely put the calling thread into wait and get the kernel into running.)
- System up, POST, GRUB to kernel.
- Kernel set up virtual root filesystem, unarchive initramfs (an tar archive) into it.
- Kernel set up GCD (a queue-based scheduler with IPC support)
- Kernel spawns init in userspace.
- Kernel start to enumerate and monitor hardware changes.
- Meanwhile, init scans all daemons in the vrfs and prepare them for load (load into memory, but not calling their init methods yet).
- Hardware enumeration is now done. Kernel pings init.
- After init is being pinged, it enumerates the established kernel device object tree and set up corresponding userspace counterparts, OSDevice objects.
- init load up initramfs drivers in parallel. They substitute the class OSDevice of the objects init set up with their own. Memory maps are set up by calling -[NSMutableData(OSKernelExtension) mappedMemoryAtAddress:(const void *)address length:(NSUInteger)length movable:(BOOL)movable] method with movable=NO. (essentially this is what you usually known as mmap(), but it is object oriented so mapped memory appears as an object too, but this movable argument is kernel specific and a non-movable mapping will never be paged out.)
- After all drivers are loaded, the rootfs driver is called to load root filesystem object tree. Then the real root filesystem is mounted over the vrfs. vrfs memory is reclaimed as original vrfs objects are collected. (I use Objective-C ARC for this.)
- Now with rootfs available, init will look for fstab to mount all remaining filesystems, and load up all remaining drivers, and start the daemons.
After the kernel start up, there will be a object tree matching your hardware and some unmovable mappings of NSMutableData used by drivers. Kernel and init will swap themselves out unless used.
Also, on my system malloc() in user mode is backed by NSMutableData objects in kernel, so kernel can optionally perform heap pointer range check at byte percision that hardens system at a speed penalty.