466: Operating Systems

Principles of operating system design, including process management, IPC, and networking.

Topics

  • Process and thread management

  • Scheduling algorithms

  • Memory management and virtual memory

  • File systems

  • Inter-process communication (pipes, signals, sockets)

  • Unix system call interface

Projects

Custom Unix Shell (466/shell/)

The final iteration of a multi-stage shell project. Implements a POSIX-style command interpreter with:

  • Input/output redirection (<, >, >>)

  • Pipes (|) chaining arbitrary commands

  • Background execution (&)

  • Built-in commands: cd, exit

int main(void) {
  char **tokenv;
  char *s;
  int tokenc;

  s = prompt();
  while (s != NULL) {
    tokenv = tokenize(s, &tokenc);
    if (tokenv[0] != NULL) {
      handle_tokens(tokenv, &tokenc);  /* dispatch: fork, pipe, redir, builtins */
    }
    free(tokenv);
    free(s);
    s = prompt();
  }
  return EXIT_SUCCESS;
}

Socket Programming Utilities (466/tok/socket.c)

Low-level network programming examples using BSD sockets. Demonstrates TCP client/server setup, connect/accept lifecycle, and streaming data over a socket.

int create_socket(u_short portnum) {
    struct sockaddr_in sa;
    gethostname(myname, MAXHOSTNAMELEN);
    hp = gethostbyname(myname);

    sa.sin_family = hp->h_addrtype;
    sa.sin_port   = htons(portnum);

    s = socket(AF_INET, SOCK_STREAM, 0);
    bind(s, (struct sockaddr *)&sa, sizeof sa);
    listen(s, 3);
    return s;
}

int wait_for_connection(int s) {
    struct sockaddr_in isa;
    int len = sizeof isa;
    return accept(s, (struct sockaddr *)&isa, &len);
}

Token Ring Election (466/tok/tok.c)

Implements a distributed token ring leader-election algorithm using BSD sockets. Processes communicate in a ring topology, passing tokens to elect a coordinator — demonstrating socket setup, process coordination, and distributed consensus.

typedef union {
    char  as_char[2];
    short as_short;
} tToken;                          /* 16-bit token value */

#define NO_TOKEN  ((tToken)(short)-1)
#define send_token(s, t) { tToken _t = t; write(s, &_t, sizeof(_t)); }

/* Each process passes the token to the next in the ring;
 * a process keeps the token only if its ID is the highest seen. */

External Sort via Fork/Exec (466/sort/main.c)

Forks a child process and uses exec to pipe file input through the system sort utility, demonstrating the Unix process model, file descriptor manipulation, and fork/exec/wait patterns.

pipe(a);   /* parent → child (input to sort) */
pipe(b);   /* child → parent (output from sort) */

if (fork() == 0) {
    /* child: wire stdin/stdout to the pipes, then exec sort */
    dup2(a[0], STDIN_FILENO);
    dup2(b[1], STDOUT_FILENO);
    execvp("sort", (char *[]){ "sort", NULL });
} else {
    /* parent: write lines, then read sorted output */
    while (fgets(line, sizeof line, infile))
        write(a[1], line, strlen(line));
    close(a[1]);   /* signal EOF to sort */
    FILE *fin = fdopen(b[0], "r");
    while (fgets(buf, sizeof buf, fin))
        printf("%s", buf);
}

Directory Reader (466/dir/main.c)

Reads and prints a file character-by-character using low-level POSIX I/O, an introductory exercise in file descriptor usage and the read system call.

FILE *dir = fopen("dir", "r");
char c;

/* Read one byte at a time and print char + its ASCII value */
while ((c = fgetc(dir)) != EOF)
    printf("%c %d\n", c, (unsigned char)c);