Process: include memory containing instructions, data and a stack. Instructions implement the program’s computation. The data are the variables on which the computation acts. The stack organizes the program’s procedure calls.
Procedure calls: 也叫系统调用,用来给process调用kernel service. 当process调用系统调用时,硬件cpu会提高程序的privilege level然后执行内核中已经定义好的函数.
Processes and Memory
- xv6 进程包括user-space memory和per-process state private to the kernel.
- 当进程不工作时保存CPU Register,下次启动时再restore.
一个进程可以调用fork system call来生成一个新进程叫child process. 原来那个进程叫parent process.
child process 有父进程的memory content.
fork system call returns in both parent and child.
In the parent process, fork returns the child’s pid. In the child, it returns 0.
1 2 3 4 5 6 7 8 9 10 11 |
|
输出结果是:
1 2 3 4 5 |
|
注意:parent process 和 child process 执行在不同的内存和寄存器中,修改其中的一个变量不会影响到另一个进程的执行 * exec(filename, *argv) system call: 从file system 加载指定文件到进程的内存中. 在xv6中,文件格式为ELF. exec执行以后,不会反回原进程,而是继续从载入的文件开始执行.
1 2 3 4 5 |
|
第一个变量argv[0]会被忽略,不起实际作用
- Xv6 shell的执行流程:
- shell 执行 getcmd 获得用户输入的命令
- shell 执行 fork 创建一个shell 进程的copy,然后shell进入wait状态
- shell 执行 runcmd 运行用户的命令
- runcmd函数调用exec 系统调用加载适当的函数如:echo
- 在函数(echo) 的结束,有exit系统调用返回shell, shell从wait中退出
xv86为用户分配内存空间: fork为子进程copy父进程的内存,exec 为可执行文件ELF开辟内存, 当用户需要额外内存时(malloc) 调用sbrk(n)
I/O and File Descriptors
- File Descriptor: a small integer representing a kernel-managed object that a process may read from or write to.
xv6中,所有的object都有file descriptor
- 每一个进程都有private file descriptor table.
- read(fd,buf,n)系统调用: 读取fd中n个bytes到buf. 每个fd中都有一个offset,读取一次都会update offset,以便下次继续读取. 若没什么可读了,返回0,否则返回读取的字节数.
- write(fd,buf,n)系统调用: 向fd中写入buf里的n个bytes. 工作原理与read类似,也有offset 举例(Cat的实现):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
- close 系统调用会释放一个file descriptor。 当有进程申请新的file descriptor时,数值最小的那个fd会被分配给新的object.
实现 I/O 重定向: 先用close释放一个file descriptor,然后重新open一个文件,这样新的文件就拥有了释放掉的fd(因为总是从最小的fd开始分配).
举例(实现cat < tinput.txt):
1 2 3 4 5 6 7 |
|
> Fork 拷贝父进程的file descriptor table 到子进程. exec在载入文件时依然会保留进程的file descriptor table.
- 当父子进程同时操作一个fd时,fd中的offset是共享的.
1 2 3 4 5 6 7 |
|
父进程写的word不会覆盖子进程写的hello. 因为offset是共享的.
- dup系统调用: 复制当前fd,返回一个新的fd指向同一个Object. dup出来的新fd共享之前fd的offset
1 2 3 |
|
> 在不创建子进程的情况下,通过dup实现共享offset。除此之外offset不能共享
Pipes
实现一个简单的Pipe, wc 连接pipe的read end
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
上面代码中, p[0]: read end, p[1]: write end . 子进程必须在wc之前关闭p[1], 否则wc时read end一直等待所有指向write end的fd关闭.
- 如果pipe的read end没有数据, 则read end 要么是等待数据,要么等待所有指向write end的fd都关闭. 若后一种情况发生时,读到的是0,表示读到文件的结束了.
- 如何实现
fork sh.c | wc -l
- child process creates a pipe to connect the left end of the pipe with the right end.
- child process calls runcmd for the left end of the pipeline
- child process calls runcmd for the right end of the pipe
- waits for the left and the right ends to finish by calling wait twice
- pipe 和 temp file的不同之处:
- pipe 可以自动清理
- tmp file需要空间,pipe不需要空间,传递的是data stream
- pipe 可以 synchronization,一个process可以block read等到另一个process写完pipe,再读取.
File System
- chdir 系统调用: 改变当前工作目录
- mkdir 系统调用: 创建一个新的目录, 用open系统调用可以创建新的文件
- mknod 系统调用: 创建新的设备文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
- fstat 系统调用: 可以读取一个fd指向的object的信息. fstat 读取的数据结构定义如下
1 2 3 4 5 6 7 8 9 10 |
|
- link 和 unlink 系统调用: 为已存在的文件创建一个“别名”,但是都指向相同的Innode.
1 2 3 |
|