2017-2018-1 20155233 《信息安全系统设计基础》第十三周学习总结
学习目标
- 本周我主要学习第八章,目标如下:
- 完成这一章所有习题
- 详细总结本章要点
- 给我的结对学习搭档讲解你的总结并获取反馈
参考上面的学习总结模板,把学习过程通过博客(随笔)发表,博客标题“学号 《信息安全系统设计基础》第十三周学习总结”,博客(随笔)要通过作业提交,截至时间本周日 23:59。
教材学习内容总结
8.1异常
1.异常时异常控制流的一种形式,他一部分是由硬件实现,一部分由软件实现。
2.异常就是控制流中的突变,用来响应处理器状态中的某些变化。
3.如果异常中断的是一个一场程序,那么就将恢复为用户模式。
4.四种情况
a.中断:来自IO的设备的信号,异步,总是返回到下一条指令。
(1)异步发生,是来自处理器外部的I/O设备的信号的结果。 硬件异常中断处理程序通常称为中断处理程序。(2)异步异常是有处理器外部的I/O设备中的时间产生的,同步异常是执行一条指令的直接产物。(3)陷阱、故障、终止时同步发生的,是执行当前指令的结果,我们把这类指令叫做故障指令。
b.陷阱:有意的异常,同步,总是返回到下一条指令
(1)陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。(2)普通的函数运行在用户模式中,用户模式限制了函数可以执行的指令的类型,而且它们只能访问与调用函数相同的栈。系统调用运行在内核模式中,内核模式允许系统调用执行指令,并访问定义在内核中的栈。
c.故障:是由错误情况引起的。
潜在可恢复的错误,同步,可能返回到当前指令
d.终止:不可恢复的错误,同步,不会返回
5.异常与过程调用的不同之处:
(1)过程调用时,在跳转到处理器之前,处理器将返回地址压入栈中。然而,根据异常的类型,返回地址要么是当前指令,要么是下一条指令。(2)处理器把一些额外的处理器状态压入栈里,在处理程序返回时,重新开始被中断的程序会需要这些状态。(3)如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中,而不是压到用户栈中。(4)异常处理程序运行在内核模式下,意味着它们对所有的系统资源都有完全的访问权限。
8.2进程
1.一个独立的逻辑控制流:他提供一个假象,好像我们的额程序独占的使用处理器。
2.一个私有的地址空间:他提供一个假象,好像我们独占的使用存储器系统。
多个流一起执行被称为并发。
3.一个进程和其他进程轮流进行的概念被称为多任务。
4.一个进程执行执行他的控制流的一部分的每一段时间叫做时间片。
5.并发流:一个逻辑流的执行在时间上与另一个流重叠。
6.并发:多个流并发地执行的一般现象。
7.多任务:一个进程和其他进程轮流运行的概念。
8.时间片:一个进程执行它的控制流的一部分的每一时间段。多任务也叫时间分片。
9.模式位:用某个控制寄存器中的一个位模式,限制一个应用可以执行的指令以及它可以访问的地址空间范围。
10.当设置了位模式,进程就运行在内核模式中,一个运行在内核模式中的进程可以中兴指令集中的任何指令,而且可以访问系统中任何存储器位置。
11.没有设置位模式时,进程就运行在用户模式中,不允许执行特权指令,例如停止处理器、改变位模式,或者发起一个I/O操作。
12.用户程序必须通过系统调用接口间接的当问内核代码和数据。
13.进程从用户模式变为内核模式的唯一方法是通过诸如中断、故障、或者陷入系统调用这样的异常。
上下文切换机制:
(1)保存当前进程的上下文
(2)恢复某个先前被抢占的进程被保存的上下文
(3)将控制传递给这个新恢复的进程
引起上下文切换的情况:
(1)当内核代表用户执行系统调用时
(2)中断时
8.3 系统调用错误处理
错误处理包装函数:包装函数调用基本函数,检查错误,如果有任何问题就终止。
8.4 进程控制
8.4.1 获取进程ID
1.每个进程都有一个唯一的正数的进程ID。
2.getpid函数返回调用进程的PID,getppid函数返回它的父进程的PID。上面两个函数返回一个同类型为pid_t的整数值,在linux系统中,它在types.h中被定义为int。
8.4.2 创建和终止进程
1.进程总处于三种状态
(1)运行:进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。(2)停止:程序的执行被挂起,,且不会被调度。(3)终止:进程用永远停止了。
终止原因:(1)收到一个信号,默认行为是终止进程(2)从主进程返回(3)调用exit函数
2.父进程通过调用fork函数创建一个新的运行的子进程。
3.子进程和父进程的异同:异:有不同的PID同:用户级虚拟地址空间,包括:文本、数据和bss段、堆以及用户栈。任何打开文件描述符,子进程可以读写父进程中打开的任何文件。
4.fork函数: 因为父进程的PID总是非零的,返回值就提供一个明确的方法来分辨程序是在父进程还是在子进程中执行。
fork函数的特点:
(1)调用一次,返回两次
(2)并发执行
(3)相同的但是独立的地址空间
(4)共享文件
8.4.3 回收子进程
1.当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。
一个终止了但还未被回收的进程称为僵死进程。
2.一个进程可以通过调用waitpid函数来等待它的子进程终止或者停止。
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);
- 返回:若成功,返回子进程的PID;
- 若WNOHANG,返回0;
- 若其他错误,返回-1。
- 默认地,当option=0时,waitpid挂起调用进程的执行,直到它的等待集合中的一个子进程终止。
3.判定等待集合的成员
有参数pid来确定的:
(1)pid>0:等待集合是一个单独的子进程,进程ID等于pid。(2)pid=-1:等待结合就是由父进程所有的子进程组成的。
4.修改默认行为
通过options设置:
(1)WNOHANG:默认行为是挂起调用进程。(2)WUNTRACED:默认行为是只返回已终止的子进程。
(3)WNOHANG|WUNTRACED:立即返回,如果等待集合中没有任何子进程被停止或者已终止,那么返回值为0,或者返回值等于那个被停止或者已经终止的子进程的PID。
5.检查已回收子进程的退出状态
wait.h头文件定义了解释status参数的几个宏:
(1)WIFEXITED:如果子进程通过调用exit或者一个返回正常终止,就返回真;
(2)WEXITSTATUS:返回一个正常终止的子进程的退出状态。只有在WIFEXITED返回真时,才会定义这个状态。
6.错误条件
(1)若调用进程没有子进程,那么waitpid返回-1,并且设置errno为ECHILD;
(2)若waitpid函数被一个信号中断,那么返回-1,并设置errno为EINTR
7.wait函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
- 返回:若成功,返回子进程的PID;若错误,返回-1。
- 调用wait(&status)等价于调用waitpid(-1.&status,0)
8.4.4 让进程休眠
1.sleep函数:将进程挂起一段指定的时间
#include <unistd.h>
unsigned int sleep(unsigned int secs);
- 返回:还要休眠的秒数
- 如果请求的时间量已经到了,返回0
- 否则返回还剩下的要休眠的秒数。
2、pause函数:让调用函数休眠,直到该进程收到一个信号。
#include <unistd.h>
int pause(void);
- 返回:总是-1
8.4.5 加载并运行程序
1.execve函数:在当前进程的上下文中加载并运行一个新程序。
#include <unistd.h>
int execve(const char *filename,const char *argv[],const char *envp[]);
- 返回:若成功,则不返回,若错误
- 返回-1
filename:可执行目标文件
argv:带参数列表
envp:环境变量列表
特点:execve调用一次从不返回
2.getenv函数:在环境数组中搜素字符串“name =VALUE”,若找到了,就返回一个指向value的指针,否则它就返回NULL。
#include <stdlib.h>
char *getenv(const char *name);
- 返回:存在,返回指向name的指针,若无匹配的,为NULL
3.注意:
execve函数在当前进程的上下文中加载并运行一个新的进程。它会覆盖当前进程的地址空间,并没有创建一个新的进程,新的进程仍然有相同的PID,并且继承了调用execve函数时已打开的所有文件描述符。
8.4.6 利用fork和execve运行程序
1.外壳是一个交互型的应用级程序,它代表用户运行其他程序。
2.外壳执行一系统的读/求值步骤,然后终止。读步骤读取来自用户的一个命令行,求值步骤解释命令行,并代表用户运行程序。
3.eval函数:对外壳命令行求值
4.parseline函数:解析外壳的一个输入
8.5 信号
底层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。
其他信号对应于内核或者其他用户进程中较高层的软件事件。
8.5.1 信号术语
1.发送信号的两个不同步骤:
(1)发送信号:内核通过更新目的进程上下文中的某个状态,发送(递送)一个信号给目的进程。
(2)接收信号:信号处理程序捕获信号的基本思想。
发送信号的两个原因:
(1)内核监测到一个系统事件,比如被零除错误或者子进程终止。
(2)一个进程调用了kill函数,显式地要求内核发送一个信号给目的进程。一个进程可以发送信号给它自己。
2.待处理信号:一个只发出而没有被接收的信号
一个进程可以有选择性地阻塞接收某种信号。
待处理信号不会被接收,直到进程取消对这种信号的阻塞。
3.一个待处理信号最多只能被接受一次,pending位向量:维护着待处理信号集合,blocked向量:维护着被阻塞的信号集合。
8.6非本地跳转
通过setjmp和longjmp函数来提供
setjmp函数只被调用一次,但返回多次:一次是当第一次调用setjmp,而调用环境保存在缓冲区env中时,一次是为每个相应的longjmp调用
longjmp只调用一次,但从不返回
- 非本地跳转的一个重要应用就是允许从一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况引起的
- 非本地跳转的另一个重要应用是使一个信号处理程序分支到一个特殊的代码位置,而不是返回到达中断了的指令位置
8.7 操作进程的工具
Linux系统提供监控和操作进程的工具:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹(STRACE)、列出当前系统中包括僵死进程的进程(PS)、打印出关于当前进程资源使用的信息(TOP)、显示进程的存储器映射(PMAP)、虚拟文件系统(/proc)
课后作业及实现
解答:
进程A和B是互相并发的,就像B和C一样,因为它们各自的执行是重叠的,也就是一个进程在另一个进程结束前开始。进程A和C不是并发的,因为它们的执行没有重叠;A在C开始之前就结束了。
解答:
A.这里的关键点是子进程执行了两个prin七f语句。在fork返回之后,它执行了第8行的prin七fe然后它从if语句中出来,执行了第9行的printf语句。下面是子进程产生的输出:printfl:x=2 printf2: x=1
B.父进程只执行了第9行的printf:printf2: x=0
解答:
父进程打印b,然后是c。子进程打印a,然后是c。意识到你不能对父进程和子进程是如何交错执行的做任何假设是非常重要的。因此,任何满足b}c和a -- c的拓扑排序都是可能的输出序列。有四个这样的序列:acbc, bcac, abcc和bacco
解答:
A.每次我们运行这个程序,就会产生6个输出行。 B.输出行的顺序根据系统不同而不同,取决于内核如何交替执行父子进程的指令。一般而言,满足下图的任意拓扑排序都是合法的顺序:比如unix>Hello01Bye2Bye当我们在系统上运行这个程序时,会得到下面的输出:
unix> ./waitprob1
Hello
0
1
Bye
2
Bye
在这种情况下,父进程首先运行,在第6行打印''Hello'',在第8行打印“0”。对wait的调用会阻塞,因为子进程还没有终止,所以内核执行一个上下文切换,并将控制传递给子进程,子进程在第8行打印''1'',在第15行打印''Bye'',然后在第16行终止,退出状态为2。在子进程终止后,父进程继续,在第12行打印子进程的退出状态,在第15行打印''Bye''。
解答:
解答:
解答:
只要休眠进程收到一个未被忽略的信号,sleep函数就会提前返回。但是,因为收到一个SIGINT信号的默认行为就是终止进程(见图8-25 ),我们必须设置一个SIGINT处理程序来允许sleep函数返回。处理程序简单地捕获SIGNAL,并将控制返回给sleep函数,该函数会立即返回。
解答:
这个程序打印字符串“213",这是卡内基梅隆大学CS:APP课程的缩写名。父进程开始时打印''2'',然后创建子进程,子进程会陷入一个无限循环。然后父进程向子进程发送一个信号,并等待它终止。子进程捕获这个信号(中断这个无限循环),对计数器值(从初始值2)减一,打印''1",然后终止。在父进程回收子进程之后,它对计数器值(从初始值2)加一,打印,''3'',并且终止。
代码托管
结对及互评
点评模板:
- 博客中值得学习的或问题:
- xxx
- xxx
- ...
- 代码中值得学习的或问题:
- xxx
- xxx
- ...
- 其他
本周结对学习情况
- [20155210](博客链接)
- 结对照片
- 结对学习内容
- XXXX
- XXXX
- ...
其他(感悟、思考等,可选)
xxx
xxx
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 20篇 | 400小时 | |
第一周 | 5/5 | 1/1 | 10/10 | 练习hello.c |
第三周 | 343/350 | 1/3 | 22/56 | 学习信息存储 |
第五周 | 523 /873 | 1/5 | 23/79 | 学习汇编语言 |
第七周 | 671 /1544 | 1/7 | 25/104 | 学习Y86 |
第九周 | 423 /1976 | 3/10 | 22/126 | 学习存储技术 |
第十一周 | 511 /2487 | 2/12 | 23/149 | 学习虚拟存储器 |
第十三周 | 811 / 3289 | 2/14 | 28/177 | 重点学习教程第八章 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
计划学习时间:XX小时
实际学习时间:XX小时
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)