ULIX: Literate Unix
A diary documenting the implementation of ULIX-i386
Welcome to the ULIX blog.
Ulix (Literate Unix) is a Unix-like operating system developed at
University of Erlangen-Nürnberg.
I use D. E. Knuth's concept of
Literate Programming
for the implementation and documentation. The goal was a fully working system which can be
used in operating system courses to show students how OS concepts (such as paging and scheduling)
can be implemented. Literate programs are very accessible because they can be read like a book;
the order of presentation is not enforced by program logic or compiler restrictions, but instead
is guided by the implementer's creative process.
Ulix is written in C and assembler for the Intel architecture. The literate programming
part is handled by noweb.
On this page I document my progress with the implementation.
|
|
Navigation:
2015 |
2014 |
2013 |
2012 |
2011
|
User Mode Shell (25.11.2012) |
Ulix now boots into a user mode shell -- a real shell process which can fork
new processes. Currently the new shell has only a few (internal) commands;
it can show the process list (ps) and the files on the floppy (ls) and
look into them with the head command. Ah, it also can exit...
The rewrite of the task-switching code seems to work a lot better than the
original code which was based on a tutorial that I found on the web. For now,
there are no more problems with the stacks. I've also changed the code for
the system call handlers which now works with the same set of saved registers
as interrupt handlers do; that makes life easier, because there are always
the same data on the stack.
Also, the document which describes Ulix has crossed the 400 pages barrier today.
It will likely grow to something like 600-700 pages before Ulix is finished.
[ Path: | persistent link ] | |
Redoing the scheduler and init / exec code; floppy access (19.11.2012) |
Today I started rewriting the code for scheduling and process
initialization (via fork() or load_from_disk()).
The new code uses the fact that all handler functions (which
includes system call handlers) can read the register contents
of the process in user mode (before the interrupt occurred or
the system call was initiated with int 0x80).
Also new in Ulix: I can now access a floppy drive. Reading and
writing sectors works fine in both system mode (during the
OS initialization) and in user mode (through sys calls). For
testing stuff, there's also a rudimentary FAT-like filesystem
(including a makefs command that creates new floppy
images and fills them with files, a bit like genisoimage).
Since so many things depend on one another, there's also a
usermode library (ulixlib) which allows Ulix programs to be
(cross-) compiled with gcc. So it is now possible
to write C programs that open() a file on the
floppy disk and then read() from and write()
to it.
[ Path: | persistent link ] | |
Executing processes (05.10.2012) |
Ulix can run processes now. It does not handle several of them nicely yet,
but the news is that programs can be compiled (externally) using a basic
usermode library. So far it can printf() and--sort of--open files and
read/write them.
Compiling requires a linker script which creates a flat binary: So far,
Ulix does not support ELF binaries (but there'll be ELF support early
next year, since a BSc student has just started working on that component).
Such programs can call functions in the usermode library which in turn
make system calls in order to do privileged things (such as writing to
the screen and forking).
After launching a program (which has no way to properly exit() so far),
Shift-Esc jumps back to the shell. The new ps command in that shell shows
what's going on.
[ Path: | persistent link ] | |
Ulix has Hard Disk Support (sort of...) (02.10.2012) |
I've been looking at a few implementations of hard disk drivers,
since (obviously) Ulix needs this as well. Since none of the
available solutions is simple enough for inclusion in the
Ulix book, I've decided to cheat a little.
So I present: the serial hard disk. It relies on an external
process that serves as some kind of simple storage server.
Via the serial port, Ulix can request sectors (or send their
content), and the storage process translates this to
reading and writing a disk image.
It already works for non-blocking I/O (i.e. when the kernel
wants to access the disk), when Ulix runs on qemu. (Bochs
support follows soon.) In theory this would also work when
running Ulix on a real machine and connecting the serial
port to another machine running the storage process.
Next up: blocking I/O for processes.
[ Path: | persistent link ] | |
Moodle... (01.06.2012) |
When Ulix is finished, there will also be a Moodle course
which can be used as a free introduction to operating
systems. Today I've setup a temporary Moodle site.
[Update 2012/10/20: I've registered a new domain
ulixos.org which will
later host the course.]
[ Path: | persistent link ]
| |
Processes... (30.05.2012) |
The simple scheduling of several processes works. Each of them uses their
own memory (address spaces), and a simple round robin scheduler cycles
between them. For some reason they cannot access the keyboard input
buffer yet. Next: Fix this input problem; understand why fork only works
when manually moving the child's stack pointer 8 bytes up; create a user
mode library so that processes can be compiled C programs (instead of
inline assembler).
Here's the first running process:
call dofork;
call dofork;
mov ebx, esp; // print ESP
mov eax, 0x1003;
int 0x80;
mov eax, 20; // call getpid()
int 0x80;
mov eax, 0x1000; // store PID in 0x1000
mov [eax], ebx;
loop:
call doshowpid;
jmp loop;
dofork:
mov eax,2; // syscall 2: fork
int 0x80;
ret;
doshowpid:
mov eax,0x1000;
mov ebx,[eax];
add ebx,'a'-1;
mov eax,0x1001; // syscall 0x1001: putchar
int 0x80;
ret;
It forks twice, so there are 4 processes running. The screenshot shows the
beautiful output of process 4 (printing "d"s).
[ Path: | persistent link ] | |
Filesystems: read and write via mmap? (22.05.2012) |
Just a note to myself: While reading a blog posting about improved
file I/O performance via memory mapped files, I wondered whether
it might make sense to write an mmap() implementation first and then
create open, lseek, read and write
as functions which use an
mmap()ped file. Every read() or write() would just turn into a
memcpy(), an lseek() would just set an offset variable associated
with an mmap()ped file. Plus, the code for transfering disk
blocks to memory is likely to be similar to the code needed for
paging in a page that was written to disk.
There would be a problem with large files, obviously, because
a mapping cannot be larger than the address space.
[ Path: | persistent link ] | |
Processes, little updates, serial console (20.05.2012) |
I'm working on the process code. So far I can create the
initial process, run it (in usermode) and make it fork
(via a syscall). There's
also an initial implementation of a simple scheduler that
is supposed to switch between tasks 1 and 2 (which does not
work yet; after changing to the second process the system
crashes, because the stack is corrupt. I can fix the stack,
but then I get the same problem when switching back to
task 1. So there is some weird error in there).
Other new stuff in Ulix: I have a serial console (thanks to
the code from the
xv6
operating system, but I'm only using the functionality
of duplicating all terminal output in a terminal window
on the host (good for long memory dumps which scroll out of
the window). I've also added a "hlt" instruction in the
kgetch() function, so my notebook does not get hot any
longer :)
[ Path: | persistent link ] | |
Bug fix :) (19.05.2012) |
I found a bug in memory management that came into being after
including code for creating new address spaces: When there is
more than one address space, we have (identical) copies of
the page directory's mapping for c0000000..ffffffff -- so when
an entry in that area is modifed (i.e. a new page table is
created and linked from that page directory)
it must also be modified in
all existing copies... Took me quite a while to find it, but
now the code is OK :) Memory setup works for processes (as
long as no more than 4 MB are requested; can be fixed easily),
so the system is ready for process creation.
[ Path: | persistent link ]
| |
Ulix has address spaces (05.04.2012) |
Another step on the way to proper process management: Ulix now has address
spaces which can be switched on. So there is "process-private" memory
(however: no process yet).
To do: more work on address spaces (there are some bugs in there); integrate
address spaces + usermode switch + process table (not defined yet). For
starters, only cooperative scheduling.
[ Path: | persistent link ]
| |
User mode stuff: side project (02.04.2012) |
Since I'm giving the lecture "System Level Programming" at Nuernberg Univ. of Applied Sciences,
I use the practical coursework to develop some user mode tools for Ulix. Currently I'm working
on a simple shell. So far it can evaluate parameters and launch processes (assuming there are
working fork() and exec() functions).
fork() should not be a problem at all (once I have a process table), exec() can't be done properly
yet since there is no filesystem code at all. However, exec() could load a binary from RAM. I had
already experimented with precompiled programs stored in the Ulix code as byte arrays.
[ Path: | persistent link ]
| |
Update: Development goes on, Moved to Erlangen (12.01.2012) |
I've moved to Erlangen (where research and Ulix development are done), so
now I'm closer to the project, and things will continue faster this year.
Keep watching for new features :)
[ Path: | persistent link ]
| |
Copyright © 2011-2015 Hans-Georg Eßer;
Server: Debian Linux, Apache Web Server,
blog page powered by blosxom :: the zen of blogging,
Theme: Hazard Area 1.6 (modified),
created by Bryan Bell,
Copyright © 2000-2006 Weblogger.com.
|