Scribes: Lily Bao, Yeon Joon Jin, David Meng, Matthew Kwong
The BIG Idea of Unix: everything is a file
The fundamental way to get data is to use:
Advantages of using read for everything:
Disadvantages of using read for everything:
To solve this problem, there are two types of files: stream-oriented and random access
Stream-Oriented (like keyboard) |
Random Access (like disk) |
spontaneous data generation |
request/response |
(in principle) infinite input |
storage |
|
finite capacity |
Commands like read work for both stream-oriented and random access files
Commands like lseek work only for random access; they immediately fail if called with a stream-oriented file.
By convention, the first three file descriptors are reserved:
0: stdin
1: stdout
2: stderr
Remember the process table approach from last lecture. What can go wrong with this approach?
close(1);
i = write(1, "x", 1);
//write will fail and return a negative number
if (i < 0)
print(strerr(errno));
int fd = open("foo", O_RDONLY);
close(fd);
int fd1 = open("bar", O_RDONLY);
//fd1 could be the same int as fd, since fd is closed
read(fd, buf, sizeofbuf); //this could read from bar
for(i=0; i<N; i++) {
int fd=open(file[i], O_RDONLY);
if (fd<0)
error();
read_and_copy(fd);
}
fd = open("/dev/usb/flash01", O_RDONLY);
//open a flash drive in usb port
//e.g. physically remove flash drive or try to read from a wireless network
//returns -1, with special errno (not standardized)
read(fd, buf, sizeof buf);
fd = open("/tmp/foo", O_RDONLY); //foo is an ordinary file
unlink("/tmp/foo");
read(fd, buf, sizeof buf); //this succeeds!
You can make use of the fact that unlink and read are orthogonal:
Creating a temporary file approaches
fd = mktempfile(); //not visible to any directory
write();
read();
close(fd); //reclaims resources
int mktempfile(char* name, size_t size) {
do {
generate_random_file_name(name, size);
} while((fd = open(name, O_CREAT|O_RDWR|O_EXCL, 0600)) < 0 && ok_errno(errno));
//make sure you'll stop looking on a bad error
return fd;
}
Advantages to using pipes
Disadvantages of using pipes
How does the pipe work if we have a | b?
In the file descriptor table,
a's right (output) points to the write end of the pipe object
b's left (input) points to the read end of the pipe object
the pipe object is like a bounded buffer
What can go wrong?
To implement a pipe:
Given 3 processes sh (shell), b, a, where shell calls a | b
Implementing a fork that is nested in the following way:
SH forks B then B forks A
int fd[2];
pid_t bp = fork();
if(bp == 0)
{
pipe();
pid_t ap = fork;
if(ap == 0)
{
dup2(fd[1],1); //stdout of A is now writing to
//the write end of the pipe
/*run code for A*/
}
else
{
dup2(fd[0],0); //stdin of B is reading from the
//read end of the pipe
/*run code for B*/
}
}
How should the kernel deal with running out of power?