20145326蔡馨熠《信息安全系统设计基础》第11周学习总结
教材内容总结
-
异常控制流(ECF)发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制。在硬件层,异常是由处理器中的事件触发的控制流中的突变。控制流传递给一个软件处理程序,该处理程序进行一些处理,然后返回控制给被中断的控制流。
-
有四种不同类型的异常:中断、故障、终止和陷阱。当一个外部旧设备,例如定时器芯片或者一个磁盘控制器,设置了处理器芯片上的中断引脚时(对于任意指令)中断会异步地发生控制返回到故障指令后面的那条指令。
-
一条指令的执行可能导致故障和终止同时发生故障,处理程序会重新启动故障指令,而终止处理程序从不将控制返回给被中断的流。最后,陷阱就像是用来实现向应用提供到操作系统代码的受控的入口点的系统调用的函数调用。
-
在操作系统层,内核用ECF提供进程的基本概念。进程提供给应用两个重要的抽象:(1)逻辑控制流,它提供给每个程序一个假象,好像它是在独占地使用处理器(2)私有地址空间,它提供给每个程序一个假象,好像它是在独占地使用主存。
-
在操作系统和应用程序之间的接口处,应用程序可以创建子进程,等待它们的子进程停止或者终止,运行新的程序,以及捕获来自其他进程的信号。
-
最后,在应用层,C程序可以使用非本地跳转来规避正常的调用/返回栈规则,并且直接从一个函数分支到另一个函数。
实践与分析过程
exec1.c
- exec1.c中
execvp()
会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件 - 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中
-
exevp函数调用成功没有返回,所以没有打印出“* * * ls is done. bye”这句话
-
将
ls -l
变换成man -k,代码修改如下
- 重新编译运行结果如下
- 结果同样也是执行了
man -k
语句,还是没有返回“* * * man is done. bye”
exec2.c
- exec2.c代码运行如下
-
exec2与exec1的区别就在于exevp函数的第一个参数,exec1传的是ls,exec2直接用的arglist[0],不过由定义可得这两个等价,所以运行结果是相同的
-
若将exevp函数传入的arglist[0]改为arglist[1],此时exevp函数没有调用成功,于是打印出“* * * ls is done. bye”这句话。
exec3.c
- exec3.c代码运行如下
-
函数中execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……最后一个参数必须用空指针(NULL)作结束
-
以下四个函数中任意一个的运行结果都与其他三个完全一致。
env
- environ.c代码运行如下
- 代码中涉及到getenv函数和setenv函数
-
getenv函数是获得环境变量值的函数,参数是环境变量名name,例如”HOME”或者”PATH”。如果环境变量存在,那么getenv函数会返回环境变量值,即value的首地址;如果环境变量不存在,那么getenv函数返回NULL。
-
environvar.c代码简单打印环境变量表,运行结果如下
- 每个程序都有一个环境表,它是一个字符指针数组,其中每个指针包含一个以NULL结尾的C字符串的地址。全局变量environ则包含了该指针数组的地址
argv
- 查看头文件argv.h,该函数的功能是把命令行字符串转化为以NULL结尾的参数数组 int makeargv(const char *s, const char *delimiters, char ***argvp);
-
其中s为命令行字符串,delimiters为分割符,argvp为指向参数数组的指针,如果转化成功则返回标记的个数,如果错误则返回-1,并设置errno
-
由于argtest.c中有如下代码: if (argc != 2) { fprintf(stderr, "Usage: %s string ", argv[0]); return 1; }
-
只有当输入命令的个数等于2时,才能显示命令正确的结果。
看了这几个c文件后,我有一些思考:
1.为什么是 int makeargv(const char s, const char delimiters, char * argvp)?
把最后一个参数理解为向字符串数组取地址(从左到右,第一个代表取地址,后两个 代表上文中说过的字符串数组)
2.关于strtok函数?
strtok函数用来将字符串分割成一个个片段,它的原型是char strtok(charr s[],const char delim)。只要在s中遇到delim中包含的字符(不一定是delim),就把这个字符改成 。每次调用成功后返回的都是被分割出的片段的指针。
fifo
- FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中
- FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作
- FIFO往往都是多个写进程,一个读进程
consumer.c代码运行:
producer.c代码运行:
-
testmf.c代码中调用了mkfifo函数 mkfifo(FIFONAME, 0777);//依据FIFONAME创建fifo文件,0777依次是相应权限
-
mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取
forkdemo1.c
- forkdemo1.c代码先是打印进程pid,然后调用fork函数生成子进程,休眠一秒后再次打印进程id,这时父进程打印子进程pid,子进程返回0
- 运行结果如下:
forkdemo2.c
- 这个代码调用两次fork,一共产生四个子进程,所以会打印四个aftre输出
- 代码运行如下:
forkdemo3.c
- fork产生子进程,父进程返回子进程pid,不为0,所以输出父进程的那句话,子进程返回0,所以会输出子进程那句话
- 运行如下:
forkdemo4.c
- 先打印进程pid,然后fork创建子进程,父进程返回子进程pid,所以输出parent一句,休眠十秒;子进程返回0,所以输出child与之后一句
- 运行如下:
forkgdb.c
- 父进程打印是先打印两句,然后休眠一秒,然后打印一句,子进程先打印一句,然后休眠一秒,然后打印两句。并且这两个线程是并发的,所以可以看到在一个线程休眠的那一秒,另一个线程在执行,并且线程之间相互独立互不干扰
- 运行如下:
waitdemo1.c
- waitdemo1.c的功能是如果有子进程,则终止子进程,成功返回子进程pid。运行如下
waitdemo2.c
- waitdemo2.c比起1来就是多了一个子进程的状态区分,把状态拆分成三块,exit,sig和core
- 运行如下
关于testbuf
- testbuf1.c代码运行如下
- testbuf2.c代码运行如下
- testbuf1.c和testbuf2.c代码运行结果一致,因为fflush(stdout)的效果和换行符
•testbuf3.c将内容格式化输出到标准错误、输出流中
•testpid.c代码输出当前进程pid和当前进程的父进程的pid
-
testsystem.c代码中system()——执行shell命令,也就是向dos发送一条指令。这里是后面可以跟两个参数,然后向dos发送这两个命令,分别执行
-
如下图,输入ls和dir两个指令后,可以看到分别执行了
pipe
-
pipe用来创建管道并将其两端连接到两个文件描述符,array[0]为读数据端的文件描述符,而array[1]则为写数据端的文件描述符,内部则隐藏在内核中,进程只能看到两个文件描述符
-
listargs.c 代码运行结果如下,证明了shell并不将重定向标记和文件名传递给程序
- pipe.c引入oops,当linux系统执行代码遇到问题时,就会报告oops,运行如下
- pipedemo.c展示了如何创建管道并使用管道来向自己发送数据
- pipedemo2.c说明了如何将pipe和fork结合起来,创建一对通过管道来通信的进程。在程序中显示了从键盘到进程,从进程到管道,再从管道到进程以及从进程回到终端的数据传输流
- stdinredir1.c 将stdin定向到文件,程序中先关闭标准输入流,后打开文件,进行重定向
- stdinredir2.c
psh1.c
- psh1.c代码的效果是输入要执行的指令,回车表示输入结束,然后输入的每个参数对应到函数中,再调用对应的指令
- 运行结果如下
psh2.c
- psh2.c比起1来,多了循环判断,不退出的话就会一直要你输入指令,并且对于子程序存在的状态条件
- 运行如下
signal
- sigdemo1.c程序连续输出五个hello,每两个之间的间隔时间为2秒,且在此期间输入的Ctrl+C都被处理成打印OUCH
- sigdemo2.c一直输出haha,按Ctrl+C不能停止
-
sigdemo2.c中: SIGDFL,SIGIGN 分别表示无返回值的函数指针,指针值分别是0和1,这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值。 SIGDFL:默认信号处理程序 SIGIGN:忽略信号的处理程序
-
sigdemo3.c根据代码,在read函数不发生错误的情况下输入什么,就输出什么,输入的Ctrl+C也无法终止程序,只有输入quit的时候才会退出
-
sigactdemo.c中sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
-
sigactdemo.c执行如下
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置SA_NODEFER标记,
那么在该信号处理函数运行时,内核将不会阻塞该信号
- sigactdemo2.c休息seconds秒后返回;或者被信号中断且信号处理函数返回后sleep()返回0。所以如果不计较返回值的话,pause()的功能相当于无限期的sleep()
学习过程中遇到的问题
在运行argtest的时候,出现了问题。
后来发现,应该将argtest.c和makeargv.c,还有freemakeargv.c和argv.h放在同一目录下;运行成功~
argv文件夹中的主要程序是argtest.c
有两种运行方法:
第一种是用-c将所有.c的文件编译成后缀为.o的文件,然后一起编译成可执行文件。
第二种方法是直接将所有的.c文件编译成可执行文件,运行结果是一样的。
代码托管
心得体会
这周学习的是第八章的内容,娄老师提前在课上就讲了一些,并且强调了其重要性。因为老师的强调,我更加认真去看了这部分的内容,并通过代码来实践。这学期还有一门操作系统的必修课,我学到了很多与进程有关的知识。因此我觉得学科之间是有共同点的,所以我想通过这一章的学习来巩固这部分知识并且做到学科间的融合。但是这一章还是有好几个函数的运行和原理不是很明白,需要多多琢磨。学过的知识还要温习,没学过的新知识要触类旁通,这样才会更有效率!
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/2 | 20/20 | |
第二周 | 58/58 | 1/3 | 20/40 | |
第三周 | 150/208 | 1/4 | 22/62 | |
第五周 |
150/358 | 1/5 | 21/83 | |
第六周 | 136/494 | 1/6 | 25/108 | |
第七周 | 115/609 | 2/8 | 24/132 | |
第八周 | 0/609 | 2/10 | 22/154 | |
第九周 | 109/718 | 3/13 | 20/174 | |
第十周 | 472/1190 | 1/14 | 21/195 | |
第十一周 | 1883/3073 | 3/17 | 21/216 |