OS_lab0
Lab0实验报告
思考题
Thinking 0.1
题面
1 | |
回答
1.cat Untracked.txt与cat Stage.txt命令输出内容的区别在于前者显示README.txt文件未跟踪,而后者则显示该文件已暂存需要提交,区别于README.txt文件的状态。同时由于在git add README.txt和git commit -m “<message>”这之间创建了Stage.txt文件,所以在前后者显示的输出中都有Untracked.txt未被跟踪,而后者此外还有Stage.txt文件未被跟踪。
Untracked.txt文件内容如下:

Stage.txt文件内容如下:

2.cat Modified.txt命令的输出与第一次add之前的输出不一样,至于原因,我认为是由于修改后,该文件版本同最近一次提交的版本中的并不相同了,但是这个文件曾经被git add进入暂存区,也就是被Git跟踪过了,因此该文件被修改后处于的是被修改的状态,而非未跟踪状态,但是根据Git提供的提示,仍需要通过git add <filename>命令来更新暂存区文件。
Modified.txt文件内容如下:

Thinking 0.2
题面
1 | |
回答

(摘自Git - 记录每次更新到仓库)
图中的Add the file代表了git add <filename>命令表示将未跟踪的文件添加到暂存区中,Stage the file也是git add <filename>命令表示将处于修改状态的文件更新至暂存区中,commit是git commit -m "<message>"命令表示将暂存区中的文件提交到HEAD区(也就是最近的一次提交)中。
Thinking 0.3
题面
1 | |
回答
1.假如print.c文件并未被跟踪,被错误删除后,将无法采用命令来恢复。假如print.c被跟踪,也就是被放入暂存区或者是被提交到HEAD后,可以使用git restore print.c丢弃在工作区中对于print.c文件的修改,将print.c重新恢复回来。本质是将暂存区的版本重新赋给工作区。
2.同1,若print.c未被跟踪,错误删除时就不可再用命令来恢复。若被跟踪,则可以先使用git reset HEAD print.c来撤销对暂存区的修改,也就是恢复到仅在工作区删除print.c的状态,然后再使用git restore print.c将暂存区赋给工作区,恢复print.c文件。
3.可以采用git rm --cached hello.txt,将该文件从暂存区删除但并没有在工作区中删除,仅起到移除的作用。
Thinking 0.4
题面
1 | |
回答
1.使用git reset --hard HEAD^后HEAD会回退到前一个git commit之前的状态,也就是回退到上一个版本,此时使用git log会显示当前处于第二次git commit后的状态。
2.再使用git reset --hard <hash>会直接切换到哈希值为<hash>的提交状态,因此每次使用该命令移动的都是HEAD这个指针,因此使用该命令可以进行版本回退和前进。因此,每次使用该命令后执行git log显示的就是以当前HEAD所指向的提交版本及此之前的版本组成的提交日志。

Thinking 0.5
题面
1 | |
回答
echo first将输出first到终端上;
echo second > output.txt将会将second输出到output.txt文件中;
echo third > output.txt将会用third覆盖掉原有的second;
echo forth >> output.txt将会将forth追加到output文件中third之后。

Thinking 0.6
题面
1 | |

回答
command文件内容:

result文件内容:

结果解释:test文件内容如下:

当以shell脚本批处理时,第4、6、8行代码会分别将a、b、c赋值为1、2、3.再者经由11、13、15行代码将c、b、a的结果输入到file1、file2、file3中,第15、16、17行代码再将file1、file2、file3的内容输出到file4中,其中file1覆盖输入,file2和file3均是追加输入,第19行代码再将file4的内容追加输入到result文件中。
因此,运行后,result文件中的每行内容分别为file1、file2、file3的内容,也就是c、b、a,即3、2、1。
Thinking A.1
题面
1 | |
回答

1.由于页面大小为4KB,虚拟地址空间为512GB,则一共有128M个页表项,且这些页表项同虚拟地址空间线性映射,因此PTbase对应的应该是第(PTbase >> 12)个页表项(其中一个页表项代表一个页目录项)。因为一个页表项占8B,则第(PTbase >> 12)个页表项相对于页表基地址的偏移为(PTbase >> 12) * 8,则二级页表页目录的基地址为(PTbase >> 12) * 8 +PTbase = PTbase >> 9 + PTbase。同理可得:三级页表页目录基地址为(PTbase >>18 + PTbase >>8 + PTbase)。
2.映射到页目录自身的页目录项(自映射),题目要求不明,并不知道是要要求什么?是要求页目录项的内容还是地址?如果是内容的话,未知,无法求解。如果是地址的话,同第一问:应该是(PTbase>>27 + 3 * PTbase>>18 + 3 * PTbase>>9 + PTbase)。
lab0课下的一些可能的易错点和踩的坑
Exercise 0.1
1.palindrome.c的编写,回文数的判断,我的思路是先将输入的n按每一位分解到一个数组中,然后从数组头尾进行比较,一旦发现不同就输出N,这个应该都不会出错吧!
2.将.c文件编译成可执行文件的方式为:gcc xxx.c -o xxx;也可以先只编译然后再链接:gcc -c xxx.c -o xxx.o、gcc xxx.o -o xxx。
3.获取某个文件的某一行的内容可以使用sed命令,通过sed -n '{num}p' xxx.txt可以从xxx.txt文件获取其{num}行的内容并显示在终端上,当然你可以通过重定向将输出内容输出到文件中,注意:>是覆盖式输出,而>>才是追加式输出。
Exercise 0.2
1.shell里内声明定义的变量需要前置$才可使用,并且使用的时候是直接替换,部分用法和宏有点**”类似”**。
例如:
1 | |
2.注意,循环里循环变量的改变,在shell中,可以通过如下方式进行:a=$[$a+1],通过询问GPT,我得知了[$a+1]是旧式的算术式,其将告诉shell将[$a+1]当作算术式计算,最终得到”2”的结果后再赋给a,但是GPT并不推荐它,确实令人很难以理解,此外其推荐了如下的新式算术扩展:
1 | |
3.删除非空的目录,可不能再用rmdir了,它只能删除空的,那该怎么办呢?注意到rm的-r选项好像也可以删除目录啊,因此我采用rm -rf filex,这里要注意了哈,从打出-rf高亮为红色,也可以看出,这是个危险的命令选项,因此,我们只要删除要我们删除的目录即可,**切勿执行rm -rf *或rm -rf ./***,否则后果不堪设想。
Exercise 0.3
1.可以使用grep命令查找有xxx字符的地方,通过加上**-n选项可以显示相应的行数,利用通道将输出作为输入给awk命令,此前,可以先执行一下grep命令的输出格式,观察到相应使用的分隔符后,即可在修改分隔符的基础上利用awk命令输出行数**。
Tips:传参的时候如果最终的命令形式是文件或字符串一类的,可以用双引号括起来,这样可能会减少”歧义”。
Exercise 0.4
1.使用sed命令可以实现字符串的替换,记得加上-i选项才会对源文件修改啊!-n只是安静模式哟,只会输出改后的效果,但是并不改(我会输出,但是我不改)。此外,带上/g才会全部替换,否则,替换的只会是每行的第一个匹配的。
2.code文件夹内的fibo.c依赖于本文件夹内的main.c和另一个文件夹的fibo.h,因此在相应的编译的过程中需要使用-I选项来指定头文件的路径,例如:-I../csc就是告诉shell有个头文件在前一个目录的csc文件夹内。此外,最终生成的时候一定要把所有有参与到达文件一块链接,否则,大有出现找不到入口的bug的情况。
3.不确定的地方一律使用绝对路径,当然由于评测机的根路径会与我们的本地有所不同,因此,应该确保你使用的决对路径里没有题目提供的文件树以外的东西,否则,会出现编译错误的情况,都找不到了可不出错了。
4.make内近似于并行执行,因此,你原先设定的顺序并不一定是最终的执行顺序,可以使用以下三种方式解决:
此处摘自Make 命令教程 - 阮一峰的网络日志)
1.把两行命令写在一起,中间用分号分隔:
1
2var-kept:
export foo=bar; echo "foo=[$$foo]"2.在换行前加反斜杠转义\:
1
2
3var-kept:
export foo=bar; \
echo "foo=[$$foo]"3.加上
.ONESHELL:命令:
1
2
3
4.ONESHELL:
var-kept:
export foo=bar;
echo "foo=[$$foo]"
上机前需要注意的
最好在每个脚本文件末尾手动写一个
exit 0来避免报错,$?是上一条语句返回值,$#代表传参个数,$*代表内容包含所有参数的字符串。文件处理三剑客
grep、sed、awk,一定要熟练掌握,例:如何输出hello.c中所有包含"os_hello"(区分大小写)的行的”os_hello”左边的内容,若一行出现多个,则输出第一次出现的左边的内容。grep+sed:1
grep -E '.*os_hello' hello.c | sed -E 's/(.*)os_hello.*/\1/'(不推荐,太过于复杂了,如果你能轻松编写的话,那
当我没说)awk:1
2
3
4
5awk -F 'os_hello' '{print $1}' hello.c #这是一种错误的写法,这样会导致没有'hello'的那一行将会整行输出。
#如下是正确的写法(即先利用grep命令找到有hello的每一行,然后再利用awk来输出以'hello'为分隔的第一块内容):
grep 'os_hello' hello.c | awk -F 'os_hello' '{print $1}'
awk '/os_hello/ { sub(/os_hello.*/,"");print}' hello.c #也不是很推荐,过于复杂了#var获取变量var长度,${#var}获取长度值${variable:start:count}表示从变量variable的start开始截取count个字符(超出末尾,截取至末尾)if需要fi进行闭合>>在文件中追加会自动换行,不换行可以采用echo -n xx >> xx在脚本文件中,输出文件内容,一般采用
cat,不能用more,后者是交互式命令,在终端使用。awk用法补充:1
2#awk+if
awk '{if($2=="'$PID'") print $3}' $FILE可以通过
"'$VAR'"的形式在awk中使用shell中定义的变量。可以使用倒引号将
awk结果赋值给变量,例如:1
var=`echo $result|awk '{print substr($result,16,3c)}'`
课上感受和踩坑教训
1.一开始由于没有初始化分支,导致后续的fetch、checkout等操作都毫无作用,耽搁了4、5分钟,还影响了心态,一定要先初始化分支!!!
2.第一个Makefile的编写还算顺利,就是后续提交的时候,clean目标明明本地不加-f都可以进行有效的操作,但是交到评测机上就是不行,一开始还以为是什么奇怪的地方出了问题,结果是没加-f,QAQ。(不过试后,一位舍友没加-f也能过,形式还与我没加-f相同,不知道怎么回事()。
3.第二个bash脚本的最后一个编写出现了些许小问题,导致被卡了很久。也就是并未充分掌握好sed命令的用法,主要是其中单形参的考虑。题目要求在单形参时,输出相应文件从第形参行开始到最后一行每一行的内容,显然应该使用sed命令来实现,由于''中一切皆字符,所以使用""方便来引用输入的形参,根据$表示最后一行,得出了第一版想法:
1 | |
但是如你所见,bash认为$p连在一起应该指代一个变量,于是就水灵灵的报错了。
在百般尝试后,(包括但不限于使用,分隔)最终得到了以下两种方式:
1 | |
4.extra题量大,考量全面,拼尽全力,无法拿分,没有办法,菜就多练。
致谢
页表那道题,是在和舍友kxq同学讨论后的结果,特此致谢。




