Pregunta

I was going through this tutorial about how to write a minimalist kernel. I read this in between :

The Run-Time Library

A major part of writing code for your OS is rewriting the run-time library, also known as libc. This is because the RTL is the most OS-dependent part of the compiler package: the C RTL provides enough functionality to allow you to write portable programs, but the inner workings of the RTL are dependent on the OS in use. In fact, compiler vendors often use different RTLs for the same OS: Microsoft Visual C++ provides different libraries for the various combinations of debug/multi-threaded/DLL, and the older MS-DOS compilers offered run-time libraries for up to 6 different memory models.

I am kind of confused with this part. Suppose I write my kernel in C code and against the advice use the inbuilt printf() function to print something. Finally my code will be translated to the machine code. When it will be executed, processer will directly run it. Why does the author says :

inner workings of the RTL are dependent on the OS in use ?

¿Fue útil?

Solución

There are two separate issues:

  1. What will printf() do when run inside of your kernel? Most likely it will crash or do nothing, since the RTL of the C compiler you use to develop your kernel is probably assuming some runtime environment with console, operating system, etc. Even if you're using a freestanding implementation of C/C++, the runtime will likely take over serial ports or whatnot to perform the output. You don't want that, probably, since your kernel's drivers will control the I/O. So you need to reimplement the underlying file I/O from the RTL.

  2. What will printf() do when run in a user process that runs on top of your kernel? If the kernel protects access to hardware resources, it can't do anything. The underlying file I/O code from the RTL has to be aware of how to communicate with the kernel to open whatever passes for standard input/output "files" and to perform data exchange.

You need to be aware of whether you're using a free-standing or hosted implementation of the C/C++ compiler + RTL, and all of the implications. For kernel development, you'll be using a free-standing implementation. For userspace development, you'll want a hosted implementation, perhaps a cross-compiler, but the runtime library must be written as for a hosted implementation. Note that in both cases you can use the same compiler, you just need to point it to appropriate header files and libraries. On Linux, for example, kernel and userspace development can be done using the very same gcc compiler, with different headers and libraries.

The processor has no clue what a console is, or what a kernel is. Some code has to actually access the hardware. When you take printf() from a hosted C/C++ implementation, that implementation, somewhere deep in its guts, will invoke a system call for the particular platform it was meant to run on. That system call is meant to write to some abstraction that wraps the "console". On the other side of this system call is kernel code that will push this data to some hardware. It may not even be hardware directly, it may well be userspace of another process.

For example, whenever you run things in a GUI-based terminal on a Unix machine (KDE's Konsole, X11 xterm, OS X Terminal, etc.), the userland process invoking printf() has very, very far to go before anything hits hardware. Namely (even this is simplified!):

  1. printf() writes data to a buffer
  2. The buffer is flushed to (written to) a file handle. The write() library function is called.
  3. The write() library function invokes a system call that transfers the control over to the kernel.
  4. The kernel code copies the data from the userspace pages, since those can vanish at any time, to a kernel-side non-paged buffer.
  5. The kernel code invokes the write handler for a given file handle - a file handle, in many kernels, is implemented as class with virtual methods.
  6. The file handle happens to be a pseudo-terminal (pty) slave. The write method passes the data to the pty master.
  7. The pty master fills up the read buffer of given pseudo-terminal, and wakes up the process waiting on the related file handle.
  8. The process implementing the GUI terminal wakes up and read()s the file handle. This goes through the library to a syscall.
  9. The kernel invokes the read handler for the pty master file handle.
  10. The read handler copies its buffered data to the userspace.
  11. The syscall returns.
  12. The terminal process takes the data, parses it for control codes, and updates its internal data structure representing the emulated screen. It also queues an update event in the event queue. Control returns to the event loop of the GUI library/framework. This is done through an event since those events are usually coalesced. If there's a lot of data available, it will be all processed to update the screen data structure before anything gets repainted.
  13. The event dispatcher dispatches the update/repaint event to the "screen" widget/window.
  14. The event handler code in the widget/window uses the internal data structure to "paint" somewhere. Usually it'd be on a bitmap backing store.
  15. The GUI library/framework code signals the operating system's graphics driver that new data is available on the backing store.
  16. Again, through a syscall, the control is passed over to the kernel. The graphics driver running in the kernel will do the necessary magic on the graphics hardware to pass the backing bitmap to the screen. It may be an explicit memory copy, or a simple queuing of a texture copy with the graphics hardware.

Otros consejos

Printf() is a high-level function that can be independent of the OS. It is however just part of the puzzle, it has dependencies itself. It needs to be able to write to stdout. Which will result in low-level OS dependent system calls, like create() to open the stdout stream and write() to send printf output there. Different OSes have different system calls so there's always an adaption layer, there will be in yours.

So sure, you can make printf() work in your kernel. Actually seeing the output of calls to printf() is going to be the real problem to solve. Nothing like a terminal window in kernel mode.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top