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