CS 111 Lecture 4 Scribe Notes

Winter 2012

Lecture date: January 23, 2012

Scribes: Kevin Du and Andrew Law

How to implement hard modularity

  1. Client/Service Organization Client Code:
        send(fact_name, (m) {"!", 5});
        a = get_response(fact_name);
        if(a.response_code == OK)
            print(a.val)
        else
            return ERROR;
            
    Server Code:
        for(;;){
        receive(fact_name, request);
        if(request.opcall == "!"){
            n=request.val;
            //compute n!
            response = (m) {OK, n!}
        }
        else
            response = (m) {NG, "0"};
        send(fact_name, reponse);
        }
            
    Pros and cons:
  2. Virtualization

    Create a virtual machine to run your untrusted code:

    Write an x86 emulator
        int emul(int start_ip)
        {
            int ip;
            char ins = mem[ip++]; // handwaving code
            switch(decode(ins))
                case POP:
        }
    
        r=emul(fact's code address)
        switch(r)
        {
            case STACK_OVERFLOW:
            case TOO_MANY_INSNS:
            case OK:
                get return value of fact;
        }
    		
    Pros and cons:

Hardware assist to make emulators faster

What we need:

  1. A real machine needs to take over when the virtual machine issues a "privileged instruction"(e.g. halt, inb, outb).
  2. A real machine takes over after a time-interval.
  3. The virtual machine has memory access limited to pertinent locations.
  4. Other than the requirements above, it must run at full speed.

This is only possible with architecture that supports this (i.e. a virtualizable processor).

From the application's point of view

normal computation

    a=b*b-c*c;

system call

    write(1,"hello, world\n", 13); //no inb, outb here

This is implemented via a system crash by convention.

The INT instruction is an example of a system call that would accomplish this.

x86

1 byte (if this is 128, it's a system call).

example:

%eax    system call #
%ebx    arg1
%ecx    arg2
%edx    arg3
%esi    arg4
%edi    arg5
%ebp    arg6

This runs in the virtual machine:

ssize_t write(int fd, char const * buf, size_t bufsize)
{
    ...
    asm("int 128");
    ...
}
INT 128

This causes a hardware trap and pushes the following onto the real machine's stack:

RETI is the instruction for return from interrupt.

Virtualization is only 1-way protection

Virtualization is faster than the client and server approach.

OS organization:

The kernel keeps track of the ALU, registers, RAM and I/O registers.

Beyond this, there can be multiple layers of abstraction.

Why would a process not run?

On a single-core machine, at most 1 process can run at a time.

Registers don't always have to be saved as in this case:

n=getpid();

**Protecting memory is harder. For now we'll skip this and cover it later (virtual memory).

Ways for apps to create and destroy processes

Note: You can only wait for your own child process.