20135327郭皓——信息安全系统设计基础第十一周学习总结

第十一周(11.16-11.22):

 

学习计时:共6小时

读书:

代码:

作业:

博客:

一、学习目标

1. 了解异常及其种类
2. 理解进程和并发的概念
3. 掌握进程创建和控制的系统调用及函数使用:fork,exec,wait,waitpid,exit,getpid,getppid,sleep,pause,setenv,unsetenv,
4.  理解数组指针、指针数组、函数指针、指针函数的区别
5. 理解信号机制:kill,alarm,signal,sigaction
6. 掌握管道和I/O重定向:pipe, dup, dup2
 
 
 
 

第八章 异常控制流

  • 现代系统通过使控制流发生突变来对这些情况做出反应。我们把这些突变称为异常控制流

  • 作为程序员,理解 ECF 很重要:

    • 理解 ECF 将帮助你理解重要的系统概念。
    • 理解 ECF将帮助你理解应用程序是如何与操作系统交互的。
    • 理解 ECF 将帮助你编写有趣的新应用程序。
    • 理解 ECF 将帮助你理解并开发。
    • 理解 ECF 将帮助你理解软件异常如何工作。
 

8.1 异常

  • 异常是异常控制流的一种形式,它一部分是由硬件实现的,一部分是由操作系统实现的
  • 异常( exception)就是控制流中的突变,用来响应处理器状态中的某些变化。
  • 当异常处理程序完成处理后,根据引起异常的事件的类型,会发生以下三种情况中的一种: 
    1)处理程序将控制返回给当前指令 curr 即当事件发生时正在执行的指令。 
    2) 处理程序将控制返回给 酬,即如果没有发生异常将会执行的下一条指令。 
    3) 处理程序终止被中断的程序。
 

8.1.1 异常处理

  • 系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号。
  • 异常表的起始地址放在一个叫做异常在基址寄存器的特殊CPU 寄存器里。
  • 异常类似于过程调用,但是有一些重要的不 
    同之处。 
    • ·过程调用时,在跳转到处理程序之前,处理器将返回地址压人找中。
    • 处理器也把一些额外的处理器状态压到栈里,在处理程序返回时,重新开始被中断的程序会需要这些状态。
    • 如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中,而不是压到用户栈中。
    • 异常处理程序运行在内核模式下,这意味着它们对所有的系统资源都有完全的访问权限。
 

8.1.2 异常的类别

  • 异常可以分为四类:中断( interrupt)、陷阱 (trap)、故障 (fault) 和终止 (abort)。

1.中断

  • 中断是异步发生的,是来自处理器外部的1/ 设备的信号的结果。
  • 硬件中断的异常处理程序通常称为中断处理 
    程序。
  • 剩下的异常类型(陷阱、故障和终止〉是同步发生的,是执行当前指令的结果。我们把这类指令叫做故障指令。

2.陷阱和系统调用

  • 陷阱是有意的异常,是执行一条指令的结果。就像中断处理程序一样,陷阱处理程序将控制返回到下一条指令。陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。

3.故障

  • 故障由错误情况引起,它可能能够被故障处理程序修正。当故障发生时,处理器将控制转移给故障处理程序。如果处理程序能够修正这个错误情况,它就将控制返回到引起故障的指令,从而重新执行它。否则,处理程序返回到内核中的 abort 例程, abort 例程会终止引起故障的应用程序。

4.终止

  • 终止是不可恢复的致命错误造成的结果,通常是一些硬件错误,比如 DRAM 或者SRAM被损坏时发生的奇偶错误。终止处理程序从不将控制返回给应用程序。
 

8.1.3 Linux/IA32系统中的异常

  1. Linux/IA32故障和终止
  • 除法错误
  • 一般保护故障
  • 缺页
  • 机器检查

2.linuxllA32 系统调用

  • Linux 提供上百种系统调用,当应用程序想要请求内核服务时可以使用,包括读文件、写文件或是创建一个新进程。

 

8.2 进程

  • 异常是允许操作系统提供进程 (process) 的概念所需要的基本构造块,进程是计算机科学中 
    最深刻最成功的概念之一。
  • 进程提供给应用程序的关键抽象: 
    • 一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。
    • 一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用存储器系统。
 

8.2.1 逻辑控制流

  • 如果想用调试器单步执行程序,我们会看到一系列的程序计数器 (PC)值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或者是包含在运行时动态链接到程序的共享对象中的指令。这个 PC值的序列叫做这辑控制流,或者简称逻辑流。
  • 每个进程执行它的流的一部分,然后被抢占 (preempted) (暂时挂起),然后轮到其他进程。
 

8.2.2 并发流

  • 一个逻辑流的执行在时间上与另一个流重叠,称为并发流 (concurrent fiow) ,这两个流被称为并发地运行.
  • 多个流并发地执行的一般现象称为并发 (concurrency)。一个进程和其他进程轮流运行的 
    概念称为多任务 (multitang).一个进程执行它的控制流的一部分的每一时间段叫做时间片(time slice)。因此,多任务也叫做时间分片(time.slicing).
 

8.2.3 私有地址空间

  • 进程也为每个程序提供一种假象,好像它独占地使用系统地址空间。在一台有 位地址的机器上,地祉空间是 个可能地址的集合 0, 1,…, -l. 一个进程为每个程序提供它自己的私有地址空间。
 

8.2.4 用户模式和内核模式

  • 处理器通常是用某个控制寄存器中的一个模式位 (mode bω 来提供这种功能的,该寄存器描述了进程当前享有的特权。当设置了模式位时,进程就运行在内核模式中(有时叫做超级用户模式)。一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中任何存储器位置。
  • 没有设置模式位时,进程就运行在用户模式中。用户模式中的进程不允许执行特权指令(privi1eged instruction),比如停止处理器、改变模式位,或者发起一个I/O操作。也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。任何这样的尝试都会导致致命的保护故障。反之,用户程序必须通过系统调用接口间接地访问内核代码和数据。
 

8.2.5 上下文切换

  • 操作系统内核使用一种称为上下丈切换 (context switch) 的较高层形式的异常控制流来实现多任务。
  • 内核为每个进程维持一个上下文(context)。上下文就是内核重新启动一个被抢占的进程所需的状态。
  • 在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。这种决定就叫做调度,是由内核中称为调度器( scheduler)的代码处理的。
  • 当内核选择一个新的进程运行时,我们就说内核调皮了这个进程。在内核调度了一个新的进程运行后,它就抢占当前进程,并使用一种称为土下丈切换的机制来将控制转移到新的进程,上下文切换 
    1)保存当前进程的上下文, 
    2) 恢复某个先前被抢占的进程被保存的上下文, 
    3) 将控制传递给这个新恢复的进程。

 

8.3 系统调用错误处理

见书P491

 

8.4 进程控制

 

8.4.1 获取进程ID

  • 每个进程都有一个唯一的正数(非零)进程 ID (PID). getpid 函数返回调用进程的 PID。getppid 画数返回它的父进程的 PID (创建调用进程的进程〉。
  • getpid getppid 函数返回一个类型为 pid_t 的整数值,在 Linux 系统上它在 types.h中被定义为 int。
 

8.4.2 创建和终止进程

  • 进程总是处于下面三种状态之-:

    • 运行。进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。
    • 停止。进程的执行被挂起(suspend),且不会被调度。当收到 SIGSTOP、SIGTSTP、SIDTTN或者 SIGTTOU 信号时,进程就停止,并且保持停止直到它收到一个 SIGCONT 
      信号,在这个时刻,进程再次开始运行。
    • 终止。进程永远地停止了。进程会因为三种原因终止: 
      1) 收到一个信号,该信号的默认行为是终止进程。 
      2) 从主程序返回。 
      3) 调用 exit 函数。
  • fork函数的特点

    • 调用一次,返回两次
    • 并发执行
    • 相同但是独立的地址空间
    • 共享文件

8.4.3 回收子进程

  • 当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种己终止的状态中,直到被它的父进程回收 (reap)。当父进程回收已终止的子进程时,内核 
    将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了。一个终止了但还未被回收的进程称为僵死进程 (zombie)。
  • waitpid函数 
    1. 判断等待集合的成员 
      等待集合的成员是由参数 pid 来确定的: 
      • 如果 pid>0,那么等待集合就是一个单独的子进程,它的进程lD等于 pid
      • 如果 pid = -1 ,那么等待集合就是由父进程所有的子进程组成的。
    2. 修改默认行为 
      可以通过将 optioins 设置为常量 WNOHANG WUNTRAα 的各种组合,修改默认行为: 
      • WNOHANG: 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。默认的行为是挂起调用进程,直到有子进程终止。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。
      • WUNTRACED :挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止。返回的 PID 为导致返回的己终止或被停止子进程的 PID。默认的行为是只返回己终止的子进程。当你想要检查已终止和被停止的子进程时,这个选项会有用。
      • WNOHANG UNTRACED: 立即返回,如果等待集合中没有任何子进程被停止或已终止,那么返回值为 ,或者返回值等于那个被停止或者己终止的子进程的 PID
    3. 检查已回收子进程的退出状态 
      如果 status 参数是非空的,那么 waitpid 就会在 status 参数中放上关于导致返回的子进程的状态信息。 wait.h 头文件定义了解释 status 参数的几个宏 
      • WIFEXITED (status) :如果子进程通过调用 exit 或者一个返回 (return) 正常终止,就返回真。
      • WEXITSTATUS (status) 返回一个正常终止的子进程的退出状态。只有在 WIFEXITED返回为真时,才会定义这个状态。
      • WIFSIGNALED (status): 如果子进程是因为一个未被捕获的信号终止的,那么就返回真。
      • WTERMSIG (status): 返回导致子进程终止的信号的数量。只有在 WIFSIGNALED(status) 返回为真时,才定义这个状态。
      • WIFSTOPPED (status) :如果引起返回的子进程当前是被停止的,那么就返回真。
      • WSTOPSIG (status): 返回引起子进程停止的信号的数量。只有在 WIFSTOPPED(status) 返回为真时,才定义这个状态。
    4. 错误条件 
      • 如果调用进程没有子进程,那么waitpid返回-1,并且设置 errno为ECHILD。如果waitpid函数被一个信号中断,那么它返回一1,并设置 errno为EINTR
    5. wait函数 
      • wait函数是waitpid函数的简单版本。
      • 调用 wait(&status) 等价于调用 waitpid(-l &status , 0)
 

8.4.4 让进程休眠

  • sleep函数将一个进程挂起一段指定的时间。
 

8.4.5 加载并运行程序

  • exceve函数在当前进程的上下文中加载并运行一个新程序
  • execve 函数加载并运行可执行目标文件filename,且带参数列表 argv和环境变量列表envp。

 

8.5 信号

  • 一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。
 

8.5.1 信号术语

  • 传送一个信号到目的进程是由两个不同步骤组成的: 
    • 发送信号。内核通过更新目的进程上下文中的某个状态,发送(递送)一个信号给目的进程。发送信号可以有如下两个原因: 
      1)内核检测到一个系统事件,比如被零除错误或者 
      子进程终止。 
      2) 一个进程调用了kill函数,显式地要求内核发送一个信号给目的进程。一个进程可以发送信号给它自己。
    • 接收信号。当目的进程被内核强迫以某种方式对信号的发送做出反应时,目的进程就接收了信号。进程可以忽略这个信号,终止或者通过执行一个称为信 处理程的用户层函数捕获这个信号。
  • 一个只发出而没有被接收的信号叫做待处理信号。
 

8.5.2 发送信号

  1. 进程组 
    • Unix 系统提供了大量向进程发送信号的机制。所有这些机制都是基于进程组这个概念的。
    • 每个进程都只属于一个进程组,进程组是由一个正整数进程纽 ID来标识的。getpgrp函数返回当前进程的进程组 ID:
    • 默认地,一个子进程和它的父进程同属于一个进程组。一个进程可以通过使用setpgid数来改变自己或者其他进程的进程组:

  2. 用/bin/kill 程序发送信号 

    • /bin/kill 程序可以向另外的进程发送任意的信号。比如,命令 

ix> /bin/ki11 -9 15213

  3. 从键盘发送信号 

    • Unix 外壳使用作业这个抽象概念来表示为对一个命令行求值而创建的进程。在任何时刻,至多只有一个前台作业和个或多个后台作业。

  4. 用kill函数发送信号 

    • 进程通过调用kill函数发送信号给其他进程(包括它们自己)。如果pid大于霉,那么kill函数发送信号 sig 给进程 pid。如果pid小于霉,那么kill 发送信号 sig 给进程组 abs (pid) 中的每个进程。

  5. 用alarm函数发送信号 

    • 进程可以通过调用alarm函数向它自己发送SIGALRM信号。

 

8.5.3 接收信号

  • 当内核从一个异常处理程序返回,准备将控制传递给进程 时,它会检查进程的未被阻塞的待处理信号的集合。如果这个集合为空(通常情况下),那么内核将控制传递到的逻辑控制流中的下一条指令。
  • 然而,如果集合是非空的,那么内核选择集合中的某个信号并且强制接收信号k.收到这个信号会触发进程的某种行为。一旦进程完成了这个行为,那么控制就传递回的逻辑控制流中的下一条指令(]next)。每个信号类型都有一个预定义的默认行为,是下面中的一种 
    • 进程终止。
    • 进程终止并转储存储器 (dump core)
    • 进程停止直到被 SIGCONT 信号重启.
    • 进程忽略该信号。

  • signal 函数可以通过下列三种方法之一来改变和信号signum相关联的行为 
    • 如果 handler是SIG_IGN,那么忽略类型为signum的信号。
    • 如果 handler是SIG_DFL,那么类型为signum的信号行为恢复为默认行为。
    • 否则, handler就是用户定义的函数的地址,这个函数称为信号处理程序,只要进程接收到一个类型为 signum的信号,就会调用这个程序。通过把处理程序的地址传递到signal函数从而改变默认行为,这叫做设置信号处理程序。调用信号处理程序称为捕获信号。执行信号处理程序称为处理信号。
 

8.5.4 信号处理问题

  • 对于只捕获一个信号并终止的程序来说,信号处理是简单直接的。然而,当一个程序要捕获多个信号时,一些席位问题就出现了: 
    • 待处理信号被阻塞
    • 待处理信号不会排队等待
    • 系统调用可以被中断
  • 重要教训:不可以用信号来对其它进程中发生的事件计数。
 

8.5.5 可移植的信号处理

  • 不同系统之间,信号处理语义的差异〈比如一个被中断的慢速系统调用是重启还是永久放弃)Unix信号理的一个缺陷。为了处理这个问题, Posix标准定义了 sigaction 函数,它允许像Linux和Solaris这样与Posix 兼容的系统上的用户,明确地指定他们想要的信号处理语义。
  • Signal 包装函数设置了一个信号处理程序,其信号处理语义如下: 
    • 只有这个处理程序当前正在处理的那种类型的信号被阻塞。
    • 和所有信号实现一样,信号不会排队等待。
    • 只要可能,被中断的系统调用会自动重启。
    • 一旦设置了信号处理程序,它就会一直保持,直到 Signal带着handler参数为SIG_IGN或者SIG_DFL被调用。
 

8.5.6 显式地阻塞和取消阻塞信号

  • 应用程序可以使用sigprocmask函数显式地阻塞和取消阻塞选择的信号:
  • sigprocmask 函数改变当前已阻塞信号的集合。具 
    体的行为依赖于 how 的值: 
    • SIG_BLOCK: 添加 set 中的信号到 blocked (blocked = blocked | set).
    • SIG_UNBLOCK: blocked 中删除 set 中的信号 (blocked = blocked & ~set)
    • SIG_SETMASK: blocked = set
 

8.5.7 同步流以避免讨厌的并发错误

 

8.6 非本地跳转

  • C语言提供了一种用户级异常控制流形式,称为非本地跳转,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列。非本地跳转是通过setjmp和longjmp函数来提供的。
 

8.7 操作进程的工具

  • Linux 系统提供了大量的监控和操作进程的有用工具:
    • STRACE:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹。
    • PS: 列出当前系统中的进程(包括僵死进程)。
    • TOP: 打印出关于当前进程资源使用的信息。
    • PMAP: 显示进程的存储器映射。
    • /proc: 一个虚拟文件系统,以ASCII文本格式输出大量内核数据结构的内容,用户程序可读取这些内容。
 

小结

  • 异常控制流(ECF)发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制。 
    • 在硬件层,异常是由处理器中的事件触发的控制流中的突变。控制流传递给一个软件处理程序,该处理程序进行一些处理,然后返回控制给被中断的控制流。
    • 有四种不同类型的异常中断、故障、终止和陆阱。当一个外部I/O设备,例如定时器芯片或者一个磁盘控制器,设置了处理器芯片上的中断引脚时,中断会异步地发生。
    • 控制返回到故障指令后面的那条指令。一条指令的执行可能导致故障和终止同时发生。故障处理程序会重新启动故障指令,而终止处理程序从不将控制返回给被中断的流。最后,陆阱就像是用来实现向应用提供到操作系统代码的受控的人口点的系统调用的函数调用。 
    • 主要来说这一章要重点理解掌握代码,理解知识点。

 

posted @ 2015-11-21 13:11  20135327郭皓  阅读(212)  评论(0编辑  收藏  举报