| Lecture 5: Orthogonality, Processes, and Races | 
| CS111: Operating Systems | 
| Scribes: Yuhuang Chen, Jianan Li and Chuchu Ding | 
1. Recap of Previous Lectures
Different operating systems deploy different schemes when it comes to kernel abstraction
Examples:
    
    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)
Buffers are located in kernel memory
		
		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?