PB158 · Lecture 3
Your program never talks to the disk, the network, or another program. It talks to the layer next to it. Every layer says done about something different [V50].
PB158 · Lecture 2
Lecture 2 left four claims. Today asks each of them whose done that was [V50].
printf
ltrace
write
strace
Two blocks. The first finishes Lecture 2. The second is announced, and may run into Lecture 4.
Part 1
Three minutes, on paper, before I show you anything.
Part 1 · Recap
Three minutes. Nothing on the screen until you have written something down.
fflush()
Two different failures. Part 2's demonstration tests one. Your recap question asked about the other.
The process dies
The machine loses power
The prediction for Part 6. You write it now, before Part 2, so you carry the question through the carried hour.
Part 2 · What dies with the process
Parts 2 to 5 are Lecture 2, finished. Same slides, same order, renumbered into today.
Part 2
Last Tuesday ended after slide 32: the device reports done early [V20], and no tool in the room can check that report [V5].
A demonstration. You have the stack now, so predict this one.
hello
The rule sits in the C standard, not in the kernel.
It proves level one is real. It proves nothing about levels three and four.
Before I run the second tracer. Two lines on your page [S14].
fwrite
ltrace -S
strace stops at system calls. ltrace stops at calls into shared libraries.
apt-get install ltrace
How to use it
I promised three of four boundaries. Here is the tool each needs.
Part 3 · One write, three levels
Now that the levels have a reason to exist, walk down them with one small program.
Part 3
iodemo.c
Nine lines of C. The first open fails on purpose, so the trace shows an error path.
Four arguments, and two of them multiply.
Part 4 · Two kernels
Read as a comparison. Try the tools yourself afterwards.
Part 4
It reports filesystem and registry activity [S18], which is a different cut than the system-call boundary.
It watches the native API, "the interface between the application space and the OS kernel" [S10].
ntdll.dll
\??\
Before the Windows capture goes on the screen. Two answers, on paper [S14].
-1 ENOENT
You can already read this. The anatomy matches the Linux line, and the vocabulary differs.
First difference, visible in the first argument of almost every line.
Linux
= 3
Windows
FileHandle=0x124
Second difference, and it is the layer boundary showing up where nobody expects it.
NTSTATUS
errno
Third difference, and the other two are symptoms of it.
Two readings survive the evidence. I will not pick one for you.
Only Linux promises that the system calls themselves stay the same [V9]. Say different, never worse.
One program, two systems, traced on one day.
Part 5 · Buffering moves your errors
One question I will not answer, and the work that follows from it.
Part 5
write()
I built the whole hour towards this sentence. I leave it unresolved.
The error arrives somewhere other than where you caused it.
fclose
Yes or no, on paper. Then a second question: if it fails, is the descriptor still yours?
close()
The kernel deferred the write's error. The close surfaces it [S4].
demo/close/ — one descriptor, two closes, both platforms [S39].
demo/close/
The obvious repair is wrong, and the manual page says so [S4].
Not new subjects. The same objects and the same calls, wearing different names.
SOCKET
What today's tools cannot see.
And what today's tools cost.
--seccomp-bpf
write() returned success and the bytes are not on a disk [S2] — so when is it safe? Part 6 measures it.
Part 6
The question I left you with last week. Your prediction is already on your page.
Part 6 · Durability
No tracer here. The kernel publishes the number itself, and we watch it fall.
Dirty
Writeback
One run is an anecdote. Here are all five, including the one that disagrees.
The program's done and the kernel's done are different events, five seconds apart.
The setting a student reads first is not the one that applied.
dirty_expire_centisecs
dirty_writeback_centisecs
dirty_background_ratio
The objection you are about to make, and the answer to it.
Visibility
Durability
fsync
Before the counts. Same program, same hundred records, four ways [S14].
f.flush()
os.fsync()
fsync()
It crosses one boundary. The next is not yours to check.
The Windows twin of the four-mode run, on my Windows 11 VM [S52].
The same watch, on Windows, until the counter returned to baseline [S52].
The device rejected the write, so fsync() returned −1 with EIO. The obvious repair: call it again.
EIO
2018, and the case is known as fsync-gate. A PostgreSQL developer found that retrying a failed fsync() "is not OK at least on Linux" [S41].
Mechanical, not sloppy code: the retry does not re-attempt the failed I/O [V54].
Three storage engines, three architectures, one answer, in one year.
It is undocumented, which is a third answer and a worse one.
This part is yours, and the classic recipe is incomplete [V51].
How every editor saves, and how ext4 made it lose files in 2009 [S44].
Most programs are right not to pay, until the failure is "silent" [V52].
The write-ahead log protects you because of an order, and the order covers its own failure.
The answer, and what it does not cover.
Parts 7 to 9 · The announced tail
Parts 7, 8 and 9 are built in full. If the clock has gone, Lecture 4 opens on this slide and we jump to part 10.
Part 7 · Waiting
The thing we are about to wait on, a hundred times over.
read()
Three ways to ask a pipe "is there anything to read?"
O_NONBLOCK
EAGAIN
select()
poll()
epoll
Two programs, two seconds, zero bytes of data. They differ only in how they wait [S14].
The cost of waiting, not of I/O.
A hundred pipes, a hundred blocking threads — thread-per-connection — failed on two bills [S59].
read
epoll_pwait
Three things about 28,017 and 1 before you carry them anywhere [S53].
epoll_create1
epoll_ctl
epoll_wait
One flag. Get it wrong and a connection goes quiet.
Level-triggered — the default
poll(2)
Edge-triggered — EPOLLET
EPOLLET
One ladder. Each rung answers "who gets parked, and where" [V69].
The word ready sounds like a guarantee. It is not one.
Not the API shape. The ownership, and it is why porting between the models is a rewrite.
I have used the platforms as labels. Here is the correction.
kqueue
We measured pipes. A regular file behaves differently, and the difference is silent.
The same two programs, in Windows vocabulary [S53].
PeekNamedPipe
ReadFile
GetQueuedCompletionStatus
Completion is the rung we have not yet watched run [S14].
NtRemoveIoCompletion
Two counts per platform. Compare within a platform, never across [S53].
One word before the last slide of Part 7.
Not convergence. Each one took the half it was missing.
io_uring
IoRing
Part 8
A file, a pipe and a socket. One program, one trace, three destinations.
Part 8 · Three destinations
You know the file run by heart now. I change only the destination [S14].
openat
close
The reason is a kernel structure, not a convention.
./iodemo /dev/stdout with nothing after it. No pipe, no file. The screen [S14].
./iodemo /dev/stdout
\n
Same fourteen bytes, over a TCP socket, with netcat on both ends [S14].
./iodemo /dev/stdout | nc 127.0.0.1 9000
nc -l
write(3, …)
read(4, …)
send
recv
They are the program's choice of spelling, not the kernel's.
MSG_DONTWAIT
MSG_NOSIGNAL
Three boundaries on the result.
PIPE_BUF
lseek
On Linux the destination changed nothing in the trace. Windows is the contrast [S57].
WriteFile
send()
HANDLE
NtWriteFile
NtDeviceIoControlFile
0x1201f
Third time today, and the same asymmetry each time.
Part 9
One call can carry several buffers. And the kernel can move a file your process never holds.
Part 9 · Zero-copy
A message that is three pieces in your program, and one message in the file [S14].
strace -e trace=write,writev
The distinction the name hides, and your second answer.
What writev removes
writev
What it does not remove
Atomic means one thing: your bytes land as one unbroken block, with nobody else's write in the middle [S58].
4 MiB from a file into a pipe. Two programs, same bytes [S14].
sendfile(out, in, …)
strace -c -e trace=read,write,sendfile
It counts the copies it removes at one boundary. No others.
Fewer copies changes who copies, not when the bytes are safe.
sendfile
Other names, and a Python fallback that says nothing.
WSASend
WSABUF
WriteFileGather
TransmitFile
Part 10
What changed about the assignment, what you can run this week, and where Lecture 4 picks up.
Part 10 · Close
I told you 22 September in Lecture 1. That has moved, and here is the honest reason.
Everything I ran today ships with this deck. Run it yourself.
Same as last week. I name it and do not answer it.
select(2)
Take it with you
ondrasek.github.io/talks
me@ondrejkrajicek.com