有没有门友做过 PKU 的 Pintos 啊,在犹豫做原版的还是 PKU 的,我看了下好像把最后一个实验拆成了两半
Windows 装 docker 蓝屏了
拿电脑店修去了 tmd
学 os,然后被 os gank
好像在哪里看到过某个(比较新)版本的 docker 与 Windows 的 hyper-v 之间有点协作不好……感觉自己电脑是 Windows 的话,不如直接装 VM 玩算了
就是 Hyper-V,我开安全模式然后把一系列 Hyper-V 组件全删了就好了,
去薅台 aliyun 来做实验吧,正好最近 aliyun 在撒钱
我有一台闲置的 Linux 电脑,于是决定使用这个旧电脑来操作,话说我已经买了阿里云的云服务器,这种情况下还能优惠吗?
新活动好像是学生原价三折优惠 +300 元优惠券并且可叠加,已经买过这次应该也可以搞台免费机器玩,这个和新客优惠无关
决定使用 pku 版本的 pintos
刚刚做完了 lab0,gdb 搞得我有点头昏眼花的
我都没用过 gdb,都是 clion 打断点,然后 alt+shift+v eval expression,debug 面板看内存变量,断点好像也能设置条件
wow,那跟 gdb 好像也每什么区别吧。
clion 调汇编方便吗?
这就涉及我的盲区了,我都不知道什么是汇编
就是汇编语言,比起 C 要低级一些
来个链接看看 pku 版本的 lab
Lab0
别的没什么好提的,大概熟悉了一下 Pintos 的编译运行和调试。
最后一个 Ex 是要求写一个非常简单的 terminal,只需要执行非常很少量的工作,然而这确实有助于我理解我们平时使用的 terminal 是如何编写的。
在 input.c 文件中我找到了以下代码:
/** Retrieves a key from the input buffer.
If the buffer is empty, waits for a key to be pressed. */
uint8_t
input_getc (void)
{
enum intr_level old_level;
uint8_t key;
old_level = intr_disable ();
key = intq_getc (&buffer);
serial_notify ();
intr_set_level (old_level);
return key;
}
这段代码会即时地 return 一个输入的 key,因此只需要调用这个函数并加上一定的逻辑处理,就可以用来实现我们的 terminal 了。
不过这显然并不令人满意,继续递归检索 intq_getc() 函数,会得到如下代码:
//intq.h & intq.c
/** An "interrupt queue", a circular buffer shared between
kernel threads and external interrupt handlers.
Interrupt queue functions can be called from kernel threads or
from external interrupt handlers. Except for intq_init(),
interrupts must be off in either case.
The interrupt queue has the structure of a "monitor". Locks
and condition variables from threads/synch.h cannot be used in
this case, as they normally would, because they can only
protect kernel threads from one another, not from interrupt
handlers. */
/** Queue buffer size, in bytes. */
#define INTQ_BUFSIZE 64
/** A circular queue of bytes. */
struct intq
{
/* Waiting threads. */
struct lock lock; /**< Only one thread may wait at once. */
struct thread *not_full; /**< Thread waiting for not-full condition. */
struct thread *not_empty; /**< Thread waiting for not-empty condition. */
/* Queue. */
uint8_t buf[INTQ_BUFSIZE]; /**< Buffer. */
int head; /**< New data is written here. */
int tail; /**< Old data is read here. */
};
/** Removes a byte from Q and returns it.
If Q is empty, sleeps until a byte is added.
When called from an interrupt handler, Q must not be empty. */
uint8_t
intq_getc (struct intq *q)
{
uint8_t byte;
ASSERT (intr_get_level () == INTR_OFF);
while (intq_empty (q))
{
ASSERT (!intr_context ());
lock_acquire (&q->lock);
wait (q, &q->not_empty);
lock_release (&q->lock);
}
byte = q->buf[q->tail];
q->tail = next (q->tail);
signal (q, &q->not_full);
return byte;
}
大略的看了一下,这个interrupt queue
看起来就是一个阻塞式循环队列,略有不同的是,该队列从 tail
字段处取走字符,从head
字段处增加字符。
不过令人惊奇的是该队列的条件变量和锁都是自己实现的,看起来很有意思,不知道会不会在后续的实验中让学生也参与进这部分代码的编写。
除此之外还有一个有趣的技巧,我们发现在实现一个 terminal 的时候,仅仅拥有 input_getc
或许是不够的,一个比较麻烦的情况是:我们键入了Backspace
退格键,该如何将已经显示的字符取消呢?
为此我了解了一个 '\b'
字符,该字符可以将光标向前移动一格,但并不会更改已经存在的字符,他对显示出的字符的更改存在于未来——当下一个字符被输出的时候,会覆盖掉光标后的一个元素,如果光标向前移动一格,同时被空格覆盖,那么我们就可以实现类似退格的效果。
不过有一些不太好的 bug:
例如运行printf("1234\b 567")
将会输出字符串"123 567"
,平白无故多出的空格将无法处理。
因此我们将目光从一字符后的未来放到更远的二字符后的未来,我们就会明白,空格也需要被覆盖。
对上面的例子作出更改可以得到:printf("1234\b \b567")->"123567"
。
对应可得:Backspace = \b \b
如果想要更加仔细的看代码,可以访问这个链接
一般 lab 的实现代码不是不让公开吗
各种 cs 开头的课 GitHub 上一搜一下吧,谁管,能管得到吗?
Lecture 3. Thread & Process
- Why threads?
Kubi 提出了 MTAO 的概念(他自己造出来的词儿,Multiple things at once),同时做多件事。
因为需要线程去实现 MTAO。 - parallelism & concurrently
Multiprocessing 是 parallelism,Multiprogramming 不是 parallelism,他们都是 concurrently。 - syscalls
上图系统调用示意图。
疑问:为什么我们没有见过 syscall,这是因为往往 syscall 被隐藏在我们运行的语言的函数接口之下。这是因为在不同的操作系统上,syscall 是不同的,因此为了同一语言在不同操作系统上的运行,采用了这种隐藏的方式。
然而 syscalls 是有一个子集被标准化了的,这个子集称为 POSIX syscall interface,这一组 syscalls 在大部分系统上以同名实现。 - Threads State
信息共享情况如图 - Execution Stack
这个图很直观的展示了在函数递归的过程中,运行时堆栈会保存的信息,首先是参数,然后是临时变量(如果有),还有 ret,ret 表示的是结束调用后需要执行的下一条指令的位置。
上图是同一进程中不同线程的运行时堆栈的内存布局。
这涉及到一些有趣的问题:当一个栈增长得过长,将会搞乱另一个栈。
因此为此设立了 Guard Page,当栈触及这个页面的时候会陷入内核,并且决定是应该让栈获得更大的内存,抑或是终止线程,Kubi 说后面的课程会提到。 - Threads Race
多线程竞争资源,指令交错执行,老生常谈 - 一些和阻止多线程错误执行相关的概念
同步:协调多线程并让其正确执行的手段,通常于线程间共享的数据有关
互斥:同时只有线程作某件特定的事情,属于一种同步手段
Critical Section:同时只有一个 Thread 执行 Critical Section 的代码,Java 中可以使用 Synchronization 关键字来制作一段拥有这种功能的代码
锁:用来使某个对象同时只有一个 Thread 可以访问,属于一种互斥手段 - pthread(POSIX Thread)
pthread_create,创建线程
pthread_exit,退出线程
pthread_join,等待某个线程结束
pthread_mutex_init,创建锁
pthread_mutex_lock,阻塞式地获取锁
pthread_mutex_unlock,释放锁 - Process Create
每个进程都由其他进程创建。
宇宙大爆炸:在内核启动前,会有一个进程作为参数放入内核,这通常被称为 init 进程,然后 init 进程作为整个进程树的树根存在。 - PV operation for mutex
使用 PV(down/up) 操作来实现锁和 pthread_join(区别在于不同的阻塞位置和信号量的初始值)。
P 操作会等待信号量变为正数时给信号量 -1,V 操作会给信号量 +1,并告诉一个 P 操作停止等待 - Proccess APIs
exit,终止进程,进程正常 return 0 未调用 exit 时,OS 会自动为该进程调用 exit(0)
fork,开启子进程,对于父进程,返回子进程的 pid,对于子进程,返回 0。子进程会复制父进程的地址空间,并在新的地址空间中从同样的位置开始运行,子进程只会运行调用 fork 的 thread,其他所有的 threads 在子进程中会被终止
exec,没听懂,说是用来复制父进程的地址空间,然后把原来的副本全部删除,并在新的地址空间中运行的意思
wait,等待子进程运行完毕,并在参数中返回其 exit 的结果
sigaction,信号,有固定的信号表(例如 SIGINT 表示 Ctrl-C),也可以实现定制化的信号调用函数
是指我个人的实现代码吗?
我见 github 上一堆人发,没太在意这个事
主要是感觉对于本校的,想抄作业的话还是很简单的,如果对于外校的抄不抄本来就是全凭自觉吧 ovo。
如果是指 lab 自身的代码的话似乎 PKUOS/pintos 是开源的。