There are two separate issues:
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.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!):
printf()
writes data to a buffer- The buffer is flushed to (written to) a file handle. The
write()
library function is called. - The
write()
library function invokes a system call that transfers the control over to the kernel. - The kernel code copies the data from the userspace pages, since those can vanish at any time, to a kernel-side non-paged buffer.
- 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.
- The file handle happens to be a pseudo-terminal (pty) slave. The write method passes the data to the pty master.
- The pty master fills up the read buffer of given pseudo-terminal, and wakes up the process waiting on the related file handle.
- The process implementing the GUI terminal wakes up and
read()
s the file handle. This goes through the library to a syscall. - The kernel invokes the read handler for the pty master file handle.
- The read handler copies its buffered data to the userspace.
- The syscall returns.
- 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.
- The event dispatcher dispatches the update/repaint event to the "screen" widget/window.
- 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.
- The GUI library/framework code signals the operating system's graphics driver that new data is available on the backing store.
- 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.