Pype.Clicker wrote:One of my goals with Clicker was to avoid "arbitrary limits" at all cost. E.g. nothing like a "grand total of N openend files are allowed" or "sorry, there are just too many threads, you can't start another one".
It turned out that i needed trees or similar structures where most "small OS" would just have an array -- so my design choice wasn't really in favour of small code.
You may call it bloated, of course. The intent was to pick up internal datastructures that could "scale" with the number of element they have to store (e.g. don't store things in a way that require walking a list of N items to find the one you need), which again, makes insertion/deletion code bigger...
Another source of code size increase is typically the need for plug-ins or drivers. Writing efficient and small FAT12/16/32 code for ATA disks isn't too hard to achieve. Having efficient implementation of a file system that can support FAT, EXTx and exotique filesystems in ATA/ATAPI, SCSI, floppy, or over (todo) USB, and that loads the appropriate items as they are used requires a completely different approach.
Scaleable data structures doesn't mean large code, just use good algorithms and easy to re-use code/sections. I can allocate any # of pages to store processes or disk/partition info, doesn't mean my algorithm needs to be huge, just need to implement a re-alloc function, or use a linked list or dynamic array (for things that don't change often). I have more than 1 filesystem driver for my 8kb OS, both are simply loadable modules, the kernel reads the first sector of the disk/partition and calls each currently loaded FS driver with a "CanSupport" function, which uses the first sector to determine if it can handle that file system. Once it finds one that does, it passes along a pointer to the i/o driver that reported the block device, so when the FS is invoked it uses the functions from the I/O driver. My Fat driver is used with both my ATA/IDE driver and my floppy disk driver without issue, and my other FS driver uses both of those i/o drivers just the same (my other FS is a custom one that I was using before I implemented fat). So, it's completely extensible, no limit on sizes, yet isn't overly huge. My FS doesn't care how the readblock/writeblock functions are done, it can be through any medium from floppy, ide, cd-rom, network, serial, whatever, it just doesn't care. I first load all drivers, then invoke each i/o driver's init function, which returns a list of however many disk/partitions it finds, and searches for the FS that can support it. Once it finds the correct FS driver, the FS driver registers that section as a volume, and has it's structure filled in (things like block size, read/write flag, total blocks, and the read/write block functions). Also, the drivers don't care when or what order they are loaded, i can load a USB driver after the OS is running, invoke it's init fnuction to search for partitions on a device, it will then search for the correct FS driver for each partition found, and begin working properly. I can load FS drivers at any time, but currently don't have a method to load them as needed (since it doesn't know what's needed unless the FS driver says it can support it). I was going to write FS stubs that are small functions just used to detect support that are all loaded, then the driver itself is only loaded if it finds a device that needs it, but dumped that kernel and started re-writing everything from scratch anyways
. Tree's are ok, so are array's, so are linked lists, whatever works for you is fine, none of the above are size limited. Arrays can be re-allocated if it's not done to often (as memory allocation/deallocation is slow sometimes), and if you need to remove/insert a lot, you either need a flag or similar for each element to say whether it's active, and have to traverse the entire list each time (slow for large lists!). Any of those methods is very easy to implement without creating a huge code base and wasting megabytes of memory. I have not put limits on myself, but do try to keep everything small and fast. I would only tell someone they can't open any more threads if they ran out of ram and swap space and couldn't allocate any more memory to create the thread! I also re-use code if possible, if you know you want to store a bunch of tree's, make a generic function to insert/delete from tree's and use it over and over again, that way you only made it a little bigger to add functionality to a lot of your OS parts. My virtual memory manager and physical page manager both use similar functions, so i merged them. I use bitmaps for the allocation, so, i have 2 main functions, allocate and free. If I pass it my struct for my physical page manager it will use the physical base address to start allocations and physical page bitmap to find the next free page (or consecutive pages) and return its physical address. If I pass in my struct for my virtual page manager, it does the same thing except returns the virtual address. No need to write it twice, just a tiny extra thought to make them the same
. And now I can add extra tables easily, if I wanted a sub 1mb manager, i just set it's base address to wherever i want it to start (say I want to leave real-mode interrupt vector alone + bios info alone), I can start it after the bios info block up to 1mb. Then I can have a 1mb-16mb table, and another for > 16mb, and all use the same functions, yet just made my kernel much more useful for legacy devices, and barely made it grow at all.
Having a disk i/o driver that can load while the OS is running or as needed isn't much different than one that loaos on startup, and you don't need special case code all over the place for it, just seperate the functions, your Fat/Ext2/etc file system shouldn't care one bit whether it's reading from an ATA, scsi, floppy, usb, or whether that device was present at startup or just initialized, there is no reason why it should care, and if it does care, you might want to re-think your implementation so you can make everything more conforming. I know that the more you add, the bigger it gets, and the more advanced you make it, the larger it grows, but that doesn't mean it has to be a 2gb OS either. My small fat12/16/32 code works fine with ata or floppy, it doesn't have a special case for either one, it treats any i/o device the same. My fat12 driver could work with any device I wanted as long as it had a few details for it, like block size block count, read/write block, and that's all my FS driver cares about
.