20145308 《信息安全系统设计基础》第11周学习总结
20145308 《信息安全系统设计基础》第11周学习总结
教材学习内容总结
第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 *
本周代码托管截图
https://git.oschina.net/yg1022/CSAPP2E.git
其他
- 本周通过阅读教材第八章和调试代码学习了异常控制流的相关内容,感觉自己对老师上课讲授的
fork
理解的比较好,相对而言execve
就理解的不太好了。本周将学习时间提到了周末之前,感觉时间充裕了好多
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/2 | 25/30 | 学习Linux指令 |
第二周 | 50/50 | 1/3 | 25/55 | Linux系统下的开发环境 |
第三周 | 20/70 | 1/4 | 25/80 | 信息的表示和处理 |
第五周 | 20/90 | 1/5 | 30/110 | 程序的机器级表示 |
第六周 | 20/110 | 1/6 | 30/140 | 处理器体系结构 |
第七周 | 20/130 | 1/7 | 30/170 | 存储器层次结构 |
第八周 | 0/130 | 2/9 | 10/180 | 期中复习 |
第九周 | 48/178 | 2/11 | 10/190 | 系统级I/O、错误处理 |
第十周 | 539/717 | 2/13 | 10/200 | 系统调用学习 |
第十一周 | 429/1146 | 2/15 | 10/210 | 异常控制流 |