Scribe Notes - Orthogonalilty
April 14, 2009
Nick Venturino
Brendan Ryan
Nick Johnson
Last Lecture - Abstract Machinery
Build a virtual machine on top of a real machine.
Design principle used to line between OS and application
Today – Orthogonality
Software is infinitely malleable - “Let's change our OS”
orthogonal: perpendicular, independent
These are independent axis PUT IN PICTURE OF AXIS
Choice of z is independent of x and y
-we have to supply lots of different features
However, there are never features that are completely independent
think about files independently → modularity
System Calls
Implement call via a trap –
These calls acts like a function call except for:
(-) more stuff needs to be saved (less efficient)
(-) more state changes to CPU
(+) safe way to enter OS (from OS viewpoint)
Problems to Address
-making transitions from one domain to another
(registers and files)
-how to communicate information from app to OS and back
(safe, secure, and efficiently)
---------------->(1) simplest approach: pass a pointer to a larger object
| (-) protection problems (application writes into memory?)
common technique (-) you have to share data structure between OS and App
in embedded apps (-) race conditions (write and get it updated at same time)
(+) fast and cheap
Doesn't matter what data structure you use!
(2) pass a handle (high level pointer, i.e. looks like a pointer)
- access is controlled more strictly – need OS permission
-an opaque identifier that acts like a pointer, except all access is mediated by the OS
-can make a simple handle in C, but not good for OS because it still allows break ins
stdio.h “handle”
FILE typedef struct_FILE FILE;
FILE* fopen(...);
int fclose(FILE*);
ssize_t fread(FILE*...);
If you tried to look in f
C Library
# include <stdio.h> FILE * f = fopen(“foo, “r”);
__________________ f->fd //NO!!!!
struct_FILE{ | ^ //This is the purpose of a handle
int fd; | | //an opaque pointer
char * buf; |_____________|
size_t bufsize; | copy this to here
};________________|
All of this security has to be in this boundry
______________________ |
Application | |
___________ | |
OS | <---------------------|
__________ |____________|
Hardware |
_______________________|
In Linux/Unix this is typically an integer
- we will typically use file descriptors
identifies a particular access to a file
int fd = open (…) //we will also use this for process IDs
process id (pid_t) => pid_t p = fork(); // pid_t is a signed int
device numbers (dev_t)
inode number (ino_t) -uniquelly identifies a file in a filesystem
Most important process in Unix
Process 1- “init” => start the unix system
trying:
kill(1) //fails with errno = EPERM
waitpid(1) //also fails with errno = EPERM
How does this work internally?
We need to map these handles efficeinty in OS to real
Internally – Simple Version
maintain process table in kernel memory
-every process has its own set of Fds
file descriptors
______________________
| | | | | |
| | | | | |
misc | | | | | |
______|___|___|___|___|__|
0 1 2 3 ….
-when you open a file, one of the file descriptors will start getting used
-when you close a file, the file descriptor will stop getting used
Combine process and files:
int fd = open(“/etc/passwd”, O_RDONLY); // or flags readonly => O_RDONLY
pid_t p = fork (); //could fail // writeonlly => O_WRONLY
Orthogonality – What happened to fd in the fork command?
-In high level, both the parent and the child can access fd
- the child has every access the parent does
Let's look at an example
PRINT DATE COMMAND
void printdate(void) {
pid_t p = fork();
switch (p) {
case -1: error(); // error goes and never comes back
case 0: {
char*const args = {“date”, NULL}; //child section
execvp(“bin/date”, args);
error(); }
default: //we are sharing stdout (fd=1) with child
//FIXME date could hang
int status;
if (waitpid(p, &status, 0) < 0)
error (); //cannot happen
when you compile and line, cruntime object (crt.o) is before main
-entry point → exit (main());
_______________
$date
date.c
int main (void)
clock_gettime (….);
localtime(.....);
printf(....);
return 0;
________________
//what if child exits before we call waitpid? OS keeps PTE around until parent waits for it.
//a zombie processes, such as that, cannot be killed, even by superuser
//when a zombie's parent exits, 'init' (pid 1) inherits the zombie
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
error;
}
the init function (pid 1) inherits zombies like so
init: startup();
while(waitpid(-1,...)) //this reaps zombies
continue;
Send output of date to some file location
$ date > /tmp/foo // <-- this requires fixing old code
case 0: {char* ...;
---------New stuff-----------
open(, O_WRONLY | O_CREAT | O_TRUNC, 0666);
//O_CREAT will create the file if it does not exist
//the 0666 is a umask permission code
//the leading 0 signifies it is an octal number and
//each digit after represents permission for self,
//group and other respectively
//the binary translation of the number represents read, write and executable permission
//for each group. So for example 666 would become 110 110 110 (rwx rwx rwx)
//so self, group, and other would all be granted executable permission but denied
//read and write permission.
if(f < 0)
error();
if(f != 1){
if(dup2(f,1) < 0)
error();
if(close(f) < 0)
error();
}
--------back into old code ---------
execvp(...);
spawnvp(...)
fork + exec all at once
(-) requires many extra args for redirects between fork and exec
(-) non orthogonal design