20145330 《信息安全系统设计基础》第11周学习总结
教材学习内容总结
8.1 异常
- 异常就是控制流中的突变,用来响应处理器状态中的某些变化
- 8.1.1 异常处理
- 系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号。
- 处理器:被零除、缺页、存储器访问违例、断点以及算术溢出
- 操作系统:系统调用和来自外部I/O设备的信号
- 系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号。
- 8.1.2异常的类别
- 中断
- 来自I/O设备的信号,异步,总是返回到下一条指令
- 陷阱
- 有意的异常,同步,总是返回到下一条指令
- 故障
- 潜在可恢复的错误,同步,可能返回到当前指令
- 终止
- 不可恢复的错误,同步,不会返回
- 中断
- 8.1.3 Linux系统中的异常
- Linux故障和终止
- 除法错误:异常号:0,通常报告为“浮点异常”
- 一般保护故障:异常号:13,Linux不会尝试回复这类故障,通常报告为“段故障”
- 缺页:异常号:14,是会重新执行产生故障的指令的一个异常事例
- 机器检查:异常号:18,从不返回控制给应用程序
- Linux系统调用
- 每个系统调用都有一个唯一的整数号,对应于一个到内核中跳转表的偏移量
- 将系统调用和与他们相关的包装函数称为系统级函数
- Linux故障和终止
8.2 进程
- 系统中的每个程序都是运行在某个进程的上下文中的。上下文是由程序正确运行所需的状态组成的
- 8.2.1 逻辑控制流
- 程序计数器(PC)的值的序列叫做逻辑控制流,简称逻辑流
- 每个进程执行它的流的一部分,然后被抢占(暂时挂起),然后轮到其他进程
- 8.2.2 并发流
- 一个逻辑流的执行在时间上与另一个流重叠,称为并发流
- 并发:多个流并发的执行的一般现象
- 多任务:一个进程和其他进程轮流运行
- 时间片:一个进程执行它的控制流的一部分的每一段时间
- 习题8.1
- 8.2.3 私有地址空间
- 这个空间中某个地址相关联的那个存储器字节是不能被其他进程读或者写的
- 8.2.4 用户模式和内核模式
- 当设置了模式位时,进程就运行在内核模式中
- 没有设置模式位时,进程就运行在用户模式中。用户模式中的进程不允许执行特权指令。
- 进程从用户模式变位内核模式的唯一方法是通过诸如中断、故障或者陷入系统调用这样的异常。
- /proc文件系统允许用户模式进程访问内核数据结构的内容
- 8.2.5 上下文切换
- 上下文切换:是较高形式的异常控制流来实现多任务
- 调度:在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。这种决定叫做调度
- 当内核代表用户执行系统调用时,可能会发生上下文切换。如果系统调用因为等待某个事件发生阻塞,那么内核可以让当前进程休眠,切换到另一个进程
- 中断也可能引发上下文切换
- 8.2.1 逻辑控制流
8.3 系统调用错误处理
- 定义错误报告函数,简化代码
- 错误处理包装函数
8.4 进程控制
- 8.4.1获取进程ID
- 每个进程都有一个唯一的正数进程ID
- gitpid函数返回调用进程的PID,getppid函数返回它的父进程的PID(创建调用进程的进程)
- 8.4.2 创建和终止进程
- 进程状态:运行、停止、终止
- exit函数以status退出状态来终止进程
- fork函数
- 父进程通过调用fork函数创建一个新的运行子进程
- 当父进程调用fork时,子进程可以读写父进程中打开的任何文件
- 父进程和子进程之间最大的区别在于有不同的PID
- 调用一次,返回两次
- 并发执行
- 相同但是独立的地址空间
- 共享文件
习题8.2
- 8.4.3 回收子进程
-
僵死进程没有运行,他们仍然消耗系统的存储器资源
-
一个进程可以通过调用waitpid函数来等待它的子进程终止或停止
-
1.判定等待集合的成员(由参数pid确定)
- pid>0,等待集合就是一个单独的子进程,它的进程ID等于pid
- pid=-1,等待集合就是由父进程所有的子进程组成的
-
2.修改默认行为
- WNOHANG:默认行为是挂起调用进程,直到有子进程终止
- WUNTRCED:默认行为是只返回已经终止的子进程
- WNOHANG|WUNTRCED:立即返回
-
3.检查已回收子进程的退出状态
-
4.错误条件
- 如果调用进程没有子进程,那么waitpid返回-1,设置errno为ECHILD
- 如果waidpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR
-
5.wait函数
-
6.使用waitpid的示例
-
- 8.4.4 让进程休眠
- sleep函数将一个进程挂起一段指定的时间
- pause函数让调用函数休眠,直到该进程收到一个信号
- 8.4.5 加载并运行程序
- execve函数在当前进程的上下文中加载并运行一个新程序,execve调用一次并从不返回
- argv参数列表,envp环境变量
- getenv函数
- execve函数在当前进程的上下文中加载并运行一个新程序,execve调用一次并从不返回
8.5 信号
- Unix信号:更高层的软件形式的异常,允许进程中断其他进程
- P505:30种不同类型的信号表
- 8.5.1 信号术语
- 发送信号:1)内核检测到一个系统事件 2)一个进程调用了kill函数
- 接收信号:当目的进程被内核强迫以某种方式对发送的信号作出反应时,目的进程就接收了信号
- 待处理信号:一个只发出而没有被接收的信号
- 8.5.2 发送信号
- 1.进程组
- 每个进程都只属于一个进程组
- 一个进程可以通过使用setpigd函数来改变自己或者其他进程的进程组
- 2.用/bin/kill程序发送信号
- 一个为负的PID会导致信号被发送到进程组PID中的每个进程中
- 3.从键盘发送信号
- 4.用kill函数发送信号
- 5.用alarm函数发送信号
- 1.进程组
- 8.5.3 接收信号
- 如果集合是非空的,那么内核选择集合中的某个信号k,并且强制p接收信号k
- signal函数可以改变和信号signum相关联的行为
- 8.5.4 信号处理问题
- 捕获多个信号时的问题:
- 待处理信号被阻塞
- 待处理信号不会排队等待
- 系统调用可以被中断
- 捕获多个信号时的问题:
- 8.5.5 可移植的信号处理
- 不同系统之间,信号处理语义有差异
- sigaction函数明确地指定它们想要的信号处理语义
- 8.5.6 显式地阻塞和取消阻塞
- 应用程序可以使用sigprocmask函数显式地阻塞和取消阻塞选择的信号
- sigprocmask函数改变当前已阻塞信号的集合
8.6非本地跳转
- 通过setjmp和longjmp函数来提供
- setjmp函数只被调用一次,但返回多次:一次是当第一次调用setjmp,而调用环境保存在缓冲区env中时,一次是为每个相应的longjmp调用
- longjmp只调用一次,但从不返回
- 非本地跳转的一个重要应用就是允许从一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况引起的
- 非本地跳转的另一个重要应用是使一个信号处理程序分支到一个特殊的代码位置,而不是返回到达中断了的指令位置
8.7 操作进程的工具
- Linux系统提供监控和操作进程的工具:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹(STRACE)、列出当前系统中包括僵死进程的进程(PS)、打印出关于当前进程资源使用的信息(TOP)、显示进程的存储器映射(PMAP)、虚拟文件系统(/proc)
代码调试
exec
1.exec1
- exec1.c中execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件
- 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中
- 编译运行结果如下,没有返回“* * * man is done. bye”
2.exec2
- 若将exevp函数传入的arglist[0]改为arglist[1],此时exevp函数没有调用成功,于是打印出“* * * ls is done. bye”这句话
- exec2.c代码运行如下
3.exec3
-
exec3.c代码运行如下
-
函数中execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……最后一个参数必须用空指针(NULL)作结束
env
1.environ.c
-
environ.c代码运行如下
-
代码中涉及到getenv函数和setenv函数
-
getenv函数在环境数组中搜索字符串“name=value”。如果找到了,就返回一个指向value的指针,否则就返回NULL
-
setenv函数是修改或添加环境变量的函数
2.environvar.c
-
environvar.c代码简单打印环境变量表,运行结果如下
-
每个程序都有一个环境表,它是一个字符指针数组,其中每个指针包含一个以NULL结尾的C字符串的地址。
-
全局变量environ则包含了该指针数组的地址
fifo
- fifo是一种文件类型,可以通过查看文件stat结构中的stmode成员的值来判断文件是否是FIFO文件。fifo是用来在进程中使用文件来传输数据的,也具有管道特性,可以在数据读出的时候清除数据
1.consemer.c管道写端
2.producer.c管道读端
fork
1.forkdemo1
- 打印进程pid,然后调用fork函数生成子进程,休眠一秒后再次打印进程id,这时父进程打印子进程pid,子进程返回0。
2.forkdemo2
- 调用两次fork,一共产生四个子进程,所以会打印四个aftre输出.
3.forkdemo3
- fork产生子进程,父进程返回子进程pid,不为0,所以输出父进程的那句话,子进程返回0,所以会输出子进程那句话。
4.forkdemo4
- 先打印进程pid,然后fork创建子进程,父进程返回子进程pid,所以输出parent一句,休眠十秒;子进程返回0,所以输出child与之后一句。
5.forkgdb
- 父进程打印是先打印两句,然后休眠一秒,然后打印一句,子进程先打印一句,然后休眠一秒,然后打印两句。并且这两个线程是并发的,所以可以看到在一个线程休眠的那一秒,另一个线程在执行,并且线程之间相互独立互不干扰
wait
1.waitdemo1
- waitdemo1.c的功能是如果有子进程,则终止子进程,成功返回子进程pid。
2.waitdemo2
- waitdemo2.c比起1来就是多了一个子进程的状态区分,把状态拆分成三块,exit,sig和core
再次学习代码驱动的程序设计
参考博客:http://www.cnblogs.com/rocedu/p/5927306.html
-
建立教材的项目文件夹,在20145330CSAPP2E文件夹下建立相应的文件夹:
- src:存放源代码文件
- include: 存放头文件
- bin:存放编译后的目标文件、可执行文件等
- lib:存放项目所需的静态库、动态(共享)库
- res: 存放项目所需的图标、声音、图片等资源
- docs: 存放项目相关的参考资料、帮助文档,比如大家的学习博客就可以放在这里
-
之前也建立过文件夹,但还是文件所有的部分都放在一个文件夹下
-
再次学习博客,知道如何放入相应文件夹。
-
例如在src/01intro目录下输入教材第一章第一页的hello world程序hello.c, 我们使用vim src/01intro/hello.c命令。
-
在Vim中通过:wq保存hello.c并退出Vim后,我们使用gcc src/01intro/hello.c -o bin/hello来编译程序,注意命令中相对路径的使用。可执行程序hello生成在bin目录下,我们可以使用./bin/hello来执行hello程序。
-
此时目录结构如下图所示:
- 我们通过git init 把项目纳入git管理。以后就是最常用的git add; git commit了,使用git commit时一定要注意解释一下刚才做了什么。
- git cheatsheet中有常用的git 查看命令
- git log会列出所有历史记录
-
但是命令过长难以记忆,比如上面我们使用gcc src/01intro/hello.c -o bin/hello来编译hello world程序。
-
我们可以通过shell脚本来简化编译运行程序。注意我们要用chmod +x compile.sh给脚本添加可执行权限,我们就可以用./compile.sh来执行脚本了。
-
脚本中是编译、运行程序的命令,shell脚本中第一行是固定写法, #后面是注释:
-
源代码产生的中间文件是不需要纳入git的管理的,也就是说我们上面建立的项目目录结构中的bin是不需要git管理的,否则项目所占空间可能会很大,我们可以使用.gitignore文件把不想纳入git管理的文件、文件夹排除掉。
-
我们完成一个小任务,就git add; git commit 一下
-
这时可以git log以下列出历史记录
-
-
积极主动敲代码
- 以p44例题为例
- 每次编写完一个代码就git add .;git commit -m ""一下,养成良好习惯。
本周代码托管截图
感悟与思考
这周的学习内容很丰富,需要化整为零每天学习一点,并且需要将第八章的内容与老师给的代码结合起来学习,才能更深刻的理解好各种函数的作用。之前代码驱动的程序设计我掌握的不是很好,所以在这周初我先又重新学习了以下,对这一周的学习很有帮助,希望坚持学习态度。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 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 | |
第五周 | 50/1350 | 2/11 | 20/120 | |
第六周 | 100/1450 | 1/12 | 20/140 | |
第七周 | 50/1500 | 1/13 | 20/160 | |
第八周 | 000/1500 | 2/15 | 30/190 | |
第九周 | 50/1550 | 2/17 | 30/220 | |
第十周 | 400/2050 | 2/19 | 30/280 | |
第11周 | 500/2550 | 2/21 | 30/310 |