Lecture 5 - 4/14/08: Orthogonality, Processes, and Race ConditionsClass: CS111Professor: Paul Eggert Scribes: Matthew Pham, John Gang, Edward Chang :Orthogonality:We want our operating system's features to be choosable independently - the choice of one feature shouldn't affect other choices. Think of each feature as an axis. Changing one axis should not change any of the other axes. The axes are orthogonal to each other. By designing an orthogonal system, we reduce the propagation of effects.Process Creation and Destruction:.Process creation and destruction involves several orthogonal functions:
/bin/date .
void printdate(void) { pid_t p = fork(); if (p < 0) error(); // exercise for reader; assumption is it does not return if (!p) { char const *args[] = {"/usr/bin/date", NULL}; execvp(*args,args); // maybe /bin/date or /usr/bin/date error(); } int status; if (waitpid(p,&status,0) < 0) error(); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) error(); }There are two library functions that a program can use to report its exit status:
SIDENOTE: When the main program
returns, the OS automatically calls exit on it.
int main(void){ ... return 3; }The OS does this: exit(main()); Also, the OS will only call exit(main())
on the top-level main, so a recursive function
will not exit prematurely.
Pipes:.A pipe is a buffer through which one process can communicate to another.
The
pipe function:
int pipe(int fd[2])
This function creates a pipe. It returns zero if successful,
and -1 on failure. It places the file descriptors for the read
and write ends of the pipe into the argument. This is equivalent
to int pipe(int *fd) .
fd[0]
is the file descriptor for the read end.fd[1]
is the file descriptor for the write end.
Pipes are bounded buffers:
Back to Orthogonality:.
Orthogonality often generates interfaces in nice pieces, but some common patterns become a pain to use. One example:
"I just want to run a program"
vs.
fork/execvp/exit/waipid/pipe/close/read/write
Solutions:
SIDENOTE: The "restrict" keyword:
pid_t buf; posix_spawnp(&buf,...,&buf); If we say a pointer is restricted, we are telling the compiler that no other pointers point to that memory block. This allows for compiler optimization. Orthogonality and Files:.The following is a list of file-access primitives:
creat .
The
creat function:
int creat(const char *path, mode_t mode)
EXAMPLE:fd = creat("/tmp/newfile",0666);
The file descriptor fd is what we use for read-write access.
"/tmp/newfile" refers to the name of the file
that we want to create. "0666" refers to the permissions (in
octal) of the file. In binary, the permissions come out to 000110110110.
creat is a standard function in UNIX
v7, POSIX, and Linux. Note: if the file already exists,
creat truncates it (it discards all data in that file,
setting the size to zero).
SIDENOTE: UNIX permissions
In UNIX, each file is assigned an owner. Permissions are split into three
groups: owner, group, and other. The owner user is given
owner permissions. Users in the same group as the owner are
allowed group permissions. Users outside the owner's group are
allowed other permissions. Permissions allow users to read, write, or
execute a file. The table below shows the permissions of the file that we
just created:
However, there is a problem with
read and write this file,
but they cannot execute it.
creat :
it is not orthogonal!
Why isn't it orthogonal?
open . The creat function is now deprecated.
Nowadays, we just use open , with flags.
The
open function:
int open(const char *path, int oflag, ... )
EXAMPLE:open("/tmp/newfile",0666, O_RDWR | O_CREAT | O_TRUNC);
This call is equivalent to creat("/tmp/newfile", 0666) .
Let's take a look at the flags.
O_RDWR specifies the kind of access that the file descriptor that returned from open .
In this case, it can read and write.
O_CREAT tells open to create the file if it does
not exist.
O_TRUNC tells open to truncate
the file if it already exists.
NOTE: If you use 0 instead of 0666, there are will not be any permissions at
all. However, the file descriptor that is returned to our process will
still have read-write access, provided that the O_RDWR flag is included.
SIDENOTE: the "umask"
If we do:
$ echo foo > bar $ ls -l barWe get: -rw-r--r-- ... barBut the shell says: open(...,0666,...)What happened to our permissions? Blame the umask , a per process mask that
applies to each "open" that creates a file. You may want to use the
umask for security reasons.
EXAMPLE:
$ umask 022This will set the umask
to 022 . If open creates a file with permissions 0666
, those permissions will go through the umask (0666 &~022 ) to become 0644 .
This means that we're not letting group members or others have write
permissions to any files that we create.
:Race Conditions:Suppose we have two processes accessing same file; they both want to create a temp file/use/remove it.We now have a problem: a race condition. A race condition usually happens when two or more processes try to access the same thing at the same time. It can lead to some very unexpected results. Let's say process 1 creates the file and starts storing data in it. Process 2, which is running at the same time, tries to create the file. It sees that the file already exists, so it truncates the file. If process 1 wants to read back some of the data that it wrote to the file, it is out of luck. It usually works, but if you call the processes at same time (or close), you get a collision. Possible solutions:
|