Lecture 5: Orthogonality, Processes, and Races
CS111: Operating Systems
Scribes: Yuhuang Chen, Jianan Li and Chuchu Ding

1. Recap of Previous Lectures

2. Processes

Code example: program that prints out the time of the day


//Program that prints out the time of the day
bool printdate() {
    pid_t p = fork();
    switch(p) {
        case -1:
            error();
        case  0: {
            static char const date[] = "/bin/date";
            execvp("/bin/date", (char const *){date,0});
            error(); //execvp doesn't return if succeed
        }
        default: {
            int status;
            if (waitpid(p,&status,0) != p)
                error();
        }
    }
    //WIFEXITED: if child terminated normally, returns ture
    //WEXITSTATUS(status) prints out the real exit status
    return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}

//Program that prints out the time of the day to a file
bool printdate(char const *outfile) {
    pid_t p = fork();
    switch(p) {
        case -1:
            error();
        case  0: {
            static char const date[] = "/bin/date";
            int fd = open(outfile, O_WRONLY);
            if (fd < 0)
                error();
            if (dup2(fd,1) < 0)
                error();
            if (fd != 1)
                close(fd);
            execvp("/bin/date", (char const *){date,0});
            error(); //execvp doesn't return if succeed
        }
        default: {
            int status;
            if (waitpid(p,&status,0) != p)
            error();
        }
    }
    return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}

Usual pattern for executing a program:

if(fork() == 0) {
  //Housekeeping;
  execvp(...);
}

Another school of thought, which is simpler and does all jobs in one system call:

int posix_spawnvp(
  pid_t * restrict pid,    
  char const *restrict file,
  posix_spawn_file_actions_t const * restrict file_acts,
  posix_spawn_attr_t const * restrict attrp,
  char * const * restrict argv,
  char * const * restrict envp
);

posix_spawnvp doesn't copy all that memory(imagine you copied 8GB memory only to do an execvp), so it is very efficient and fast and cheap.

3. Orthogonality

Comparison of Linux’s files:

4. IPC (Interprocess Communication)

5. Race Conditions

Code example: creating a temporary file that stores sorted data.


// create_temp_file returns the new file descriptor, or -1 if an error occurred

//Proposal:
int create_temp_file(void) {
    return open("/tmp/sort.tmp",O_RDWR|O_CREAT|O_TRUNC,0600);
}

//Problem: when two sort programs are running at the same time, one will trash the other's temp file!

//Fix:
int create_temp_file(void) {
    char name[1000];
    sprintf(name, "/tmp/sort.%d", getpid());
    return open(name, O_RDWR|O_CREAT|O_TRUNC,0600);
}
//name[1000] is an char array big enough to hold our temp file's name
//sprintf() puts "/temp/sort." and our process ID into name[1000]. (e.g. "/temp/sort.30149", where 30149 is our process ID) Thus, no two sort programs running at the same time can trash each other's temp files because each process has a unique process ID!

//Problem: what if someone else has already created the same file and you don't have the access to open it?

//Fix:
int create_temp_file(void) {
    char name[1000];
    int fd;
    while (1) {
        sprintf(name, "/tmp/sort.%d", random());
        if((fd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_EXCL,0600)) > 0)
            return fd;
    }
}

//now we have a loop, and a random name generator. O_EXCl flag ensures that when the file with the same name already exists, open() should fail.

//Simplified version:
int create_temp_file(void) {
    for(;;) {
        sprintf(name, some_random_string());
        if(open(name, O_RDONLY|O_CREAT|O_TRUNC|O_EXCL))
            return open(name, O_WRONLY, 0600);
    }
}

//Problem: now race condition arises! What if two sort call open() on the same file at the same time?