《信息安全系统设计基础》第11周学习总结
20145336张子扬 《信息安全系统设计基础》第11周学习总结
学习目标
- 了解异常及其种类
- 理解进程和并发的概念
- 掌握进程创建和控制的系统调用及函数使用:fork,exec,wait,waitpid,exit,getpid,getppid,sleep,pause,setenv,unsetenv
- 理解数组指针、指针数组、函数指针、指针函数的区别
- 理解信号机制:kill,alarm,signal,sigaction
- 掌握管道和I/O重定向:pipe, dup, dup2
教材学习内容总结
异常控制流
异常控制流(ECF)
- 最简单的“平滑序列”类型的控制流是指PC中相邻的指令在存储器中也相邻。而异常控制流则是指程序变量表示的内部程序状态中的变化、系统状态的变化等突发情况使得控制系统做出的反映。
- 应用程序如何与操作系统实现交互。应用程序使用系统调用(system call)的ECF形式向操作系统请求服务;实现并发的基本机制;提供软件异常机制等等
异常
- 处理器中的变化(事件)触发从应用程序到异常处理程序的突发的控制转移,也就是异常。比如:被零除,缺页,存储器访问违例,断点,算术溢出;系统调用,来着外部I/O设备的信号等等。
- 在任何情况下,当处理器检测到有事件发生时,它就会通过一张叫做异常表的跳转表进行一个间接过程调用,到一个专门处理这类时间的操作系统子程序(异常处理程序);当 exception handler处理结束之后,会有三种结果 :处理程序将控制返回给事件发生的时候正在执行的指令;处理程序将控制返回给如果没有发生异常将会执行的下一条指令;处理程序终止被终端的程序
**异常&过程调用 **
- 过程调用的时候,在跳转到处理程序之前,处理器将返回地址压入栈中。然而根据异常的类型,要么返回当前指令,要么返回下一条指令;
- 异常虽然类似于过程调用,但在压入栈的数据方面有不同。它会把一些额外的处理器状态压入栈中;并且如果是转移到内核的程序,压入的是内核栈中;
- 异常处理程序运行在内核模式下,意味着它们对所有的系统资源都有完全的访问权限
中断
- 原因:由I/O设备的信号引起的结果
- 类型:异步(不是由任何一条指令造成的)
- 处理:i/o设备,例如定时器芯片、网络控制器等,通过处理器芯片上的一个引脚发信号,并将异常号(标识引起中断的设备)放在系统总线上;在当前指令完成之后,处理器注意到引脚电压变化,从系统总线中读取异常信号,调用中断处理程序处理中断;处理器返回(无中断的时候)应该执行的下一条指令。
陷阱
- 原因:有意的异常,是执行指令的结果
- 类型:同步
- 处理:用户程序需要或者希望向内核请求服务(比如创建或者终止进程、读文件等)的时候,执行 syscall n(n是想要请求的服务号)指令;把控制权交给处理程序;陷阱处理程序运行;处理程序结束之后,返回到下一条指令。
故障
- 原因:由潜在的可恢复的错误的情况引起
- 类型:同步;可能能够被修复然后返回当前指令。
- 过程:当前指令导致故障;控制转移给处理程序;故障处理程序运行,如果可以修正这个错误,就将控制引起故障的指令从而重新执行它;否则,返回内核中的abort例程,abort终止引起故障的程序。
终止
- 原因:由不可恢复的致命错误造成;通常是一些硬件错误
- 类型:同步
- 过程:发生致命硬件错误;传递控制给处理程序;处理程序将控制返回给abort例程,该例程终止此应用程序
进程
- 定义:一个执行中的程序的实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需要的状态组成的。这个状态包括放在存储器中的程序的代码和数据等
逻辑控制流
- 进程计数器(PC)中的每一个值都唯一地对应于包含在程序的可执行目标文件中的指令,或者是包含在运行时动态链接的到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流。
- 进程是轮流使用处理器的;每个进程执行它的流的一部分然后被挂起,其他进程执行。 对于一个运行在其中一个进程上下文中的程序而言,它看上去就像是唯一地占用了处理器(只不过如果精确测量的话,会发现对于一个进程来说,它在执行期间好像被停顿了若干个很短的时间)。
并发流
- 计算机系统中有很多逻辑流的不同形式,比如异常处理程序、进程、信号处理程序等;一个逻辑流的执行在时间上与另一个流重叠,称为并发流;多个流并发执行的现象称为并发;一个进程和其他进程轮流运行,称为多任务;又叫做时间分片。
- 如果两个流并发地运行在不同的处理器核或者计算机上,那么我们称它们为并行流。
私有地址空间
- 在一台有n位地址的机器上,地址空间是一个2^n个可能地址的集合。一个进程为每个程序提供它自己的私有地址空间;一般而言,和这个空间中某个地址相关联的那个存储器字节是不能够被其他进程读或者写的(所以说它是私有的)
- 存储器内容通用结构:地址空间底部留给用户程序,包括通常的文本、数据等;地址空间顶部保留给内核(包括内核在代表进程执行指令的时候使用的代码、数据和栈)
进程控制
-
获取ID每个进程都有一个唯一的进程ID(PID);getpid函数获取进程的PID;getppid获取创建调用进程的进程(即它的父进程)的PID。以上两个函数的返回值为pid_t,在linux系统中,它在types.h中被定义为int
-
运行:进程要么在CPU上运行,要么在等待被执行且最终被内核调度;
- 停止:进程的执行被挂起,且不会被调度。【当进程收到SIGSTOP,SIGTSTP,SIDTTIN,SIGTTOU信号的时候,进程就会停止,并且保持停止直到它收到一个SIGCONT信号,在此时再次运行】
- 终止:进程永远地停止。三种原因:1)收到一个信号,其默认为终止程序;2)从主程序返回;3)调用exit函数(exit(int stauts),其中status是退出状态)
- 回收子进程:当一个进程由于某种原因终止的时候,内核并不是把它从系统中清除,而是保持在已经终止的状态中,直到被它的父进程回收。这时,内核将子进程的退出状态传递给父进程,然后抛弃已经终止的进程。这之后,该进程才可以说是“不存在”了。
代码调试
fork
forkdemo1
- forkdemo1.c代码先是打印进程pid,然后调用fork函数生成子进程,休眠一秒后再次打印进程id,这时父进程打印子进程pid,子进程返回0
forkdemo2
- 这个代码调用两次fork,一共产生四个子进程,所以会打印四个aftre输出
forkdemo3
- fork产生子进程,父进程返回子进程pid,不为0,所以输出父进程的那句话,子进程返回0,所以会输出子进程那句话
forkdemo4
- 先打印进程pid,然后fork创建子进程,父进程返回子进程pid,所以输出parent一句,休眠十秒;子进程返回0,所以输出child与之后一句
forkgdb
- 父进程打印是先打印两句,然后休眠一秒,然后打印一句,子进程先打印一句,然后休眠一秒,然后打印两句。并且这两个线程是并发的,所以可以看到在一个线程休眠的那一秒,另一个线程在执行,并且线程之间相互独立互不干扰
exec
exec1
- exec1.c中execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件
- 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中
- exevp函数调用成功没有返回,所以没有打印出“* * * ls is done. bye”这句话
exec2
- exec2与exec1的区别就在于exevp函数的第一个参数,exec1传的是ls,exec2直接用的arglist[0],不过由定义可得这两个等价,所以运行结果是相同的
exec3
- 函数中execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……最后一个参数必须用空指针(NULL)作结束
wait
waitdemo1
- waitdemo1.c的功能是如果有子进程,则终止子进程,成功返回子进程pid。运行如下
waitdemo2
- waitdemo2.c比起1来就是多了一个子进程的状态区分,把状态拆分成三块,exit,sig和core
testbuf
testbuf1
testbuf2
- testbuf1.c和testbuf2.c代码运行结果一致,因为fflush(stdout)的效果和换行符\n是一样的
testbuf3
- 将内容格式化输出到标准错误、输出流中
testpid
- 输出当前进程pid和当前进程的父进程的pid
pipe
pipe用来创建管道并将其两端连接到两个文件描述符,array[0]为读数据端的文件描述符,而array[1]则为写数据端的文件描述符,内部则隐藏在内核中,进程只能看到两个文件描述符
listargs
- 代码运行结果如下,证明了shell并不将重定向标记和文件名传递给程序
pipe
- 引入oops,当linux系统执行代码遇到问题时,就会报告oops,运行如下
pipedemo
- 展示了如何创建管道并使用管道来向自己发送数据
pipedemo2
- 说明了如何将pipe和fork结合起来,创建一对通过管道来通信的进程。在程序中显示了从键盘到进程,从进程到管道,再从管道到进程以及从进程回到终端的数据传输流
stdinredir1
- 将stdin定向到文件,程序中先关闭标准输入流,后打开文件,进行重定向
stdinredir2
psh1
- 效果是输入要执行的指令,回车表示输入结束,然后输入的每个参数对应到函数中,再调用对应的指令
psh2
- psh2.c比起1来,多了循环判断,不退出的话就会一直要你输入指令,并且对于子程序存在的状态条件
signal
sigdemo1
- 程序连续输出五个hello,每两个之间的间隔时间为2秒,且在此期间输入的Ctrl+C都被处理成打印OUCH
sigdemo2
- 一直输出haha,按Ctrl+C不能停止
- SIGDFL,SIGIGN 分别表示无返回值的函数指针,指针值分别是0和1,这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值。
- SIG_DFL:默认信号处理程序
- SIG_IGN:忽略信号的处理程序
sigdemo3
- 根据代码,在read函数不发生错误的情况下输入什么,就输出什么,输入的Ctrl+C也无法终止程序,只有输入quit的时候才会退出
sigactdemo
-
sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号
-
SARESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIGDFL
-
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
-
SANODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置SANODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
本周代码托管截图
http://git.oschina.net/Zziyang/CS11
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第二周 | 0/0 | 1/2 | 19/20 | |
第三周 | 80/80 | 1/3 | 25/44 | |
第四周 | 110/190 | 1/4 | 23/67 | |
第五周 | 60/250 | 2/6 | 26/93 | |
第六周 | 80/330 | 2/8 | 25/118 | |
第七周 | 60/390 | 1/9 | 25/133 | |
第八周 | 0/390 | 2/11 | 22/155 | |
第九周 | 70/460 | 2/13 | 23/178 | |
第十周 | 375/835 | 2/15 | 22/200 | |
第十一周 | 880/1715 | 2/17 | 26/226 |