文章目录
1.异常
异常控制流
顺序执行或者通过正常的Call、Ret、JMP等进行跳转的执行流称为正常控制流,除此以外的就称为异常控制流。
逻辑控制流 | 物理控制流
对于某个处理器而言,他的某段时间上的控制流称为物理控制流。
对于某个进程而言,某段时间上执行该进程的整个控制流称为逻辑控制流。
内部异常&外部中断
例1:缺页
程序需要到内存去取指令,可能发生内存缺页(查页表发现V或者P那位为0),取不到那条指令,这个程序就执行不下去了,就发生了异常。这种情况下用户程序是无能为力的,因为用户程序是不能直接访问磁盘去读那个程序的,只能喊操作系统过来处理去缺页,由操作系统去读磁盘把缺页的那一页取过来装到内存填好页表,然后从缺页处理程序返回,返回后继续执行。所以缺页这种异常是可修复的。
像栈溢出、访问越权(访问了不该访问的内存),访问越级(用户级别的去访问内核),是不可修复的,直接杀死进程。
例2:整除0
内部异常,如整除0,发生这种情况时不知道应该得到什么结果,这就是一种异常事件,只能喊操作系统内核来处理(——杀死这个程序,并给一个message说明发生了整除0异常)。
如溢出,可以编一个硬件,取出OF看是否为1,在溢出时禁止把结果写入到目的寄存器,并终止进程报个溢出错误.
辨3:内部异常与外部中断
外部中断
是发送从CPU外部来的中断请求信号通过总线送到CPU的一个引脚上面,CPU去探测这个引脚,发现这个引脚有效,说明外部有请求信号,CPU被告知外面有请求信号需要停一停,这叫做外部中断。
内部异常
:这个容易理解,是CPU内部发生的意外事件或特殊事件,分为了陷阱,故障和终止。
知4:为什么称外部中断是异步的,内部异常是同步的
1.
同步与异步
两个执行流之间有依赖关系,一方需要等待另一方执行完才能继续执行的,称为同步.
没有依赖关系就称为异步
2.why 外部中断是异步
这是因为中断处理程序和进程没有任何关系,所谓和进程没有任何关系就是指中断处理程序不会依赖进程的任何资源,换句话说,任何一个进程都可能在任何时候被中断而执行中断处理程序,而到底哪个进程被中断,什么时候被中断,具有不确定性,而且中断后,被中断的进程资源不会被中断处理程序所使用。因为中断处理程序和进程没有任何的关系,所以一旦运行起来就会一直运行下去,不会等待某些资源而暂停,所以说中断处理程序时异步运行的。
由于中断处理程序的异步性,其和进程没有任何联系,所以往往中断处理程序,既没有参数也没有返回值。
3.why内部异常是同步
这是因为内部异常是当前进程在执行完某个指令后检测到异常时,就必须终止当前进程的执行流,转而执行异常处理程序之后才能继续执行当前的进程的执行流(也就是设一个断点),或者很严重的问题就只能终止。
2.进程
程序
是一个静态概念,所有的数据和指令都以二进制的形式保存在exe文件中,这个文件就是一个程序。
进程
是执行中的程序的实例,是一个动态的概念,系统中每个程序都运行在某个进程的上下文context中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
3.系统级调用错误处理
C程序用syscall
函数可以直接调用任何系统调用。然而,实际上几乎没必要这样做,大多数都是C库提供了针对系统调用的一组方便的包装函数。这些包装函数将参数打包到一起,以适当的系统调用指令陷入内核,然后将系统调用的返回状态传递回调用程序。本书称系统调用及其包装函数都称为系统级函数
。
x86-86架构中
,syscall
的参数全部由寄存器传递:
rax
中存放系统调用号
rdi rsi rdx r10 r8 r9
一次存放1~6
参数 <故max(arg count)=6
>
例如
int main()
{
write(1,"hello,world\n",13);
_exit(0);
}
转成汇编代码为
.section .data
string:
.ascii "hello,world\n"
string_end:
.equ len,string_end - string
.section .text
.global main
main:
//First:call write(1,"hello,world\n",13)
movq $1,%rax //write is system call 1
movq $1,%rdi //Arg1:file decripter 1 means stdout标准输出流
movq $string,%rsi //Arg2:hello world string
movq $len,%rdx //Arg3:string length
syscall //Make the system call
//Next:call _exit(0)
movq $60,%rax //_exit is system call 60
movq $0,%rdi //Arg1:exit status is 0
syscall //Make the system call
当Unix系统级函数遇到错误,通常返回-1,并设置全局整数变量errno
来表示出什么错,程序员应该去检查错误。
int pid;
if ( (pid = fork()) <0)
{
fprintf(stderr, "fork error: %s\n",strerror(errno));
exit(0);
}
strerror(errno)
返回一个字符串,描述了errno指定的错误。
可以专门用一个错误报告函数来报告错误。
void unix_error(char *msg)
{
fpintf(stderr,"%s: %s\n",msg,strerror(errno));
exit(0);
}
给定这个函数,我们可以对fork的调用从4行缩减到2行
if ((pid = fork()) < 0 )
unix_error("fork error");
可以进一步使用错误处理包装函数
进一步简化代码。
pid_t Fork()
{
pid_t pid;
if ( (pid = fork() ) < 0)
unix_error("Fork error");
return pid;
}
给定这个包装函数,我们对fork的调用就缩减为1行
pid = Fork();
本书的剩余部分都使用错误处理函数,保持代码简洁且可以有允许忽而略错误检查的假象。
4.进程控制
4.1 获取进程ID
每一个进程都有唯一的正数(非0)进程ID(PID)。getpid
返回该进程的PID,getppid
返回其父进程的PID。
#include <sys/types.h>
#include <unistd.h>
// Linux中在types.h中,pid_t被定义为int
pid_t getpid(void);
pid_t getppid(void);
示例:
#include