Following one print() all the way down
You now know every layer. Here is print("hi"), bottom to top no more mysteries:
- Python:
printformats its arguments and callssys.stdout.write("hi\n"). - Buffer (lesson 7-2): the text lands in a user-space buffer. To a terminal, stdout is line-buffered, so the
\ntriggers a flush. - System call (lesson 8-1): the flush issues
write(1, b"hi\n", 3). That 1 is a file descriptor, a small number the kernel gives your process for each open thing. 0 is stdin, 1 is stdout, 2 is stderr. - Kernel: the CPU switches to kernel mode, the kernel routes the bytes to whatever descriptor 1 is connected to, a terminal, a file, or a pipe to another program.
- Terminal: another user-space process receives the bytes, decodes them as UTF-8 (lesson 2-3), and draws glyphs.
Your program never touched the screen. It handed 3 bytes across the boundary.
Code exercise · python
Run this. os.write is a thin wrapper around the raw write system call, no Python buffering at all. File descriptor 1 is stdout, and it needs raw bytes, not a string.
Code exercise · python
Run this. print is a convenience layer over sys.stdout.write. The write method also returns how many characters it wrote, which mirrors what the underlying syscall reports.
Code exercise · python
Your turn. Without using print or sys.stdout, write the text "hello from fd 1" plus a newline to standard output using os.write and file descriptor 1. Remember it must be bytes.
Quiz
You run python3 app.py > out.txt in the shell. app.py calls print("hi"). Where do the bytes go, and did app.py need any code changes?
Quiz
print() to a terminal flushes on every newline, but print() redirected to a file flushes only when a large buffer fills. Connecting this to lesson 7-2, why is that a sensible default?