Path: news.funet.fi!sunic2!mcsun!sun4nl!phigate!relay.philips.nl!philce!meulenbr From: meulenbr@ce.philips.nl (Frans Meulenbroeks) Newsgroups: comp.os.minix Subject: minix kernel structure Message-ID: <1992Jun24.121431.22027@philce.ce.philips.nl> Date: 24 Jun 92 12:14:31 GMT Sender: usenet@philce.ce.philips.nl (USENET post news) Organization: Philips Consumer Electronics, Eindhoven Lines: 151 Hi, I've been rethinking the minix kernel structure during the past few days. I still think that minix as it is now is quite cumbersome if you want to add new tasks. You have to change NR_TASKS, table.c, add the new task, recompile fs/mm/kernel/tools (they all depend on the include file which defines NR_TASKS). Quite a job. This way it is quite complicated to add/remove drivers. My idea is to restructure the kernel into a number of different processes. Each process corresponds with a kernel task (e.g. the floppy driver). None of them accesses variables from other task (low coupling). To support this, there is a micro kernel (further on called core, to avoid confusion with the current kernel) mainly consisting of system.c and proc.c (and perhaps a part of main.c). This core should have the following functions: - context switching - message passing - basic clock handling - memory copying between tasks - support for interrupt handling - creation and removal of processes. - system call dispatching (perhaps this should be done by a special task) Functions supplied by the core itself are: - the functions from system.c and proc.c - install interrupt handler - deinstall interrupt handler - install sytem call (allowing a process to register itself as servicing source for a specific system call) - attach to major device number (this way device numbers can be connected to drivers). This can simply be the mapping of the device number to the task id. If we identify tasks by a bit in their proc struct and make them standalone, it is very easy to add a new task to the system or remove one. Of course only the super user can do so (by means of a new system call) I see two problems with this approach which I do not know the best solution. - starting up I don't know what the best way is to get things up and running. Perhaps a small image has to be made, in the way like build does. That way the kernel can start wit a basic set of drivers (e.g. fs/mm/console/keyboard/disk/init). Other drivers can be started from /etc/rc). However, maybe there is a better solution. How do other systems deal with this bootstrap problem?? - Interrupt handling This is perhaps the most difficult problem. When an interrupt occurs an interrupt service routine must be called. This routine resides in the memory space of the driver. The interrupt routine maybe uses global variables of the driver (e.g. a tty buffer), although I would not recommend such a driver design. To call the interrupt service routine the following options exist: - capture all interrupts in the kernel and send a message to the process which wants that interrupt. Problem is that this will require a context switch before the interrupt is serviced, causing problems with devices which generate a substantial amount of interrupts (e.g. an rs232 line at 9600 baud) - set up the memory management hardware in such a way that the interrupt runs in the task space. If you have a real mmu there is a problem because on the return of the interrupt you also want to go back to the mmu settings of the kernel. Tricks can be used to solve this (e.g. by using different pages/segments, or by trapping to the kernel at the end of the interrupt routine), but they are not trivial (at least not on 680x0 systems which use the mmu, it may be easier if you have an 8088 pc).. - let all drivers use the mmu in a mode where virtual addresses are physical addresses. This is exactly the way it currently works. Disadvantage of this problem is that tasks may damage other tasks, but since they are priviledged they can tamper security if they want to anyway. Another disadvantage is that maybe on 8088 all tasks + the kernel core must reside in one 64k area. I'm not familiar enough with the 8088 and its interrupt mechanism to firmly state something about this. - make the interrupt handler a part of the kernel. I don't really like this idea. It means you have to remake the kernel if you want to add a driver, and that is just what I am trying to avoid. Can anyone explain how other systems (e.g. mach) deal with this problem. Also: am I forgetting something, or is there another solution for this problem? Advantages of this approach are: - the kernel itself becomes much smaller. - the system is more flexible for adding new drivers and tasks (which means that class assignments like add a driver for device xyzzy become more feasible) - the system can be documented easier. - the system is conceptually clearer, more elegant, and easier to explain. - drivers can be loaded when needed. This means that if a user has little memory, he can for instance leave out the printer driver. If he wants to print, he can start the printer driver, print, and terminate the driver. Disadvantages are: - perhaps there is some performance loss due to the fact that more message are passed around. - the code size will increase due to the fact that now routines are shared between various kernel tasks. This will not be the case for.the new approach. In my opinion the gains outperform the losses for an educational system. An implementation of this could be done stepwise by: - introducing a task and driver bit in the proc struct. - rewrite the macros in proc.h to use this driver bit instead of the NR_TASKS. - rewrite the code which uses things like END_TASK_ADDR. - take a simple driver and rewrite it so that it is stand alone compilable (so it uses the sys_copy system call instead of directly calling phys_copy and umap). The printer or the memory task seems a good candidate for this. - add a way to attach/detach an interrupt routine to an interrupt source - add a way to attach/detach to a system call - add a way to attach/detach to a major device number - add a system call which lets a program start as task or server. - remove the rewritten driver from the system and start it using the new system call - add code to the build process to load standalone drivers on boot time - rework the other drivers, taking them out of the kernel one at a time - clean up the remaining kernel (which should mainly be system.c proc.c and some hulp code). This scheme makes it possible to split the work in small testable steps. Of course during the process it will be unavoidable that a number of hulp functions are added to the library or to a specific kernel library (for operations that are used in more drivers, like panic() ) How does this sound? Am I missing something? Suggestions for enhancements? Things I forgot?? Any comments are welcome. Flames to /dev/null. Note that I have no intention to implement this tomorrow or next week. I want to start up some discussion first and obtain some consensus about which way to go. Of course ast has a veto in deciding whether or not he wants minix to go in this direction. -- Frans Meulenbroeks Philips Research Laboratories preferred email address: meulenbr@prl.philips.nl