CS 111 Lecture 6 Note
Designed by Yao Zhang
Orthogonality & Trouble:
If a child process become an orphan, its parent will be linked to process number 1.One process: fd = open("file", O_RDWR); <---unlink("file");//remove the file a similar situation is to unplug USB read(fd...); write(fd...); (Then call read again and write and so on) suppose read(fd) fails, return -1 and set errno When exits, space is reclaimed $ls $du//file base $fu//file system base
parent: int status; pid_t p = fork(); if (p > 0) waitpid(p, &status, 0);//here p must be your child if child calls _exit(37) Then suppose p is 19, we will have the process status of 19 with exit 37 and dead 1 (zombie process)
("init")//part of system
while (waitpid(-1, ) > 0)
continue;
sort.c
int fd = open(sorttmp, O_RDWR | O_CREAT | O_EXCL, 0600);
//O_EXCL means exclusive We can use (0000) instead of exclusive (won't work correctly under root access)
write(fd...);
read(fd...);
close(fd);
unlink(sorttmp);
race condition: a | sort | b | sort | c
char sorttmp[100];
for (int c = 0; i < 1000000; i++){
int r = random();
sprint(sorttmp, "/tmp/sort%d", r);
if (int fd = open(sorttmp, O_RDWR | O_CREAT | O_EXCL, 0600))
break;
}
if (i == 1000000)
error();
$gzip foo
gzip.c: <---int ttyfd = open("/dev/tty", O_RDONLY); fcntl(fd, ...);
fd = open("foo.gz", O_WRONLY | O_CREAT | O_TRUNC, 0600);
[do work] <---on error (eg. write failure) unlink("foo.gz"); exit(1);
close(fd);
unlink("foo");
exit(0);
//foo is gone and foo.gz is compressed version
What if we have a Control-C in middle of gzip?
We want a version that compressed or not (all or nothing, never partially done)
void check_for_interrupt(void) {
char c;
every now and then
read(ttyfd, &c, 1);<---return -1
if (c == 4)
{
unlink("foo.gz");
exit(29);
}
}
Signals:
Here is our implementation of a signal handler:
What can be used in handle_signal(int sig)?
We can use: _exit, signal, pthread_sigmask, close and etc.
We can not use: malloc, printf(which calls malloc), exit and etc.
void
handle_signal(int sig) {
unlink("foo.gz");
_exit(1);
}
Here is gzip.c:
int
main (int argc, char **argv) {
if (open("foo", O_RDWR) < 0), set errno and exit;
signal(SIGINT, handle_signal);
[rest of main];
open("foo.gz", O_RDWR);
[other stuff]; <---signal(SIGINT, SIG_IGN);
unlink("foo");
exit(0);
}
again in gzip:
bool volatile foo_gz_created;
signal(SIGINT, handle_signal);
...
open("foo.gz", O_WRONLY | O_CREAT | O_TRUNC, 0600); --->pthread_sigmask(SIGBLOCK, &old, &new);
foo_gz_created = true; (signal masks containing SIGINT)
(critical section)
Then SIGUNBLOCK
Handler becomes:
void
handle_signal(int sig) {
if (foo_gz_created) {
unlink("foo.gz");
_exit(1);
}
}
foo_gz_created should be declared as volatile since: for example,
int x;
x = 12;
if (x > 0)//Will be optimized out
print("ok");//Will print forever
gcc -O2(Signal do not "trash" variables)
Signals have changed our abstract machine between any pair of machine instructions a signal handler can be called, makes programs harder to analyze.
Keep your handlers simple! Use "volatile" to discourage optimization.
Reasons for signals:
Uncooperative processes (control-c) | SIGINT |
Invalid programs Illegal instructions | SIGILL |
floating point exception | SIGFPE |
Invalid address | SIGSEGV, SIGBUS |
I/O Error | SIGPIPE SIGIO |
A child died | SIGCHLD |
User signals | SIGTSTP SIGKILL |
hang up | SIGHUP |
Timer expires | SIGALRM |
We can use: _exit, signal, pthread_sigmask, close and etc.
We can not use: malloc, printf(which calls malloc), exit and etc.
Threads:
We need threads because:
- Processes are heavy weight
- fork() is slow
- IPC is slow
Expensive stuff (per process) |
address space(heap, code); file descriptors; signal handler table; working directory; umask; process ID; uid/gid. |
Cheap stuff (per thread) | registers; stack; signal mask; errno; thread ID; state. |