Principles of Computer Systems
Winter 2020
Stanford University
Computer Science Department
Instructors:
Chris Gregg and Nick Troccoli
cgregg@myth55:/usr/class/archive/cs/cs110/cs110.1202$ ls -1
ARCHIVE.README
cgi-bin
final-tests
include
lecture-examples
lib
local
main.cgi
private_data
repos
samples
staff
tools
WWW
ls -1
to the left.cgregg@myth55:/usr/class/archive/cs/cs110/cs110.1202$ ls -1
ARCHIVE.README
cgi-bin
final-tests
include
lecture-examples
lib
local
main.cgi
private_data
repos
samples
staff
tools
WWW
ls -1 -i
504030014 ARCHIVE.README
503231001 cgi-bin
503723839 final-tests
503186329 include
503185617 lecture-examples
503186393 lib
503186405 local
504453014 main.cgi
503231019 private_data
503192313 repos
503216939 samples
503216981 staff
503230523 tools
503185411 WWW
ls -1 -i
504030014 ARCHIVE.README
503231001 cgi-bin
503723839 final-tests
503186329 include
503185617 lecture-examples
503186393 lib
503186405 local
504453014 main.cgi
503231019 private_data
503192313 repos
503216939 samples
503216981 staff
503230523 tools
503185411 WWW
gcc
, valgrind
, and make
to the extent they're covered in CS107 or its equivalent.myth
machines, which is where you'll complete all of your CS110 assignments/usr/class/cs110/lecture-examples
, which you can initially git clone
, and then subsequently git pull
to get the newer examples as we check them in
stat
, struct stat
, open
, close
, read
, write
, readdir
, struct
dirent
, file descriptors, regular files, directories, soft and hard links, programmatic manipulation of them, implementation of ls
, cp
, find
, and other core Unix utilities you probably never realized were plain old C programsfork
, waitpid
, execvp
, process ids, interprocess communication, context switches, user versus kernel mode, system calls and how their calling convention differs from those of normal functionsmyth
machine, 12-16 per wheat
machine, 16 per oat
machine), pros and cons of threading versus forkingthread
construction using function pointers, blocks, functors, join
, detach
, race conditions, mutex
, IA32 implementation of lock
and unlock
, spinlock, busy waiting, preemptive versus cooperative multithreading, yield
, sleep_for
condition_variable_any
, rendezvous and thread communication, wait
, notify_one
, notify_all
, deadlock, thread starvationsemaphore
implementation, generalized counters, pros and cons of semaphore
versus exposed condition_variable_any
, thread pools, cost of threads versus processesmutex
, and condition_variable_any
pthreads
, pros and cons of pthreads
versus C++'s thread
packagegethostbyname
, gethostbyaddr
, IPv4 versus IPv6, struct sockaddr
hierarchy of records, network-byte ordersocket
, connect
, bind
, accept
, read
, read
, simple echo server, time server, concurrency issues, spawning threads to isolate and manage single conversationssockbuf
and sockstream
C++ classes (via socket++ open source project)GET
, HEAD
, POST
, response codes, cachingread
, and write
return immediately instead of blocking
select
, epoll
, and libev
libraries all provide nonblocking I/O alternatives to maximize CPU time using a single thread of execution within a single processgdb
and valgrind
ls
commandcgregg@myth58:~/cs110/spring-2019/lecture-examples/filesystems$ ls
alphabet.txt contains.c copy.c list.c Makefile search.c t.c vowels.txt
ls -al
total 23
drwx------ 2 cgregg operator 2048 Mar 29 12:33 .
drwx------ 10 cgregg operator 2048 Mar 29 12:33 ..
-rw------- 1 cgregg operator 27 Mar 29 12:33 alphabet.txt
-rw------- 1 cgregg operator 2633 Mar 29 12:33 contains.c
-rw------- 1 cgregg operator 1882 Mar 29 12:33 copy.c
-rw------- 1 cgregg operator 5795 Mar 29 12:33 list.c
-rw------- 1 cgregg operator 628 Mar 29 12:33 Makefile
-rw------- 1 cgregg operator 2302 Mar 29 12:33 search.c
-rw------- 1 cgregg operator 1321 Mar 29 12:33 t.c
-rw------- 1 cgregg operator 6 Mar 29 12:33 vowels.txt
$ ls -l list
-rwxr-xr-x 1 cgregg operator 19824 Mar 29 12:47 list
owner
group
other
In this case, the owner has read, write, and execute permissions, the group has only read and execute permissions, and the user also has only read and execute permissions.
r
, w, or x, there are three bits of information per permission field. We can therefore, use base 8 to designate a particular permission set. Let's see how this would work for the above example:111 101 101
open
system call, and you can set the permissions at that time, as well. We will discuss the idea of system calls soon, but for now, simply think of them as a function that can do system-y stuff. The open command has the following signatures (and this works in C, even though C does not support function overloading! How, you ask? See here.):int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
We will generally only care about the following other flags when creating a file:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
mode_t umask(mode_t mask); // see "man 2 umask" for details
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
mode_t old_mask = umask(0); // set to 0, but get old mask as return value
umask(old_mask); // restore to original
printf("umask is set to %03o\n",old_mask);
return 0;
}
$ gcc show_umask.c -o show_umask
$ ./show_umask
umask is set to 077
/usr/class/cs110/lecture-examples/filesystems
.
/usr/class/cs110/lecture-examples
directory is a git
repository that will be updated with additional examples as the quarter progresses.git clone /usr/class/cs110/lecture-examples cs110-lecture-examples
at the command prompt to create a local copy of the master.git pull
. Doing so will update your local copy to match whatever the master has become.#include <fcntl.h> // for open
#include <unistd.h> // for read, write, close
#include <stdio.h>
#include <sys/types.h> // for umask
#include <sys/stat.h> // for umask
#include <errno.h>
const char *kFilename = "my_file";
const int kFileExistsErr = 17;
int main() {
umask(0); // set to 0 to enable all permissions to be set
int file_descriptor = open(kFilename, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (file_descriptor == -1) {
printf("There was a problem creating '%s'!\n",kFilename);
if (errno == kFileExistsErr) {
printf("The file already exists.\n");
} else {
printf("Unknown errorno: %d\n",errno);
}
return -1;
}
close(file_descriptor);
return 0;
}
$ make open_ex
cc open_ex.c -o open_ex
$ ./open_ex
$ ls -l my_file
-rw-r--r-- 1 cgregg operator 0 Mar 31 13:29 my_file
copy
to emulate cp
copy
(designed to mimic the behavior of cp
) illustrates how to use open
, read
, write
, and close
. It also introduces the notion of a file descriptor.
man
pages exist for all of these functions (e.g. man 2 open
, man 2 read
, etc.)copy
, with exhaustive error checking, is right here.copy
to emulate cp
read
and write are defined as follows. #include<unistd.h> to use them.int main(int argc, char *argv[]) {
int fdin = open(argv[1], O_RDONLY);
int fdout = open(argv[2], O_WRONLY | O_CREAT | O_EXCL, 0644);
char buffer[1024];
while (true) {
ssize_t bytesRead = read(fdin, buffer, sizeof(buffer));
if (bytesRead == 0) break;
size_t bytesWritten = 0;
while (bytesWritten < bytesRead) {
bytesWritten += write(fdout, buffer + bytesWritten, bytesRead - bytesWritten);
}
}
close(fdin);
close(fdout);
return 0;
}
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
copy
to emulate cp
int main(int argc, char *argv[]) {
int fdin = open(argv[1], O_RDONLY);
int fdout = open(argv[2], O_WRONLY | O_CREAT | O_EXCL, 0644);
char buffer[1024];
while (true) {
ssize_t bytesRead = read(fdin, buffer, sizeof(buffer));
if (bytesRead == 0) break;
size_t bytesWritten = 0;
while (bytesWritten < bytesRead) {
bytesWritten += write(fdout, buffer + bytesWritten, bytesRead - bytesWritten);
}
}
close(fdin);
close(fdout)
return 0;
}
FILE
pointers and C++ iostream
sFILE
pointers and C++ iostream
s work well when you know you're interacting with standard output, standard input, and local files.
FILE
pointers and C++ iostream
s assume they can rewind and move the file pointer back and forth freely, but that's not the case with file descriptors associated with network connections.read
and write
and little else used in this course.FILE
pointers and C++ streams, on the other hand, provide automatic buffering and more elaborate formatting options.t
to emulate tee
tee
tee
program that ships with Linux copies everything from standard input to standard output, making zero or more extra copies in the named files supplied as user program arguments. For example, if the file contains 27 bytes—the 26 letters of the English alphabet followed by a newline character—then the following would print the alphabet to standard output and to three files named one.txt
, two.txt
, and three.txt
.$ cat alphabet.txt | ./tee one.txt two.txt three.txt
abcdefghijklmnopqrstuvwxyz
$ cat one.txt
abcdefghijklmnopqrstuvwxyz
$ cat two.txt
abcdefghijklmnopqrstuvwxyz
$ diff one.txt two.txt
$ diff one.txt three.txt
$
If the file vowels.txt contains the five vowels and the newline character, and tee is invoked as follows, one.txt would be rewritten to contain only the English vowels.
$ cat vowels.txt | ./tee one.txt
aeiou
$ cat one.txt
aeiou
t
executable, with error checking, is right here.copy.c
does, but it illustrates how you can use low-level I/O to manage many sessions with multiple files. The implementation inlined across the next two slides omit error checking.Source: https://commons.wikimedia.org/wiki/File:Tee.svg
t
to emulate tee
int main(int argc, char *argv[]) {
int fds[argc];
fds[0] = STDOUT_FILENO;
for (size_t i = 1; i < argc; i++)
fds[i] = open(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0644);
char buffer[2048];
while (true) {
ssize_t numRead = read(STDIN_FILENO, buffer, sizeof(buffer));
if (numRead == 0) break;
for (size_t i = 0; i < argc; i++) writeall(fds[i], buffer, numRead);
}
for (size_t i = 1; i < argc; i++) close(fds[i]);
return 0;
}
static void writeall(int fd, const char buffer[], size_t len) {
size_t numWritten = 0;
while (numWritten < len) {
numWritten += write(fd, buffer + numWritten, len - numWritten);
}
}
argc
incidentally provides a count on the number of descriptors that write to. That's why we declare an integer array (or rather, a file descriptor array) of length argc
.STDIN_FILENO
is a built-in constant for the number 0, which is the descriptor normally attached to standard input. STDOUT_FILENO
is a constant for the number 1, which is the default descriptor bound to standard output.myth
machines include real error checking.