CS 111

Scribe Notes for 10/20/2014, Lecture 5

by Hanfeng Liu, Yingjun Shen, Yue Luo, and Zhengliang Wu

Orthogonality

Orthogonality is an important principle to design operating system or any other big projects. It is very useful in managing complexity. Projects designed with orthogonality have no side effects theoretically. For operating system, we want processes to be independent with files. Without orthogonality, it will be painful when projects are getting bigger. orthogonality image

Lab 1 System Calls

file category processes
open folk
read _exit
write waitpid
close
lseek

Orthogonality in System Calls That Deal With Files

In linux system, the way to access files is unified, no matter regular files, devices files or network files. However, there are two different kinds of devices:

Because linux uses the same way to access files, it uses same set of system calls for both kinds of devices.

In Lab 1b, we deal with some of file-accessing system calls:

Among the above 5 system calls, "open", "read" & "close" work for both kinds of devices.

Example of programs using those system calls

wc /dev/mouse //intends to count the number of words of the mouse input file should be able to run (not necessarily be able to fulfill our goal though)

But there are problems:

Another example

cp /dev/disk/01 backupfile //backup the file in disk to backupfile should be able to copy the content in /dev/disk/01 to the file "backupfile".

In the program "cp", system call "lseek" is used. It it obvious that "lseek" only works with random access devices such as disk and memory, becasue they have finite size and so finite offset.

Linux’s Improvements on "lseek" & "read":

lseek (fd, offset, flag) & read (fd, buf, bufsize) -------> pread(fd, buf, bufsize, offset)

Use of "lseek" often associates with "read", but Linux only provides those two system calls separately. So later people combined "lseek" & "read" into a simple system call: pread, which is more efficient (only 1 syscall), more abstract, simpler

A Deviation: Two kinds of file data

open is the special one which uses filenames: open("a", 0_W/R_ONLY | 0_CREAT |…)


Orthogonality in System Calls That Deal With Processes

Look at the details of execvp system call

int execvp(char const* file, char* const* argv);

The argv list:

argv list image

Example program on dealing with execvp

void printdate(void){
  execvp("/bin/date", (char*[]){"date", "-u"/universal time, 0};
  perror("/bin/date");    //output result in stderror
}

What is the problem with this program?
If execvp successfully executes /bin/date, it will destroy the program.

How to make the program work correctly: use fork!

pid_t p = fork();
if (!p) {    //if child
  execvp("/bin/date", (char*[]){"date", "-u"/universal time, 0};
  perror("/bin/date");
  _exit(127);
}
if (p < 0)
  perror("fork");
else {
  int st;
  if (waitpid (p, &st, 0) !=p)
    perror("waitpid");
}

Details about system call fork()

fork() clones current process except:

Details about file descriptor:

process table image

The structure after fork():
parent
    waitpid
child
    close, open
    execvp

An extra system call

int posix_spawnvp (pid_t *restricted pid, 
                   char const *restricted file, 
                   posix_spawnfile_actors_t const *file_acts, 
                   posix_spawnattr_t *restricted attrp, 
                   char *const *restricted argv, 
                   char *const *restricted envp);

"restricted" means caller promises not to alias args. For example, strcpy(a,b) is actually: char* strcpy(char *restricted dest, char *restricted stc);

The posix_spawnvp (…) is a big syscall, but it violates the ideal of orthogonality.


How to model OS objects / resources (e.g. processes, files) in programs

  1. Using pointers

    + It is simple to implement and use.
    + It is fast.
    - There is typically no protection.
    - There are race conditions.

  2. Using "handles"

    Handles can be opaque identifies, which are always integer numbers. With "handles", user can only read information but can not locate the memory address.
    There are examples:

    • pid_t for process
    • int for file descriptors
    • MO_t for file numbers

What can go wrong in file-oriented system calls?

More subtle issues:

There are 3 options for case "write to full pipe & no readters" to exit:

int main(void)
{
  while(1)
  printf("yes\n");
}

If this program is not modified, it will constantly be printing "yes".
There are three ways to exit:

  1. if (printf("Yes\n") < 0)
        exit(1);
    (not good beacase user need to remember to add this if statement everytime)
  2. Error message, return -1
    (still need to manipulate mannually)
  3. SIGPIPE signal arrive, default exit
    (linux choose this option)

Some Q & A

Q1. No readers now, but a reader may show up later?
A1. Can’t happen here.

Q2. What happen if two pipes both write to and read from each others?
A2. They both wait. This is a deadlock. User should avoid doing this.
QA2 image

Q3. What would happen if user keep creating pipes, but never use them(pipe leak)?
A3. Pipes will run out. The system will not allow user to create new pipe.
QA3 image