2017-2018-1 20155207 《信息安全系统设计基础》6周学习总结
教材学习内容总结
第8章 异常控制流
- 控制转移:每次从ak到ak+1的过渡
- 处理器的控制流:控制转移序列
- 异常控制流:控制流对情况做出反应而产生的突变
8.1 异常
- 异常是异常控制流的一种形式,一部分由硬件实现,一部分由操作系统实现
- 异常:控制流中的突变,用来响应处理器状态中的某些变化
- 状态变化称为事件
- 处理器检测到有事件发生->通过异常表进行间接过程调用(异常)->异常处理程序
8.1.1 异常处理
- 系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号
- 操作系统分配和初始化异常表,异常表的起始地址放在异常表基址寄存器(特殊CPU寄存器)
8.1.2 异常的类别
- 1.中断:异步发生,硬件中断的异常处理程序称为中断处理程序
- 2.陷阱和系统调用:有意的异常,是执行一条指令的结果,重要用途是在用户程序和内核之间提供一个叫系统调用的接口
- 用户模式:普通函数
- 内核模式:系统调用
- 3.故障:由错误情况引起,能够被故障处理程序修正
- 4.终止:不可恢复的致命错误造成的结果,通常是硬件错误,终止处理程序不将控制返回给应用程序
8.1.3 Linux/IA32系统中的异常
- 1.Linux/IA32故障和终止:除法错误(0)、一般保护故障(13)、缺页(14)、机器检查(18)
- 2.Linux/IA32系统调用:IA32通过
int n
陷阱指令提供系统调用,C程序用syscall函数可以直接调用任何系统调用
8.2 进程
- 进程:一个执行中的程序实例
- 系统中的每个程序都是运行在某个进程的上下文中的
8.2.1 逻辑控制流
- 程序计数器(PC)值的序列叫做逻辑控制流,简称逻辑流
8.2.2 并发流
- 并发流:并发流一个逻辑流的执行在时间上与另一个流重叠
- 并发:多个流并发执行的一般现象称为并发
- 多任务:多个进程并发叫做多任务
- 时间片:一个进程执行他的控制流的一部分的时间段
- 并行:并发流在不同的cpu或计算机上
8.2.3 私有地址空间
- 一个进程为每个程序提供它自己的私有地址空间
8.2.4 用户模式和内核模式
- 设置模式位,进程运行在内核模式(超级用户模式),没有设置模式位就运行在用户模式
- 运行应用程序代码的进程初始时是在用户模式中的
- 进程从用户模式变为内核模式的唯一方法是通过异常
- linux提供了/proc文件系统,它允许用户模式进程访问内核数据结构的内容
8.2.5 上下文切换
- 上下文切换:操作系统内核使用叫上下文切换的异常控制流来实现多任务
- 调度:内核中的调度器实现调度
8.3 系统调用错误处理
- 错误报告函数
- 错误包装函数
8.4 进程控制
8.4.1 获取进程ID
- 每个进程都有一个唯一的正数(非零)进程ID(PID)
getpid
函数返回调用进程的PIDgetppid
返回它父进程的PID(创建调用进程的进程)
8.4.2 创建和终止进程
- 进程的三种状态——运行、停止和终止
- 进程会因为三种原因终止进程:收到信号,该信号默认终止进程;从主程序返回;调用exit函数
- 父进程通过调用fork创建一个新的运行子进程:父进程与子进程有相同(但是独立的)地址空间,有相同的文件藐视符集合
fork
调用一次返回两次、并发执行、相同的但是独立的地址空间、共享文件
8.4.3 回收子进程
- 回收:当一个进程终止时,内核并不立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收
- 僵死进程:一个终止了但是还未被回收的进程称为僵死进程,仍然消耗系统的存储器资源
- 回收子进程的两种方法:1,内核的init进程 2,父进程waitpid函数
- 如果父进程没有回收它的僵死子进程就终止了,那么内核就会安排init进城来回收它们。init进程的PID为1,并且是在系统初始化时创建的
- 一个进程可以通过调用waitpid函数来等待它的子进程终止或停止
- wait(&status)函数,等价于调用wait(-1,&status,0)
8.4.4 让进程休眠
- sleep函数将一个进程挂起一段指定的时间
- pause函数让调用函数休眠,直到该进程收到一个信号
8.4.5 加载并运行程序
execve
函数在当前进程的上下文中加载并运行一个新程序execve
调用一次且不返回getenv
函数在环境数组中搜索字符串“name=value”,找到返回指向value
的指针,否则返回NULL
8.4.6 利用fork和execve运行程序
8.5 信号
- unix信号,它允许进程中断其他进程
- 低层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常
8.5.1 信号术语
- 传送一个信号的步骤:发送信号、接收信号
- 发送信号:内核通过更新目的进程中上下文中的某个状态,发送一个信号给目的进程。发送信号有两个原因:1)内核检测到一个系统事件; 2)一个进程调用kill函数,显式地要求内核发送一个信号给目的进程
- 一个进程可以给自己发送信号
- 接收信号:目的进程就接收了信号。进程可以忽略这个信号,终止或者通过执行信号处理程序捕获这个信号
- 一种类型的信号只能有一种待处理信号
8.5.2 发送信号
- 进程组:每个进程都只属于一个进程组,进程组是由一个进程组ID来标识的
- 进程可以通过
setpgid
函数返回当前进程的进程组ID - 默认的,一个子进程和它的父进程同属于一个进程组
/bin/kill
程序可以向另外的进程发送任意的信号- 在任何时刻,至多只有一个前台作业和0个或多个后台作业
- 外壳为每个作业创建一个独立的进程组,一个作业对应一个进程组
- 进程通过调用
kill
函数发送信号给其他进程(包括自己) - 进程通过调用
alarm
函数向他自己发送SIGALRM信号 alarm
函数安排内核在secs秒内发送一个SIGALRM信号给调用进程
8.5.3 接收信号
- 进程可以通过使用signal函数来修改和信号相关的默认行为
- SIGSTOP和SIGKILL,它们的默认行为不能被修改
8.5.4 信号处理问题
- 当一个程序捕获多个信号时,有一些问题:待处理信号不会排队等待、待处理信号被阻塞、系统调用被中断
8.5.5 可移植的信号处理
- 目的是为了统一同一信号在不通系统中的语义
- sigaction函数,或者是它的包装函数Signal函数
8.5.6 显式地阻塞和取消阻塞
- 应用程序可以使用
sigprocmask
函数显式地阻塞和取消阻塞选择的信号 sigprocmask
函数改变当前已阻塞信号的集合
8.5.7 同步流以避免讨厌的并发错误
8.6 非本地跳转
- c语言提供了一种用户级异常控制流形式,称为本地跳转
- 通过setjmp和longjmp函数来提供
- setjmp函数只被调用一次,但返回多次:一次是当第一次调用setjmp,而调用环境保存在缓冲区env中时,一次是为每个相应的longjmp调用
- longjmp只调用一次,但从不返回
- 非本地跳转的一个重要应用就是允许从一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况引起的
- 非本地跳转的另一个重要应用是使一个信号处理程序分支到一个特殊的代码位置,而不是返回到达中断了的指令位置
8.7 操作进程的工具
- Linux系统提供监控和操作进程的工具:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹(STRACE)、列出当前系统中包括僵死进程的进程(PS)、打印出关于当前进程资源使用的信息(TOP)、显示进程的存储器映射(PMAP)、虚拟文件系统(/proc)
数组指针、指针数组、函数指针、指针函数的区别
- 指针数组(char *a[4]):定义了一个数组,而它的每个元素的类型是一个指向字符/字符串的指针
- 数组指针:(char (*a)[4]):表示一个指向“一个有4个字符类型元素的数组”的指针
- 函数指针(int (*ptr)(int x, int y)):函数指针是指向函数的指针变量。
- 指针函数(int *p(int a,float b)): 返回值为指针的函数,p为函数名,
int *
为函数类型
管道和I/O重定向
管道pipe函数
- 无名管道为建立管道的进程及其子孙提供一条以比特流方式传送消息的通信管道
- 该管道在逻辑上被看作管道文件,在物理上则由文件系统的高速缓冲区构成,而很少启动外设
- 发送进程利用文件系统的系统调用write(fd[1],buf,size),把buf种的长度为size字符的消息送入管道入口fd[1],接收进程则使用系统调用read(fd[0],buf,size)从管道出口fd[0]出口读出size字符的消息置入buf中
- 这里,管道按FIFO(先进先出)方式传送消息,且只能单向传送消息
- 利用UNIX提供的系统调用pipe,可建立一条同步通信管道。其格式为:
int fd[2];pipe(fd);
这里,fd[1]为写入端,fd[0]为读出端
I/O重定向dup,dup2
- dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符
- 它们经常用来重定向进程的stdin、stdout和stderr
- 利用函数dup,可以复制一个描述符,这两个描述符共享同一个数据结构
- dup2函数跟dup函数相似,但dup2函数允许调用者规定一个有效描述符和目标描述符的id。dup2函数成功返回时,目标描述符(dup2函数的第二个参数)将变成源描述符(dup2函数的第一个参数)的复制品,换句话说,两个文件描述符现在都指向同一个文件,并且是函数第一个参数指向的文件
代码调试
- exec1.c/exec2.c/exec3.c
execv(“/bin/ls”,arglist)``execvp(“ls”,arglist)``execvpe(“ls”,arglist,myenv)``execlp(“ls”,”ls”,”-l”,NULL)
- forkdemo1/2/3.c forkgdb.c
fork()
- testbuf1/2/3.c
fflush(stdout)``fprintf(“stdout/stderr,””,3”)
- waitdemo1/2.c
代码调试中的问题和解决过程
fflush()
函数是什么- 冲洗流中信息,会强迫将缓冲区内数据写入参数指定的文件中
malloc()
函数是什么- 向系统申请分配指定字节的内存空间,返回类型是
void *
10.1 Unix I/O
- Unix文件:m个字节的序列
- 所有的I/O设备都被模型化为文件,所有的输入和输出都被当作对相应文件的读和写来执行
- Unix内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行
- 打开文件:宣告想要访问一个I/O设备,内核返回描述符(小的非负整数)
- 改变当前的文件位置:内核保持着一个文件位置k,是从文件开头起始的字节偏移量
- 读写文件:文件从当前文件位置K开始读写
- 关闭文件:内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中
10.2 打开和关闭文件
- 进程是通过调用open函数来打开一个已存在的文件或创建一个新文件的
- open成功返回新文件描述符,出错为1
- open函数:filename文件描述符,flags指明进程打算如何访问文件,mode指定了新文件的访问权限位
- 文件的访问权限位被设置为mode&umask
- close成功为0,出错返回-1
10.3 读和写文件
- 应用程序分别通过调用read和write函数来执行输入和输出的
- read成功返回读的字节数,若EOF为0,出错为-1
- write成功则为写的字节数,出错则为-1
- 不足值出现原因
- 读时遇到EOF
- 从终端读入文本行:打开文件是与终端相关联的,每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小
- 读和写网络套接字:打开的文件对于网络套接字,内部缓冲约束和较长的网络延迟会引起read和write返回不足值
10.4 用RIO包健壮地读写
- RIO:I/O包,会自动为你处理不足值
- RIO提供两种函数
- 无缓冲的输入输出函数:直接在存储器和文件之间传送数据,没有应用级缓冲,对二进制数据读写到网络和从网络读写二进制数据
- 带缓冲的输入函数:允许高效的从文件中读取文本行和二进制数据,这些文件的内容缓存在应用级缓冲区内,带缓冲的RIO输入函数是线程安全的,在同一个描述符上可以被交错的调用
10.4.1 RIO的无缓冲的输入输出函数
- 通过调用rio_readn和rio_writen函数,应用程序可以在存储器和文件之间直接传送数据
- 成功则为传送的字节数,EOF则0(只对rio_readn),出错则为-1
- rio_writen遇到EOF绝不会返回不足值
- 对同一个描述符,可以交错调用rio_readen和rio_writen
10.4.2 RIO的带缓冲的输入函数
- 文本行:一个由换行符结尾的ASCII码字符序列
- 换行符(' ')与ASCII码中(LF)相同,数字值为0x0a
- 包装函数rio_readlined,从一个内部读缓冲区拷贝一个文本行,当缓冲区变空时,自动调用read重新填充缓冲区
- rio_readnb,对于即包含文本行也包含二进制数据的文件,rio_readn带缓冲区的版本
- RIO读程序的核心是rio_read函数,Unix read函数的带缓冲版本
10.5 读取文件元数据
- 应用程序能够通过调用stat和fstat函数,检索到关于文件的信息(文件的元数据),成功为0,出错为-1
- st_size包含了文件的字节数大小,st_mode成员编码了文件访问许可位和文件类型
- 普通文件:包括某种类型二进制或文本数据
- 目标文件:包含关于其他文件信息
- 套接字:一种用来通过网络与其他进程通信的文件
- 使用宏和stat函数来读取和解释一个文件的st_mode位
10.6 共享文件
- 内核用三个相关的数据结构表示打开的文件:描述符表、文件表、v-node表
- 描述符表:每个进程都有独立的描述符表,表项是由进程打开的文件文件描述符来索引的,每个描述符表项都指向文件表中的一个表项
- 文件表:打开的文件的集合,所有进程共享一个表,表项组成包括当前的文件位置、引用计数和指向v-node表对应表项的指针
- v-node表:所有进程共享,表项包括stat结构中的大多数信息
10.7 I/O重定向
- Unix外壳提供I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来
- 利用int dup2(int oldfd,int newfd),成功返回非负描述符,出错为-1
- dup2函数拷贝描述表表项oldfd到描述表表项newfd,覆盖newfd以前的内容,如果newfd已经打开了,dup2会在拷贝oldfd前关闭newfd
10.8 标准I/O
- 标准I/O库,一组高级的输入输出函数,将一个打开的文件模型化为一个流(指向FILE类型的结构的指针)
- ANSI C程序开始时有三个打开的流:标准输入stdin、标准输出stdout和标准错误stderr
- 类型为FILE的流是对文件描述符和流缓冲区的抽象
10.9 综合:我该使用哪些I/O函数
- 标准I/O函数是磁盘和终端设备I/O之选
- 标准I/O流限制
- 跟在输出函数之后的输入函数:在每个输入操作前刷新缓冲区
- 跟在输入函数之后的输出函数:对同一个代开的套接字打开两个流,一个用来读,一个用来写
- 在网络套接字上不使用标准I/O函数来进行输入和输出,使用健壮的RIO函数
附录A 错误处理
- 给定某个系统程序的包装函数,包装函数调用基本函数并检查错误
教材学习中的问题和解决过程
代码调试中的问题和解决过程
代码托管
上周考试错题总结
- 错题1及原因,理解情况
- 错题2及原因,理解情况
- ...
结对及互评
点评模板:
- 博客中值得学习的或问题:
- xxx
- xxx
- ...
- 代码中值得学习的或问题:
- xxx
- xxx
- ...
- 其他
本周结对学习情况
- [结对同学学号1](博客链接)
- 结对照片
- 结对学习内容
- XXXX
- XXXX
- ...
其他(感悟、思考等,可选)
xxx
xxx
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 2/4 | 18/38 | |
第三周 | 500/1000 | 3/7 | 22/60 | |
第四周 | 300/1300 | 2/9 | 30/90 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:XX小时
-
实际学习时间:XX小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)