前言
记录一下 css app lab,下载地址
代码放在了 Github
Data Lab
bitXor
x ^ y = (~x & y) | (x & ~y)
题目限制我们仅使用 & ~
,所以我们想办法代替 |
即可。
1 | int bitXor(int x, int y) { |
tmin
以补码形式返回最小的整数。即:符号为是1,其余均为0。
1 | int tmin(void) { |
isTmax
如果是最大的整数则返回1.
注意三个比较讨厌的数:
0x7fffffff:01111111111111111111111111111111
0xffffffff:11111111111111111111111111111111
0x80000000:10000000000000000000000000000000
1 | int isTmax(int x) { |
allOddBits
判断所有奇数位是否都为1.
1 | int allOddBits(int x) { |
思路:可以先自行构造出一个所有奇数位都为1的标准数,在进行比较。
negate
返回相反数,常识题。
1 | int negate(int x) { |
isAsciiDigit
计算输入值是否是数字 0-9 的 ASCII
值。
思路:先观察0x30-0x39的二进制数有什么特点,发现0x30-0x3f之间的第4、5位均为1。假设 x 是 0 - 9 之间的一个数,!(x << 4 ^ 0x3) = 1,y = x + 0x6, !(y << 4 ^ 0x3) = 1,确保这两个同时成立即可判断。
1 | int isAsciiDigit(int x) { |
conditional
执行运算符 x ? y : z:当 x 不为 0 时,返回 y;否则返回 z。
1 | int conditional(int x, int y, int z) { |
isLessOrEqual
判断 x <= y
1 | int isLessOrEqual(int x, int y) { |
logicalNeg
实现 !
运算
1 | int logicalNeg(int x) { |
howManyBits
用二分法来判断。
1 | int howManyBits(int x) { |
Bomb Lab
其实有re、pwn基础,拆解还不算很困难。(还可以结合ida
分析,不过个人感觉怼汇编理解会更好一些.
1 | from pwn import * |
Attack Lab
1 | from pwn import * |
Arch Lab
$ sudo apt-get install bison flex
$ cd Arch_lab
$ tar xvf sim.tar
$ cd sim; make clean; make
Part A
根据examples.c
给的三个函数,写出对应的y86-64
汇编代码。
csapp p252 给了示例代码,模仿即可。
1 | #sum.ys |
1 | #rsum.ys |
1 | #rsum.ys |
PartB
按照 iaddq
的属性在 sim/seq/seq-full.hcl
中特定的位置添加 “IIADDQ” 即可
Cache Lab
推荐阅读:
Part A
在csim.c
中写一个Cache,使用LRU替换策略。我们目的就是实现一个功能和csim-ref
一样的程序。其实预至的csim-ref
是没有脱符号表的…
数据访问:
- L:Load,数据载入,可能发生1次miss
- S:Store,可能发生1次miss
- M:store后再load,两次访存。1 miss & 1 hit + 可能eviction
1 |
|
Part B
首先要明确,尽管矩阵的转置本身导致对于A矩阵(原始矩阵)的读和B矩阵(转置矩阵)的写不可能同时为连续的(即不可能同时存在连续读和连续写——对A矩阵行的连续读必然导致对B矩阵列的非连续写)。
但只要矩阵的大小小于缓存的总大小,那么在理想的情况下,在最初的强制不命中(即缓存为空导致的不命中)后,整个矩阵都会被加载进入缓存。在这之后的所有对于B矩阵的不连续写的引用都会命中。
在该实验中,缓存采用的是直接映射高速缓存,s = 5,b = 5,E = 1。对于该缓存,总共存在32个组,每个组共32个字节,可以装入8个int型变量,是非常有限的缓存,主要需要解决以下两个问题:
- 直接映射缓存所带来的冲突不命中。观察程序中矩阵存储的位置即可以发现,矩阵A和矩阵B的同一行实际上被映射到了同一个缓存组。当进行对角线的引用时,一定会发生缓存的冲突不命中。需要仔细地处理对角线上的元素。
- 所需优化的矩阵的总大小超出了缓存的总大小。必然导致程序的访存效率低下。
代码见GitHub。
参考:
Shell Lab
任务:
eval: 主要功能是解析cmdline,并且运行. [70 lines]
builtin cmd: 辨识和解析出bulidin命令: quit, fg, bg, and jobs. [25lines]
do bgfg: 实现bg和fg命令. [50 lines]
waitfg: 实现等待前台程序运行结束. [20 lines]
sigchld handler: 响应SIGCHLD. 80 lines]
sigint handler: 响应 SIGINT (ctrl-c) 信号. [15 lines]
sigtstp handler: 响应 SIGTSTP (ctrl-z) 信号. [15 lines]
给出的函数:
int parseline(const char *cmdline,char **argv)
:获取参数列表char **argv
,返回是否为后台运行命令(true
)。void clearjob(struct job_t *job)
:清除job
结构。void initjobs(struct job_t *jobs)
:初始化jobs
链表。void maxjid(struct job_t *jobs)
:返回jobs
链表中最大的jid
号。int addjob(struct job_t *jobs,pid_t pid,int state,char *cmdline)
:在jobs
链表中添加job
int deletejob(struct job_t *jobs,pid_t pid)
:在jobs
链表中删除pid
的job
。pid_t fgpid(struct job_t *jobs)
:返回当前前台运行job
的pid
号。struct job_t *getjobpid(struct job_t *jobs,pid_t pid)
:返回pid
号的job
。struct job_t *getjobjid(struct job_t *jobs,int jid)
:返回jid
号的job
。int pid2jid(pid_t pid)
:将pid
号转化为jid
。void listjobs(struct job_t *jobs)
:打印jobs
。void sigquit_handler(int sig)
:处理SIGQUIT
信号。
tsh应有的内置命令:
- quit: 退出当前shell
- jobs: 列出所有后台运行的工作
- bg
: 这个命令将会向 代表的工作发送SIGCONT信号并放在后台运行, 可以是一个PID也可以是一个JID(job ID)。 - fg
: 这个命令会向 代表的工作发送SIGCONT信号并放在前台运行, 可以是一个PID也可以是一个JID
信号阻塞:
执行信号的处理动作成为信号递达(Delivery),信号从产生到递达之间的状态称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
信号不会丢失,如果信号被阻塞,只会保持信号未决,但是信号不丢失
对用户输入的参数进行解析并运行计算。书上已经给了 demo,我们优化一下即可。
有以下几点需要注意:
SIGCHLD
信号:只要有一个子进程终止或者停止,内核就会发送一个SIGHLD
信号给父进程。- 信号是不排队的。如果返回信号时,发现目的进程正在执行信号处理,那么该信号则会被阻塞,下一个则会被丢弃。因此,不能用信号来对其他进程中发生的事件计数。
- 条件竞争:条件竞争是指一个系统的运行结果依赖于不受控制的事件的先后顺序。本例情况是,如果在父进程能够再次运行之前,子进程终止,返回信号,而此时父进程还没执行
addjob
,而信号处理回收子进程,执行deletejob
,由于还没添加到列表,所以这个函数什么都做不了,而这结束后父进程又会添加子进程,而产生一个永远不会被删除的job。
代码参考tshref 源码:
1 | void eval(char *cmdline) { |
Malloc Lab
先占个坑
Proxy Lab
+1