概述
一、正常控制流
(1)按顺序取下一条指令执行
(2)通过call,ret,jmp,jcc等指令跳转到转移目标地址处执行
cpu控制流是什么?
cpu所执行的指令的地址序列。
二、异常控制流
1.分类
①内部异常(硬件层)
整除0,溢出,缺页,越级,越权
②外部中断(硬件层)
打印机缺纸,ctrl+c,DMA结束等
③进程上下文切换(操作系统层)
2.程序和进程
程序
按某种方式形成的代码和数据的集合,静态的概念。
进程
- 程序关于某个数据集合的一次运行活动。动态的含义。不同的数据不同的进程。
- 操作系统以外的都属于“用户”的任务。
- 计算机所有“任务”由进程完成。
为什么引入进程?
- 一个独立的逻辑控制流,好像独占计算机资源。
- 一个私有的虚拟地址空间,好像独占存储器。
- 简化了编程,编译,链接,共享,加载。
3.逻辑控制流
对于确定的数据集,某进程的指令执行地址序列是确定的。称为进程的逻辑控制流。
单处理器系统:轮流使用处理器,处理器的物理控制流由多个进程的逻辑控制流组成。
并发:不同的进程控制流在时间上交错或者重叠的情况。 p1和p2、p2和p3是并发执行。
4.进程的上下文切换
问题:
hello程序何时被装入的?谁来装入?被谁启动?每次装到相同的地方?hello是否知道还有其他程序同时运行?
①进程的上下文切换
OS通过处理器调度让处理器轮流执行多个进程,实现不同进程中指令交替执行的机制。
处理器的调度等事件会形成异常控制流,上下文切换实现了安全转换。
②进程的上下文
进程的物理实体(代码和数据),和支持进程运行的环境。
现场信息:寄存器内容。 (寄存器上下文、硬件上下文)
③进程的地址空间
5.进程的存储器映射
每个进程的虚拟地址空间相同。
OS要管理进程,如何描述一个进程的地址空间?
①存储器映射:虚拟空间中一个区域,与硬盘上一个对象建立关联(生成页表项),并初始化一个vm_area_struct结构信息。
mmap函数实现存储器映射:
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset);
如何获得实参?读取可执行文件中的程序头表。
可执行文件到主存不能直接映射。
- 磁盘上:可执行文件中的程序头表,描述了可执行文件区域到虚拟地址空间的映射信息。
- 装入系统执行,生成进程:通过进程描述符中的链表,建立虚拟地址空间的区域,与可执行文件区域的关联。
- 页表:描述了虚拟地址空间的page到主存page frame之间的映射。
6.共享对象和私有的写时拷贝对象
①可用mmap实现存储器映射
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset);
功能:将指定文件fd中偏移量offset开始的长度为length个字节的一块信息,映射到虚拟空间中起始地址为start、长度为length个字节的一块区域,得到vm_area_struct结构的信息,并初始化相应页表项(P=0) ,建立文件地址和虚存区域之间的映射关系。
进程运行时,第一次访问这些页面时,会发生缺页(P=0),由缺页处理程序进行文件读写(P=1)。
prot指定该区域内页面的访问权限位,对应vm_area_struct结构中的vm_prot字段
PROT_EXE:页面内容由指令组成
PROT_READ:区域内页面可读
PROT_WRITE:区域内页面可写
PROT_NONE:区域内页面不能被访问
flags指定所映射的对象的类型,对应vm_area_struct结构中的vm_flags字段
MAP_PRIVATE :私有对象,采用写时拷贝技术,对应可执行文件中只读代码区域(.init、 .text、 .rodata )和已初始化数据区域( .data )
MAP_SHARED :共享对象,对应共享库文件中的信息
MAP_ANON :请求0的页,对应内核创建的匿名文件,相应页框用0初始化并驻留内存
MAP_PRIVATE | MAP_ ANON :未初始化数据(.bss).堆和用户栈等对应区域
②Linux虚拟地址空间中的区域
③共享对象
所有的进程都可以访问的代码和数据。
进程1运行,缺页,内核为进程1分配若干页框。
进程2运行时,缺页,发现这个磁盘中该区域已经在主存分配过页框,于是找到进程1页表项,将页框号填到进程2的页框号处。
即一个进程对共享区域的写操作对其他共享这个区域的进程都可见。
④私有对象的写时拷贝对象。
- 一个副本,多个进程使用,发生写的时候才拷贝。
- 为什么采用写时拷贝?节省主存。 同一个可执行文件的不同进程,只读代码区一样,可读可写数据区开始时也一样,但是私有对象。
- 进程1运行,缺页,内核为进程1分配若干页框。
- 进程2运行时,缺页,发现这个磁盘中该区域已经在主存分配过页框,于是找到进程1页表项,将页框号填到进程2的页框号处,标记为只读。
- 如果都只是只读或者执行,则只有一个副本。
- 若进程2写操作,发生访问违例,内核判断异常原因是进程试图写私有的写诗拷贝页,就分配一个新页框,吧内容拷贝过去,并修改。
7.异常、中断和输入/输出
OS管理程序执行,有时候处理器执行内核代码。用一个模式位表示用户代码和内核代码。
处理器分为用户模式(用户态)和内核模式(核心态)
- 用户模式(目态,用户态):处理器运行用户进程,不允许使用特权指令
- 内核模式(系统模式,管理模式,超级用户模式,管态,内核态,核心态):允许使用特权指令(停机,开/关中断,cache flush等)
8.程序的加载和运行
- UNIX/Linux系统中可调用exeve()启动加载器 。
- 功能:在当前进程上下文中加载并运行一个新程序。
- 用法:int execve(char *filename, char *argv[],*envp[]); filename加载运行的程序名,argv参数列表,evnp环境变量列表。错误,返回-1,控制权交给调用程序;成功,不返回,最终控制权交传递到可执行目标中main。
- int main(int argc, char **argv, char **envp);或者int main(int argc, char *argv[], char *envp[]);argc参数个数,参数列表中第一个总是命令名(可执行文件名)
shell命令提示符下输入:Unix>ld -o test main.o test.o
①$./hello[enter]
②shell命令行构建了argv和envp
③调用fork()创建一个子进程,与父进程shell完全相同(只读/共享)
④调用execve()函数,在当前进程上下文中加载并运行hello,将hello中的,text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间(仅修改当前进程上下文中关于存储映像的一些数据结构,如页表,不从此盘拷贝代码和数据等内容)
⑤调用hello的main,hello开始在一个进程的上下文中运行。
- 通过调用execve调用加载器
- loader根据可执行文件中的程序头表信息,将可执行文件的代码和数据从磁盘“拷贝”到存储器中(实际不真正拷贝,仅建立一种映像)
- 加载后,将PC指向Entry point(即_start处),最终执行main函数,以启动程序执行
最后
以上就是俏皮鞋子为你收集整理的异常控制流一(进程与进程的上下文切换)的全部内容,希望文章能够帮你解决异常控制流一(进程与进程的上下文切换)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复