by Hanfeng Liu, Yingjun Shen, Yue Luo, and Zhengliang Wu
Orthogonality is an important principle to design operating system or any other big projects. It is very useful in managing complexity. Projects designed with orthogonality have no side effects theoretically. For operating system, we want processes to be independent with files. Without orthogonality, it will be painful when projects are getting bigger. |
file category | processes |
open | folk |
read | _exit |
write | waitpid |
close | |
lseek |
In linux system, the way to access files is unified, no matter regular files, devices files or network files. However, there are two different kinds of devices:
Because linux uses the same way to access files, it uses same set of system calls for both kinds of devices.
In Lab 1b, we deal with some of file-accessing system calls:
Among the above 5 system calls, "open", "read" & "close" work for both kinds of devices.
wc /dev/mouse //intends to count the number of words of the mouse input file should be able to run (not necessarily be able to fulfill our goal though)
But there are problems:
cp /dev/disk/01 backupfile //backup the file in disk to backupfile should be able to copy the content in /dev/disk/01 to the file "backupfile".
In the program "cp", system call "lseek" is used. It it obvious that "lseek" only works with random access devices such as disk and memory, becasue they have finite size and so finite offset.
lseek (fd, offset, flag) & read (fd, buf, bufsize) -------> pread(fd, buf, bufsize, offset)
Use of "lseek" often associates with "read", but Linux only provides those two system calls separately. So later people combined "lseek" & "read" into a simple system call: pread, which is more efficient (only 1 syscall), more abstract, simpler
open is the special one which uses filenames: open("a", 0_W/R_ONLY | 0_CREAT |…)
int execvp(char const* file, char* const* argv);
The argv list:
void printdate(void){
execvp("/bin/date", (char*[]){"date", "-u"/universal time, 0};
perror("/bin/date"); //output result in stderror
}
What is the problem with this program?
If execvp successfully executes /bin/date, it will destroy the program.
How to make the program work correctly: use fork!
pid_t p = fork();
if (!p) { //if child
execvp("/bin/date", (char*[]){"date", "-u"/universal time, 0};
perror("/bin/date");
_exit(127);
}
if (p < 0)
perror("fork");
else {
int st;
if (waitpid (p, &st, 0) !=p)
perror("waitpid");
}
fork() clones current process except:
Details about file descriptor:
The structure after fork():
parent
waitpid
child
close, open
execvp
int posix_spawnvp (pid_t *restricted pid,
char const *restricted file,
posix_spawnfile_actors_t const *file_acts,
posix_spawnattr_t *restricted attrp,
char *const *restricted argv,
char *const *restricted envp);
"restricted" means caller promises not to alias args. For example, strcpy(a,b) is actually: char* strcpy(char *restricted dest, char *restricted stc);
The posix_spawnvp (…) is a big syscall, but it violates the ideal of orthogonality.
+ It is simple to implement and use.
+ It is fast.
- There is typically no protection.
- There are race conditions.
Handles can be opaque identifies, which are always integer numbers. With "handles", user can only read information but can not locate the memory address.
There are examples:
More subtle issues:
There are 3 options for case "write to full pipe & no readters" to exit:
int main(void)
{
while(1)
printf("yes\n");
}
If this program is not modified, it will constantly be printing "yes".
There are three ways to exit:
Q1. No readers now, but a reader may show up later?
A1. Can’t happen here.
Q2. What happen if two pipes both write to and read from each others?
A2. They both wait. This is a deadlock. User should avoid doing this.
Q3. What would happen if user keep creating pipes, but never use them(pipe leak)?
A3. Pipes will run out. The system will not allow user to create new pipe.