2017-2018-1 20155333 《信息安全系统设计基础》第六周学习总结
2017-2018-1 20155333 《信息安全系统设计基础》第六周学习总结
教材学习内容总结
- 控制流:控制转移序列。
- 控制转移:从一条指令到下一条指令。
- 异常控制流:现代操作系统通过使控制流发生突变来对系统状态做出反应,这些突变称为异常控制流。
- 异常是允许操作系统提供进程的概念所需要的基本构造块,进程是计算机科学中最深刻最成功的概念之一。
- 处理器在检测到事件(CPU的状态被编码为不同的位和信号)发生时,通过异常表进行一个间接过程调用,到一个专门处理这类事件的OS子系统(异常处理程序)。
类别 | 原因 | 异步/同步 | 返回行为 |
---|---|---|---|
中断 | 来自IO设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可恢复的错误 | 同步 | 可能返回当前指令 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
- 进程的经典定义就是一个执行中的程序的实例。系统中的每个程序都是运行在某个进行上下文中的。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在存储器中的程序的代码和数据,它的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。
- 每次用户通过向外壳输入一个可执行目标文件的名字,并运行一个程序时,外壳就会创建一个新的进程,然后再这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,且在这个新进程的上下文中运行它们自己的代码或其他应用程序。
- 用户模式和内核模式:
- 运行应用程序代码的进程初始是在用户模式中的。进程从用户模式变成内核模式的唯一方法是通过诸如中断,故障或者陷入系统调用这样的异常。当异常发生时,控制传递大哦哦异常处理程序,处理器将模式从用户模式变成内核模式。处理程序运行在内核模式中,当塔返回应用程序代码是,处理器就把模式从内核模式改回用户模式。
- Linux提供了一种聪明的机制,叫做/proc文件系统,它允许用户模式进程访问内核数据结构的内容。/proc文件系统将许多内核数据结构的内容输出为一个用户程序可以读的文本文件的层次结构.
- 上下文切换
- 操作系统内核使用一种称为上下文切换的较高层形式的异常控制流来实现多任务。上下文切换机制是建立在较底层的异常机制之上的。
- 内核为每个进程维持一个上下文。上下文就是内核重新启动一个被抢占的进程所需的状态。它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计算器、用户栈、状态寄存器、内核栈、和各种内核数据结构。
- 进程控制
- 获取进程ID:
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
- 建和终止进程:进程的三种状态——运行、停止和终止。进程会因为三种原因终止进程:收到信号,该信号默认终止进程;从主程序返回;调用exit函数。
#include <stdlib.h>
void exit(int status);
- 父进程通过调用fork创建一个新的运行子进程:父进程与子进程有相同(但是独立的)地址空间,有相同的文件藐视符集合。
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
- 回收子进程:
回收:当一个进程终止时,内核并不立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收。
僵死进程:一个终止了但是还未被回收的进程称为僵死进程。
回收子进程的两种方法:1,内核的init进程 2,父进程waitpid函数
1)如果父进程没有回收它的僵死子进程就终止了,那么内核就会安排init进城来回收它们。init进程的PID为1,并且是在系统初始化时创建的。
2)一个进程可以通过调用waitpid函数来等待它的子进程终止或停止。
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);
waitpid函数有点复杂,默认地(当options=0时),waitpid挂起调用进程的执行,知道它的等待集合中的一个子进程终止。
wait(&status)函数,等价于调用wait(-1,&status,0)
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
- 让进程休眠:
sleep函数将一个进程挂起一段指定的时间。
#include <unistd.h>
unsigned int sleep(unsigned int secs);
pause函数让调用函数休眠,知道该进程收到一个信号。
#include <unistd.h>
int pause(void);
- 加载并运行程序
#include <unistd.h>
int execve(const char *filename,const char *argv[],const char *envp[]);
execve函数加载并运行可执行目标文件filename,且带参数列表qrgv和环境变量列表envp,只有当出现错误时,execve才会返回到调用程序。所以与fork调用一次返回两次不同,execve调用一次并从不返回。
- 环境组操作数
#include <stdlib.h>
char *getenv(const char *name);
getenv在环境数组中搜索字符串"name=value",如果找到了,它就返回一个指向value的指针,否则它就返回NULL。
#include <stdlib.h>
int setenv(const char *name,const char *newvalue,int overwrite);
void unsetenv(const char *name);
如果环境数组包含一个形如"name=oldvalue"的字符串,那么unsetenv会删除它,而setenv会用 newvalue代替oldvalue,但是只有在overwirte非零时才会这样。如果name不存在,那么etenv就把 "name=newvalue"添加到数组中。
- fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
- 在父进程中,fork返回新创建子进程的进程ID;
- 在子进程中,fork返回0;
- 如果出现错误,fork返回一个负值。
- 在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程(子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。),一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
- 信号(操作系统和应用程序之间):进程之间传送信号
一种更高层次的软件形式的异常,称为unix信号,它允许进程中断其他进程。
低层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常。
- 信号处理过程
1)发送信号:内核通过更新目的进程中上下文中的某个状态,发送一个信号给目的进程。发送信号有两个原因:
a)内核检测到一个系统事件;
b)一个进程调用kill函数,心事发送信号
2)接收信号:,目的进程就接收了信号。进程可以忽略这个信号,终止或者通过执行信号处理程序捕获这个信号。
注意:待处理信号,一种类型的信号只能有一种待处理信号,多余的不会排队,而是会舍掉 ; 信号还可以阻塞。
- 发送信号:/bin/kill , kill函数,键盘,alarm函数
进程组:每个进程都只属于一个进程组,进程组是由一个进程组ID来标识的。默认的,一个子进程和它的父进程同属于一个进程组。
在任何时刻,至多只有一个前台作业和0个或多个后台作业。外壳为每个作业创建一个独立的进程组,一个作业对应一个进程组。
用kill函数发送信号:发送SIGKILL信号
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int sig);
用alarm函数发送信号:发送SOGALARM信号
#include <unistd.h>
unsigned int alarm(unsigned int secs);
- 接收信号
进程可以通过使用signal函数来修改和信号相关的默认行为。唯一的例外是SIGSTOP和SIGKILL,它们的默认行为不能被修改。
#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
- 信号处理问题
当一个程序捕获多个信号时,容易有一些细问问题:
待处理信号被阻塞
待处理信号不会排队等待
系统调用可以被中断
- 可移植的信号处理:目的是为了统一同一信号在不通系统中的语义。sigaction函数,或者是它的包装函数Signal函数。
- 显示地阻塞和取消阻塞函数
- 非本地跳转
- C提供了一种形式的用户级异常控制流,称为非本地跳转,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列。是通过setjmp和longjmp函数来提供的。
- 其中仍然需要考虑到状态转移的问题,setjmp函数在env缓冲区中保存了当前栈内容,longjmp通过读取env缓冲区来回复栈的内容。
- setjmp函数只被调用一次,但返回多次:一次是当第一次调用setjmp,而调用环境保存在缓冲区env中时,一次是为每个相应的longjmp调用。另一方面,longjmp只调用一次,但从不返回。sig—函数是setjmp和longjmp函数的可以被信号处理程序使用的版本。
- 非本地跳转的一个重要应用就是允许从一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况引起的。
- 非本地跳转的另一个重要应用是使一个信号处理程序分支到一个特殊的代码位置,而不是返回到达中断了的指令位置。
http://www.cnblogs.com/mydomain/archive/2011/06/18/2084155.html
C++和Java提供的异常机制是较高层次的,是C的setjmp和longjmp函数的更加结构化的版本。
catch子句:setjmp
throw:longjmp
- I/O重定向:dup2(oldfd,newfd);
#include <unistd.h>
int dup2(int oldfd,int newfd);
dup2函数拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在拷贝oldfd之前关闭newfd.(即:让描述符oldfd实现newfd的功能)
eg,dup2(field,1) 将标准描述符输出重定向到field描述符
教材学习中的问题和解决过程
- 问题1:为什么fork会返回两次?
- 问题1解决方案:由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。
- 问题2:异常与过程调用的不同之处
- 问题2解决方案:
- 过程调用时,在跳转到处理程序之前,处理器将返回地址压入栈中。然而,根据异常的类型,返回地址要么是当前指令,要么是下一条指令。
- 处理器也把一些额外的处理器状态压到栈里,在处理程序返回时,重新开始被中断的程序会需要这些状态。
- 如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中,而不是压到用户栈中。
- 异常处理程序运行在内核模式下,这意味着他们对所有的系统资源都有完全的访问权限。
- ...
代码调试中的问题和解决过程
- 问题1:XXXXXX
- 问题1解决方案:XXXXXX
- 问题2:XXXXXX
- 问题2解决方案:XXXXXX
- ...
代码托管
(statistics.sh脚本的运行结果截图)
上周考试错题总结
- 错题1及原因,理解情况
- 错题2及原因,理解情况
- ...
结对及互评
点评模板:
- 博客中值得学习的或问题:
- xxx
- xxx
- ...
- 代码中值得学习的或问题:
- xxx
- xxx
- ...
- 其他
本周结对学习情况
- [2015532](http://www.cnblogs.com/zjy1997/)
- 结对照片
- 结对学习内容
- 教材第八章、第十章
- XXXX
- ...
其他(感悟、思考等,可选)
xxx
xxx
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 10/10 | 1/1 | 10/10 | |
第二周 | 80/90 | 1/2 | 15/25 | |
第三周 | 100/190 | 1/3 | 15/40 | |
第四周 | 150/340 | 1/4 | 18/58 | |
第五周 | 2/6 | 20/78 | ||
第六周 | 2/8 | 20/98 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:20小时
-
实际学习时间:20小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)