Exceptional Flow Control(异常控制流)
来源:互联网 发布:移动网络营业 编辑:程序博客网 时间:2024/06/04 20:24
- 异常控制流的形式
- 异常
- 进程的上下文切换
- 信号
- 信号的发送与接收
- 信号处理
- 非本地跳转
转载请注明出处:http://blog.csdn.net/c602273091/article/details/53543145
异常控制流的形式
控制流一般是说处理器中比较平稳的程序执行过程。我们之前知道的改变控制流的方法有跳转和函数返回,但是还有很多情况我们没有考虑到。比如键盘输入、程序崩溃之类的。这些东西呢就是叫做异常控制流。
异常控制流一般有以下几种形式:
接下来就一个一个介绍他们。
异常
异常:用来响应低级的控制流的突变,把控制权转给内核以应对某些变化。
处理异常的时候需要询问异常表。根据异常表的描述进行操作。
在异常中,又分为:中断、陷阱、故障、终止。
中断是外部产生的,比如网络适配器。处理完这种中断之后,返回下一条指令。陷阱是用户请求系统调用的方法。比如用户需要执行execve,read等等。故障就是发生了错误,一般可以修复。比如缺页异常。终止:不可修复的错误。直接abort。
Tips:
1、一些常用的系统调用:
进程的上下文切换
进程就是正在执行的程序的一个实例。
在进程中有两个抽象:独立的逻辑控制流,感觉拥有了整个处理器;私有的地址空间,感觉独占了存储器系统。
CPU执行进程,感觉是在同时进行,这就是并发(Concurrency)然后每个进程都有自己拥有CPU的错觉。
在进程之间,有时候控制会传递,这就是进程的上下文切换。
进行了上下文切换,自然就是改变了原先的控制流。
PS:
1、系统调用错误的处理函数:
2、获取进程ID:
3、进程终止:exit() 一般返回0;错误的时候返回非0。进程收到SIGTTOU、SIGSTOP、SIGSTP、SIDTTIN的时候,进程停止,被挂起,直到接收到SIGCONT。
4、创建进程:fork() 调用一次,返回两次。
5、为了更好地理解fork的过程,使用进程图可以使思路清晰。
把图化成下面这样,只要箭头不是向右,那么就是不符合的。
再举个例子:
6、回收子进程:一般来说子进程需要被父进程回收,如果父进程没有机会回收(比如被意外终止),那么子进程就变成了zombie。如果长时间没有被回收,那么就会被init回收。父进程回收的时候一般使用waitpid或者wait。
7、waitpid:可以等待专门的进程。pid > 0就是专门的子进程;为-1的时候就是任何一个。
waitpid(pid, &status, 0);
8、wait(&status);回收一个子进程。当status不为NULL的时候,那么就可以有:
9、execve:运行程序。
int execve(char *filename, char *argv[], char *envp[])
执行一次,从不返回。除非找不到文件名才会返回-1。
它创建的进程的栈如下:
10、使用sleep,pause,sigsuspend挂起程序。
综合以上,一个进程相关的系统调用就是:
信号
当某些特殊的事件发生之后,系统就会发送信号给进程。比如按了ctrl-c,SIGINT信号就会发给正在前台运行的进程来通知关闭这个进程。而接收信号的进程可以选择接收,然后自己被终止或者挂起,直到接收到SIGCONT。但是进程也可以选择阻塞或者屏蔽这些信号。与此同时,进程也可以发送信号。比如父进程就可以使用KILL发送SIGKILL给子进程来关闭子进程。接下来详细说一下信号相关的操作。
信号的发送与接收
信号可以被阻塞,那就是被pending。每个进程只有一个类型为k的待处理信号。如果这个时候再发k信号过来,这个发过来的信号就会被丢弃。信号的发送,都是基于进程组的。进程分成不同的组,使用getpgrp可以获得某个进程的group id。一个进程可以使用setpgid设置group id。set(pid_t pid, pid_t gpid);
一般来说,内核会发送信号的原因有:内核检测到了系统事件,比如除0。比如按了键盘,也可以发送信号;一个进程调用了kill函数,显示发送信号。kill(pid, SIGKILL) 发给进程pidSIGKILL信号。alarm可以用来发信号给自己。每过secs个时间,发送SIGALARM信号给自己。unsigned int alarm(unsigned int secs).
接收信号:内核从处理异常的程序返回时,查看pending signal是否为空;为空的话就将控制权交到原来的进程处;否则就进行相应的信号处理。
在进程捕获信号的时候,有一个信号处理函数,对捕获的信号进行处理。
handler_ t *signal(int signum, hander_t *handler);
signum就是要处理的信号;handler就是信号处理函数。
handler可以设置成SIG_ IGN: 忽略signum信号;SIG_ DFL 恢复signum的默认行为。
请看下面的例子:
信号处理
单独处理一个信号是简单的,但是对多个信号处理的时候,信号处理就变得复杂了。待处理信号会被阻塞,待处理信号不会排队,系统调用可以被中断等等。举个例子,我们fork很多个子进程,然后在handler里面只能回收几个,小于fork的数目,有的子进程变成了zombie。那么就需要某些机制处理这个问题。这里就用到了sigpomask。这个函数可以显示地阻塞和释放信号集。
首先阻塞mask里面的信号,之前的信号集存在pre_ mask,然后处理结束之后,还原原来的信号集。
首先给出一个错误的回收信号的例子:
在信号处理函数里面,使用的是if,那么很多SIGCHILD信号发送过来的时候,很可能正在进行信号处理,有一个SIGCHILD信号被阻塞,别的都被丢弃了。
对以上的handler进行修改:
上面的修改解决了有些信号不能正常接收被丢弃的问题,但是上面的例子还有一个问题。那就是竞争问题。
如果fork的子进程执行结束,发送了SIGCHILD给handler,那么就会delete job,但是这个时候还没有add job,那么竞争就在这个时候发生了。
解决这个问题的方法如下:把子进程的SIGCHILD block就可以了。
在进行back ground job处理的时候,一般会显示地等待它结束。那么在这里就需要在父进程中加入显示等待。
显示等待可以使用while循环、pause、sleep、sigsuspend。
使用循环如下:(这种方法极大地浪费了CPU cycle,就是传说中的空转)
使用pause、sleep:
使用sigsuspend。
这里可以看到有一个pid,在handler里面改变它即可。一般来说,这个改成volatile的全局变量更加安全。
Tip:
使用简单的信号处理函数。
非本地跳转
nonlocal jump:将控制从一个函数转移到另外一个正在执行的函数。
使用setjmp和longjmp实现。longjmp调用一次,从不返回。setjmp调用一次,返回多次。
具体看CSAPP那本书,没有深入了解这个。
Pictures are adopted from course cmu 15-213Click here
- Exceptional Flow Control(异常控制流)
- Exceptional C++: [Item 47. Control Flow] [条款47 控制流]
- 串口流控制(flow control)
- 串口流控制(flow control)
- 串口流控制(flow control)
- 串口流控制(flow control)
- Swift 控制流(Control Flow)
- 串口流控制(flow control)
- knockout控制流Control Flow
- Swift Control Flow控制流
- 控制流 Flow of Control
- Chanpter Exceptional Control Flow-- Classes of Exceptions
- Chapter 8 Exceptional Control Flow -- Nonlocal Jumps
- Signal Handling on Exceptional Control Flow
- 对照Java学习Swift--控制流(Control Flow)
- 《CS:APP》 chapter 8 Exceptional Control Flow 笔记
- clisp 记录:5. 数据和控制流(Data and Control Flow)
- [iOS翻译]《The Swift Programming Language》 Control Flow - 控制流
- 传统Socket编程的回顾(一)
- POJ 2528 - Mayor's posters(模拟)
- 高质量的java命名规范
- 使用调色板Palette在背景图中取色
- espresso之自由swipe
- Exceptional Flow Control(异常控制流)
- 第十六周 -项目1 -(4)验证快速排序
- jsonobject不能转xml
- 编程常用缩写
- 第十六周项目2---大数据集上排序算法性能的体验
- JQuery Datatables 在IE浏览器表头显示错乱问题
- graviry与layoutgravity的区别---Android开发中
- Android快速实现仿美团选择城市界面,微信通讯录界面
- python字符编码的判断