A Unix system looks something like this. We shall learn about each aspect of this picture by looking at one or two parts at a time. We shall do so by analyzing and writing various system utilities.
This week we focus on the topics of users, terminals, files, and on-line documents.
Our example programs will be who, cp, and tail.
The who utility lists users currently logged on. It lists the time, terminal, and host system of their session or sessions.
Since it is a system-related utility, you might think the databases, protocols, file formats, etc, that are used by this utility are obscure, perhaps kept secret.
In fact, the man utility and the /usr/include directory provide just about all the information you need to understand how who works and how to write who.
We explore who, utmp, /usr/include/utmp, and learn about the who basics.
We draft an outline of who0 to see how the main flow goes.
If you have used getc() or fgets(), you know how to read chars and lines from a file. But the utmp file contains structs. How can we read structs from a file?
AnswerWe use open(), read(), close() to read arbitrary chunks of data from a file.
Explanation includes:
Application: We write who1.c
There are two problems here:
How do we answer these questions? Easy, we return to the man pages and the header files.
Answers:
Application: who2.c
This version of who looks great! It works just like the real one, it was based on on-line docs, it taught us about reading files at the binary level.
How do we write to a file, though? Let's use a practical example: the cp command.
Answers: We use the system call open to connect to a file to write. We use creat() to create or zap a file. We use write() to send data to the file, we close() it when done.
Pictures: open creates a fd, write transfers data, close disconnects.
Code: llcopy.c
The copy program reads data from the source file into a buffer then writes from that buffer to the destination file. What effect does the selection of buffer size have?
Answer: reduces the number of system calls and system service requests.
A program runs in two modes: user and system. Only when in system mode can it access files. In user mode, the code can only requset file operations; these cause a trap to system mode.
So the fewer mode changes, the more efficient.
That's right. One system call per line of output makes as much sense as buying pizza by the slice for a party or buying eggs by the egg for a brunch for 30.
A Better idea is to, as with pizzas and eggs, buy a bunch when you go to the store and dish 'em out as you need 'em.
Similarly with output. Stock up the recyclable bottles by the door; don't take each empty pepsi can back to the store.
Idea: utmplib.c: a set of functions that implement a `buffered' input stream. When it needs more, it gets a bunch. The caller asks for the next one.
Application: who3.c
The kernel does buffer. It turns out that the model of how read and write work by transferring data from user memory to a file is false.
The kernel keeps a pool of disk buffers and writes to them and then schedules those buffers to be copied to the disk when the time is right.
If you write data to a file and read it back, you are likely to never go to the disk. The kernel knows which buffers are mapped to which disk blocks and reads right back from them.
Consequence: fast disk i/o, need flushes, need shutdown.
Need to seek to end of file and then go back a bit.
Discussion.