Tuesday, April 17, 2012
Scribe: Robin Wang
Orthogonality in OS Design
- Processes
- fork
- exit
- waitpid
- int execvp(char const *file,char* const *argv)
- Always returns -1
- file = file to execute
- argv = arguments of ‘main’ for that file
- Aside about ‘const’
- int const* //pointer to constant int
- int* const* //constant pointer to int
- int const //const int
- int kill(pid_t p,int sig)
- kill(getpid(),SIGABRT) = abort()
- Print Date Example
- void printdate(void){
int status;
pid_t p=fork();
switch (p){
case -1: error();
case 0: //child process
alarm(5); //kills process after 5 seconds to avoid infinite loop
execvp(“/bin/date”,(char**){“/bin/date”,0});
error();
default:
if (waitpid(p,&status,0)<0)
error();
if (!WIFEXITED(status) || WEXITSTATUS(status)!=0)
error();
}
}
- Files
- open
- fd=open(“/dev/null”,O_RDONLY)
- fd=open(“/dev/nul1”,O_WRONLY|O_CREAT,0666) //permission = rw-rw-rw-
-
Process Table |
Registers |
File Descriptors (1024) |
1 |
|
|
2 |
|
(Pointer to file descriptons) |
3 |
|
|
... |
|
|
- read
- write
- close
- int dup(int fd)
- Copies pointer to another
entry in file descriptor table
- Equivalent of clone operation
- Comparable to fork
- int dup2(fd,fdclone)
- Close fdclone and dup to fdclonec
- int pipe(int fd[2])
- Bounded buffer (4KB) between process 1 (write) and process 2 (read)
-
Empty |
Data |
Empty |
|
↑ Read Pointer |
↑ Write Pointer |
int fd[2];
if (pipe(fd)<0)
error;
- P1|P2 && P3
- pipe(fd);
- p1=fork();
if (p1>0){
p2=fork();
if (p2>0){
close(fd[0]);
close(fd[1]);
}
else
close(fd[1]);
}
else
close(fd[0]);
- What can go wrong with pipes?
- Last writer dies → reader gets 0
- Reader dies → writer gets killed (by default)
- Writer forks → writes are interleaved
- Shell equivalent: (p1a & p1b) | p2
- Small writes respected
- Large writes are not
- Reader forks → reads are interleaved
- Shell equivalent: p1 | (p2a & p2b)
- Things that can go wrong with file descriptors
- Create pipe reader without writer or forget to close reader, program will hang
- char buf[1024];
int fd[2];
if (pipe[fd]<0)
error();
int n=read(fd[1],buf,sizeof buf); //hangs forever
- File descriptor leak, eventually fills up file descriptor table
- fd=open(“file1”,O_RDONLY);
read(fd,...); //file descriptor leak
fd=open(“file2”,...);
- Source no longer available
- fd=open(“/dev/usb/27”); //USB drive
read(fd,...); //unplug flash drive after read
read(fd,...); //returns -1 and set errno=EIO
- File deleted while file descriptors still exist (lsof command lists open
files)
- fd=open(“/tmp/foo”);
read(fd,...); //rm /tmp/foo after read
read(fd,...); //file cannot be reclaimed while still open
- Creating a temporary file
- int fd=open(“/tmp/sort_tmp”,O_RDWR|O_CREAT|O_TRUNC,0666);
if (fd<0)
error();
[computation here]
if (unlink(“tmp/sort_tmp”)<0)
error();
-
Temp file will
get trashed if
multiple users
run sort
- Use different temp file for each process
- char const template[]=”/tmp/sort_tmp%”PRIdMAX;
char tempname[sizeof(template)-2+sizeof(int)*CHAR_BIT];
int max_t p=getpid();
sprint(tmpname,template,p);
int fd=open(tmpname,O_RDWR|O_CREAT|O_TRUNC);
- Use random name
- for(;;){
struct stat st;
genrandom(tmpname);
if (stat(tmpname,&st)!=0)
break;
}
int fd=open(tmpname,O_RDWR|O_CREAT|O_TRUNC);
- Race condition = other process could create file during this time
- O_EXCL eliminates race condition
- Open file only if it doesn’t exist (atomic action)
- for(;;){
genrandom(tmpname);
fd=open(tmpname,O_RDWR|O_CREAT|O_EXCL,0666);
if (0<=fd)
break;
}
- mkstemp function does this