Path: utzoo!dciem!nrcaer!sce!graham From: graham@sce.carleton.ca (Doug Graham) Newsgroups: comp.os.minix Subject: Assorted questions Message-ID: <877@sce.carleton.ca> Date: 13 Jul 90 11:26:19 GMT Organization: Systems & Computer Eng. Dept.,Carleton University,Ottawa,Canada Lines: 92 I've had some time to play with Minix recently, and I've got a few questions about it. I'm using 1.5.10, but I'll refer to the book where it doesn't differ much. 1) A process which is blocked waiting on a pipe will not be woken up if a signal arrives for it. On line 10591 in the book, a check is made for a pipe, and the revive on line 10602 (which is now a reply) is only done if the task was not waiting on a pipe. Is this intentional? If so, why? I moved line 10602 out of the if block, so it is done regardless of whether the process is waiting on a pipe, and I've had no problems (yet). 2) I notice that O_NONBLOCK has been added, and is implemented for pipes. Why not extend this to work for tty's as well? The mechanism is all in place, it just hasn't been done. Somebody (Bruce Evans I think) added an extra process to the program "term" so that it could take advantage of non- blocking I/O on pipes, when it would probably have been simpler, and more generally useful, to add a non-blocking I/O capability to the tty task. 3) A call to "sync" will result in function do_sync (line 12018) being executed. The first thing this routine does is to mark the superblock for the root file system as dirty. This guarentees that it will be written to disk. Why is this done? Why should the superblock *ever* need to be written during the normal operation of the file system. The only data it contains is that written there by "mkfs" when the file system was created. I ask this because I have my root file system on a hard disk with self parking heads. On an otherwise quiescent system, every 30 seconds, the program "update" calls sync, which seeks to the root partition, and writes the superblock. Then 15 seconds later, the drive parks itself. This constant seeking is quite distracting. (At least it was, until I took out the offending lines from do_sync) 4) When a buffer is allocated from the front of the LRU list in "get_block", it is not removed from the list. Instead, it is marked as used by having it's b_count set to > 0. Now the next time a buffer is allocated, it must search past all previously allocated blocks before finding a free one. Why not remove the block from the LRU list in "get_block", and then re-insert it in "put_block"? 5) In looking at the Minix source code, I have always had the feeling that there is something quite wrong with the way it is structured. While I was fixing problem 4, I think I realized what it is. Dr. Tanenbaum uses tasking as a means to enforce some kind of modularization concept. ie. the kernel possesses all the low level task information such as the registers contents. If, for example, MM wants to fiddle with the stack of a process in order to deliver a signal to it, it must send a message to the "system" task telling it to do so. The "system" task carries out the request, and then replies a status, at which point MM continues. But this is really nothing more than a procedure call, it does nothing to enhance paralellism. All this message passing stuff only serves to obfuscate the code. The system task could just as easily be made a passive collection of procedures. As well as making the code easier to follow, this would also eliminate the message passing, and context switching overhead. Getting back to problem 4: I wanted to arrange to remove blocks from the LRU list when they were allocated. Then when a new block is to be allocated, no search is necessary; it just takes the first block from the front of the LRU list. In a properly modular design, this would have required making changes to only one file: "cache.c". However, for some reason, the function "buf_pool", which initializes the buffer cache, is a PRIVATE function in "main.c". As I discovered after a few attempts, I needed to modify this as well, in order to remove any blocks that cross DMA boundaries from the LRU list. "buf_pool" should have been a PUBLIC function in "cache.c", which, along with "get_block", and "put_block", manipulates the PRIVATE data structures associated with the buffer pool. *This* is correct modularization, not sticking a random bunch of stuff in a task, and then sending messages to it. Now for my final question: are there any real advantages to structuring an OS as a collection of co-operating tasks as in Minix? I don't see paralellism as being the answer either; in fact, the structure of Minix impedes paralellism in a lot cases. For example, FS is a major bottleneck. If one process is waiting for a sector to be read from a floppy, other processes wanting to access the winchester, or tty, wait. You can also say goodbye to all those fancy disk scheduling algorithms. In Minix, the disk driver only gets a single request at a time, so it's scheduling job is pretty simple. I think Unix has it right: the kernel should be a passive entity which carries out requests in the (kernel) context of the calling process. The Unix sleep/wakeup mechanism seems a bit weird (How come it's never mentioned in the textbooks alongside semaphores, monitors, and message passing), but it also seems quite effective. Note: None of the above should be construed as a flame. I would be quite interested in hearing any comments, especially about the Minix structure. Minix is certainly a lot of fun, and I thank Dr. T. for that. I would like it to become useful as well, and for that, I think it definitely needs to do swapping. I intend to have a go at this, but I think some restructuring needs to be done first. ------ Doug.