Lecture 1 操作系统概述 https://pdos.csail.mit.edu/6.828/2018/lec/l-overview.txt
概述
-
6.828 课程目标:
- 理解操作系统设计和实现
- 自己开发一个小型操作系统,获得一手经验
-
操作系统的意义:
- 支持应用
- 将硬件抽象,变得方便调用和可移植
- 让硬件在不同软件间多路复用
- 隔离应用程序,把bug限制在应用程序内部
- 允许应用程序之间恭喜那个
- 提高性能
-
O/S设计方法是什么?
- 具体:管理硬件的库
- 宏观:将物理机器变成有更好用的属性的抽象概念
-
层级
- 硬件: CPU、mem、磁盘等
- 内核服务
- 用户应用程序:vi, gcc等 我们要非常关心接口和内部内核结构
-
操作系统内核通常提供什么服务?
- 进程
- 内存分配
- 文件内容
- 目录和文件名
- 安全
- 其他很多例如:用户、IPC(应该是进程间通讯Inter-Process Communication)、网络、时间、终端 介绍系统调用
-
系统调用
ls调用了哪些系统调用?- macOS 使用 dtruss 来看:
sudo dtruss ls - 控制台先打印出ls的结果,然后可以看到调用了超级多的系统调用!
- macOS 使用 dtruss 来看:
ps: macOS 有一个保护程序,禁止了drace 或者dtruss的调用。解决的办法是可以在恢复模式里禁止保护模式: https://stackoverflow.com/questions/33476432/is-there-a-workaround-for-dtrace-cannot-control-executables-signed-with-restri
Although not recommended by Apple, you can entirely disable System Integrity Protection on you Mac. Here's how:
1. Boot your Mac into Recovery Mode: reboot it and hold cmd+R until a progress bar appears.
2. Go to Utilities menu. Choose Terminal there.
3. Enter this command to disable System Integrity Protection:
`csrutil disable`)
copy- UNIX传统:fd 0为“标准输入”,1为“标准输出”, 2为“标准错误输出”
- 一个更有趣的程序:Unix Shell
-
它是Unix命令行用户界面
-
它很好地说明了UNIX系统调用API
-
一些命令: ls ls > junk ls | wc -l ls | wc -l > junk
-
shell 也是一种编程/脚本语言 cat > script echo one echo two sh < script
-
shell 使用系统调用来设置重定向,管道和等待。 像
wc这样的程序是不知道输入输出设置的(ps:
man wc可以看到wc的定义、使用方式)
-
看一个简单的shell的源码
( 找不到老师的源码,找到了这个:sh.c ,可以参考)
main ()-
基本过程:接受输入的命令,解析成树,然后运行
-
主进程做的事情:
getcmd、fork、wait -
子进程做的事情:
parsecmd、runcmd -
为什么
fork ()?- 我们需要一个新的命令进程
-
fork()做什么? ( ps:man fork看fork详细介绍)- 复制用户内存
- 复制内核状态,例如文件描述符
- 所以 child 和 parent 几乎是一样的
- 子进程具有不同的“进程ID”
- 现在两个进程并行运行
fork()返回两次,一次在父进程中,一次在子进程中fork()将子进程的 pid 返回给父进程fork()将 0 返回给子进程- 所以sh是在子进程中调用的runcmd()
-
为什么
wait()? (父进程等待子进程执行,子进程退出后唤醒父进程)- 如果子进程在父进程调用
wait()之前退出怎么办?(会导致没有办法唤醒父进程,表现为操作系统卡死)
- 如果子进程在父进程调用
-
runcmd()- 执行
parsecmd()生成的解析树 - 不同的cmd类型的简单命令、重定向、管道
- 执行
runcmd(),用于带有参数的简单命令execvp(cmd, args)man execvpls命令是一个可执行文件,在 /bin/ls 文件夹中execvp在当前进程的内存上加载可执行文件,跳转到可执行文件开始的位置,也就是main()- 注意: 如果一切顺利,execvp不会返回
- 注意: execvp()只有在找不到可执行文件时才返回
- 注意: 用execvp()替换的是shell子元素
- 注意: 主shell进程仍然是wait() 子进程
runcmd()如何处理I/O重定向?- 例如
echo hello > junk - parsecmd()生成具有两个节点的树
- cmd->type = ' > ', cmd ->file=“junk”, cmd -> cmd =…
- cmd->type=' ', cmd -> argv=["echo", "hello"]
open ()和dup2()将FD 1替换为输出文件的FD- 改变其FD 1的是shell子进程
execvp保存FD设置- 因此
echo运行时,FD 1 连接到了文件 junk - 这很好,因为对于上述重定向过程echo,是无感知的的,只需要把内容写到FD 1中
- 例如
- 为什么fork和exec是分开的?
- 也许通过fork复制shell内存是浪费,因为马上复制的内存会在 exec 执行完后扔掉
- 要点:子进程在调用 exec 前,有机会改变FD设置,并且父节进程的的FD集不会受到干扰
- 将在lab中实现一些技巧来避免fork()复制成本
- shell如何实现管道?
- $ ls | wc -l
- 内核提供了一个管道抽象
int fds[2]
pipe(fds)
- 一对文件描述符:一个 write FD 和一个 read FD
- 数据写入写入write FD,出现在read FD上
- 对于
ls | wc -l, shell 需要:- 创建一个pipe
- fork
- 设置 fd 1 为 pipe 的 write FD
- 执行 ls
- 设置 wc 的fd 0 为 pipe 的 read FD
- 执行 wc
- 等待 wc