CS111

Scribe Notes for 10/4/05

by Alexandre Serriere, Brian Taing, and Andrew Tran

Review from previous lecture

 

Components of a computer made available:

  1. User mouse
  2. mouse driver
  3. screen
  4. memory
  5. disk

 

On the screen, it shows that two applications are running:

  1. a program with a start button, and
  2. an mp3 program

 

Both programs are waiting for a mouse click from the user; so the question is how should this be implemented step by step? 

 

  1. From our previous lecture, a possible solution was to allocate one bit of memory to write either a ‘1’ (for when the mouse has been click) or ‘0’ (not clicked) and have the application constantly check that block of memory to find a mouse click.

 

    • The method above is known as polling and it was determined in class that while it may work, it is not an efficient way of handling a mouse click because the computer is constantly checking for a mouse click, when instead the computer could be spending it’s time working on other tasks.

 

  1. The other method to try is interrupts:
    • Interrupt processor on click
    • Need safe sharing
    • OS kernel privileged – Provides the mechanism for safely sharing resources by handling interrupts and distributing them to the different programs

 

  1. Use of interrupts via System Call
    • Protected control transfer from application to kernel
    • Kernel’s API (Application Programmer Interface)

 

Prof. Kohler initially suggests this system call for the mouse:

sys_wait_mouse.click(*position)

{

      -put process to sleep (i.e. block this process)

      -until there is a mouse click

}

 

Problem with above system call design:

Functionality (abstraction): the system call is too specific and not easy to generalize (for instance, this is hard to combine with other sets of possible events, such as waiting for a push of the “Enter” key from the keyboard).

 

With UNIX, there is only ONE abstraction to represent ALL data communication,

 

The abstraction is done via a FILE.

Files

The file is UNIX's one big idea. It is one abstraction to represent all data communications. These include:

  • Waiting for a mouse click
  • Typing
  • General interaction with hardware
  • Disk files
  • Network communication

Files are described by file descriptors (fd) and can be accessed using just a few simple commands:

  • open(path, flags, [mode])
    • note that the path can refer to a disk file (/home/kholer/cs111) or hardware interaction (/dev/mouse)
    • different flags allow for different methods of access:
      • O_RDONLY - read only
      • O_WRONLY - write only
      • O_RDWR - read and write
      • O_CREAT - on open, create the file if it does not exist
      • O_EXCL - gives an error if the file already exists
  • read(fd, data, len)
  • write(fd, data, len)
  • close(fd)
  • rename(old, new)
    • effective for moving files aswell
  • unlink(path)
    • delete
  • select(set of fd)
    • waits until at least one file is readable (has an event) and returns which one(s) are readable

It is important to be careful when reading from a file if no data is available. If the file is finite, we can simply return 0 as the end of the file. However, if the file is infinite, say a data stream from a piece of hardware, we have two options:

  • Blocking Read: block until the data we want is available
  • Nonblocking Read (O_NONBLOCK): return a value indicating nothing is currently available (EAGAIN)

Another aspect of files that can be troublesome is the idea of exclusive access. If a file is being edited, another user, or process, should not be able to step in and edit the file. In order to prevent this, we might implement the following code:

 
fd = open(F, O_WRONLY);         //attempt to open the file F
if (fd >= 0)                    //check if there was anything in F beforehand
  {
   close(fd);            //if there was something, that's bad, someone
   //explode;            //has already created our file and we should stop 
  }                                                            
fd = open(F, O_WRONLY | O_CREAT);//open F and create it since it doesn't exist

The problem with the previous code is that a race condition can occur if two processes execute it at the same time. System calls are serializeable, meaning if two calls occur at the same time, the operating system chooses one to go first. With this code, the operating system might allow the first process to attempt to open the file, run its check, but then block it to wait for the second process. The second process could then attempt to open the file (again) and run its check and, finding no such file, create it. Then the first process would become unblocked and try to create and open the same file again, resulting in unpredictable behavior. To fix this code, we use the O_EXCL flag:

 
fd = open(F, O_WRONLY | O_CREAT | O_EXCL);             //attempt to open the file F
                                               //O_EXCL will generate an error
                                               //if the file already exists

Now, the program will behave as expected and one process will do the creating and the other will get an error.

Process

What is a process?

A process is a running instance of a program.

The relation between process and program is many-to-one: a process is always corresponding to a program, but a program can have multiple processes corresponding to it.

In general, an operating system process consists of:

  • Memory.
  • Operating system resources that are allocated to the process, such as file descriptors.
  • Security attributes, such as the process owner and the process's set of permissions.
  • Processor state, such as the content of registers, physical memory addressing, etc. The processor state is associated with each of the process's threads.

Every time you issue a command, the OS starts a new process, and suspends the current process until the new process completes (except in the case of background processes.)

The OS identifies every process by a Process Identification Number (pid) which is assigned when the process is initiated. When we want to perform an operation on a process, we usually refer to it by its pid.

 

How to create a process?

  • The fork() (no parameter needed) function creates a new process. The new process, the child process, is an exact copy of the calling process, the parent process.
  • If successful, fork() returns 0 in the child process, and returns the process ID of the child process to the parent process.
  • Even though the child process inherits some attributes from the parent process, the child process also has its own attributes such as a unique process ID, a different parent process ID (i.e., the process ID of the process that called fork()).

e.g.:

                   int x;

                   pid_tp = fork();

                   x = p;

                   printf (“%d\n”, x)

 

    Output: 2, 0

 

Some functions that handle processes:

  • ps -- lists all the processes that you own.
  • ps aux -- lists all the processes on the machine.
  • kill() -- terminates a process by using the kill command. We can find a process’s pid (by using ps), and then type kill [pid]
  • void exit(int status) -- terminates the process which calls this function and returns the exit status value.
  • waitpid (pid, statusp, options) -- waits for process termination.  The function suspends execution of the current process until a child as specified by the pid argument has exited.

 

Some useful functions that handle files:

  • execvp(file, arguments) – replaces the program with a fresh copy of ‘file’ and passes the ‘argument’.

E.g.:

            fd = open(“/dev/mouseclick”, O_RDONLY)

            read(fd, &data, 1);

            pid_t_p = fork( );

            if (p == 0) execvp(“gifviewer”, 0);

  • head -[number of lines]  [file name] -- takes a line, print out the first number of line specified.
  • sort [options] [file name] -- sorts lines of a file.