进程
1 解释操作系统中的作业、进程、线程、管程各自定义。
作业:用户在一次解题或一个事物处理过程中要求计算机系统所做工作的集合。作业是一系列有序步骤组成的。
进程:一个程序在一个数据集合上的一次运行过程。所以一个程序在不同数据集合上运行,乃至一个程序在同样数据集合上的多次运行都是不同的进程。
线程:线程是进程的一个实体,是被系统独立调度和执行的基本单位。
管程:定义了一个数据结构和在该数据结构上的能为并发进程所执行的一组操作,这组操作能同步进程或改变管程中的数据。
2 进程间的通信如何实现?
常用进程间通信方式:信号、信号量、消息队列、共享内存。
进程通信,就是不同进程之间进行一些“接触”。
信号与信号量是不同的,虽然都可用来实现同步和互斥,但前者是使用信号处理器来进行的,后者是使用P、V操作实现。
消息队列是一个高级的进程间通信方法,因为它可以在进程间传送message。一个消息队列可以被多个进程所共享;如果一个进程的消息太多,可以用多余一个的消息队列。
3 windows中互斥器mutex和临界区critical section的区别?
分析:多线程问题。 互斥器可以用于进程之间互斥,临界区是线程之间互斥。
4 短作业优先=最短剩余时间作业优先。
可剥夺式调度,又称抢先式调度。当进程/线程正在处理器上运行时,系统根据所规定的规则剥夺分配给进程/线程的处理器,并将其移入到就绪队列,选择其他进程/线程运行。
非剥夺式调度,调度程序一旦把处理机分配给某进程后,便一直让它执行。直至该进程完成或因发生某事件而阻塞时,才对处理机分配给另一进程。
5 多重处理系统中,处理死锁的方法有两种:一是防止其发生;二是发生后进行处理。下述方法可以防止其发生的()
A 破坏互斥条件 B 破坏不可剥夺条件C 破坏循环等待条件D 杀死某个激活死锁的进程(发生后处理的)
分析:死锁deadlocks是指两个或两个以上的进程在执行过程中因争夺资源而造成的一种互相等待的现象。若无外力作用,它们都将无法进行下去。此时为死锁状态或系统产生死锁。
产生死锁的4个必要条件:互斥条件、请求与保持条件、不剥夺条件、循环等待条件。只要系统发生死锁,这些条件必然成立,只要有一个条件不满足,就不会发生死锁。
可以采取3种预防措施:
(1)采用资源静态分配策略,破坏部分分配条件、
(2)允许进程剥夺使用其他进程占有资源,从而破坏不可剥夺条件。
(3)采用资源有序分配法,破坏环路条件。
注意,互斥条件无法被破坏。
避免死锁算法中最有代表性的是银行家算法,该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,从而保证系统中所有进程都能完成,所以可避免死锁的发生。
6 在Linux平台下运行C程序,假设fork()函数不失败,下列正确的是(A)
int main() {
int i=1;
if(!fork()) i++;
printf("%d\n",i);
if(!fork()) i++;
printf("%d\n",i);
return 0;
}
A 1只出现2次 B 2只出现2次C 3总会是最会一个输出D不可能出现3
分析:B错,2会出现3次;C不一定,因为父子进程谁先运行不一定;D中3会出现
分析:fork()函数创建进程,从此处分成2个进程,一个父进程,一个子进程。子进程复制了父进程的绝大部分:栈、缓冲区等。系统为子进程创建一个新的进程表项,其中id与父进程是不同的,也就是说父子进程是两个独立进程,虽然父子进程共享代码空间,但是在涉及写数据时子进程有自己的数据空间,在有数据修改时,系统会为子进程申请新的页面。
假设一开始是进程A,第一次fork,A fork之后产生子进程B,第一个printf,进程A输出1,进程B输出2;
第二次fork,A fork了子进程C,B fork了子进程D,第二个printf,进程A输出1,进程B输出2,进程C输出2,进程D输出3;
A-+---1-+--1
| |-C-2
|
|-B-2-+--2
|-D--3
7 描述进程的三种基本状态。
进程在运行中不断改变其运行状态。通常一个进程必须具备三种基本状态。
(1)就绪状态,当进程已分配到除CPU以外的所有必要资源,只要获得处理机便可立即执行。
(2)执行状态,当进程已获得处理机,其程序正在处理机上执行,此时进程状态称为执行状态。
(3)阻塞状态,正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。(引起阻塞状态的事件:等待I/O完成,申请缓冲区不能满足,等待信件)。
8 若系统中有5台绘图仪,有多个进程均需要使用两台,规定每个进程一次仅允许申请一台,则至多允许(4)个进程参与竞争,而不发生死锁。
A 2 B 3 C 4 D 5
解析:哲学家就餐问题,资源按照某种规则编号 为1~5,每一个工作单元总是先拿起左右两边编号较低的餐叉,再拿编号较高的,用完餐,总是先放下编号较高的餐叉,再放下编号较低的。在这种情况下,当4位哲学家同时拿起他们手边编号较低的餐叉时,只有编号最高的餐叉留在桌子上,从而第5位哲学家就不能使用任何一只餐叉。
线程
1 描述进程和线程的差别
进程是程序的一次执行。线程可以理解为进程中执行的一段程序片段。
进程间是独立的,这表现在内存空间、上下文环境上; 线程运行在进程空间内。 一般来讲,进程无法突破进程边界存取其他进程内的存储空间;而线程由于处于进程空间内,所以同一进程所产生的线程共享同一内存空间。
同一进程中的两端代码不能同时执行,除非引入线程。
线程是属于进程的,当进程退出时该进程所产生的线程都会被强制退出并清除。线程占用的资源要少于进程所占用的资源。进程和线程都可以有优先级。
进程间可以通过IPC通信,而线程不可以。
2 下面哪个选项不是PE文件?
A、EXE B、DLLC、COMD、DOC
PE文件被称为可移植的执行体是Portable Execute的全称,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件。
3 Windows遵循下面的哪种搜索来定位DDL?
包含EXE文件的目录->进程当前工作目录->Windows系统目录->Windlws目录->列在Path环境变量中的一系列目录、
Windows平台的大多数程序都使用各种动态链接库DDL来避免重复实现功能。操作系统为每个程序加载若干个DLL,具体由程序的类型决定。当程序不指定DLL的绝对位置时,将使用默认的搜素顺序来找到它。默认情况下,操作系统所使用的搜索顺序为:
内存-KnownDLLs-清单与.local-应用程序目录-当前工作目标-系统目录-路径变量
4 动态链接库 VS 静态链接库
目前lib为后缀的库有动态链接库和 静态链接库。静态库本身就包含了实际执行代码、地址符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本信息。
静态链接库是一个或者多个obg文件的打包,在使用静态链接库时,静态链接库需要参与编译。在执行文件生成以后,静态链接库lib文件可以弃之不用。
动态链接库dll是作为共享函数库的可执行文件。动态链接库提供一种方法,使进程可以调用不属于其可执行代码的函数。dll节省内存、减少交换操作、节省磁盘、易于升级,提供售后服务,提供扩展MFC苦累机制、支持多语言程序。
区别:
(1)静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,lib中的指令都全部被直接包含在最终生成的exe文件中。但是用dll,该dll不必被包含在最终exe文件中,exe文件执行时可以动态地引用和卸载中与exe独立的dll文件。
(2)静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。动态链接库允许可执行模块(dll或者exe文件)仅包含在运行时定位dll函数的可执行代码所需的信息。在静态链接库使用中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。
5. 假定有3个程序,每个程序花费80%的时间进行I/O,20%时间使用CPU,每个程序的启动时间和其需要使用CPU进行计算机的分支数如表所示。请问在多线程/进程环境下,系统总响应时间为多少?
程序 启动时间 需要CPU时间
1 | 00::00 | 3.5 |
2 | 00:10 | 2 |
3 | 00:15 | 1.5 |
分析:0~10分钟,10*0.8=8,10*0.2=2。A还剩下1.5分钟CPU需要跑。
10~15分钟,有2个进程,CPU利用率1-0.8^2=0.36.所以(15-10)*0.36=1.8分钟,1.8/2=0.9(两个进程均分CPU时间)这样A剩下1.5-0.9=0.6分钟,B剩下2-0.9=1.1分钟。
15分钟开始后,3个进程的利用率1-0.8^3=0.488.所以A完成所需要的0.6分钟,则0.6*3、0.488=3.69后也就是15+3.69=18.69分完成,之后的CPU利用率又变成0.36,此时B剩下1.1-0.6=0.5分钟,C剩下1.5-0.6=0.9分钟,B在0.5*2/0.36=2.78也就是18.69+2.78=21.46分钟的时候B进程结束,之后C开始单跑0.9-0.5=0.4分钟。0.4/0.2=2分钟,即2分钟后C结束,也就是21.46+2=23.46=23.5分钟之后,CPU跑完3个程序。
6. 进程是由两部分组成,一个是进程内核对象,另一个是地址空间。
线程是由两部分组成的,一个是线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。另一个是线程堆栈,用于维护线程在执行代码时需要的所有函数参数和局部变量。
进程是不活泼的。进程从来不执行任何东西,只是线程的容器。线程总是在某个进程环境中创建,而且整个寿命期都在该进程中。这意味线程在它的进程地址空间中执行代码,并且在进程的地址空间中对数据进行操作。
进程使用的系统资源币线程多,原因是它需要更多的地址空间。为进程创建一个虚拟地址空间需要许多系统资源。系统中要保持大量的记录,这要占用大量的内存。由于线程需要的开销比进程少,因此一般用增加线程来解决编程问题,而要避免创建新的进程。
每当进程被初始化时,系统就要创建一个主线程。该线程与C/C++运行期库的启动代码一道运行,启动代码则调用进入点函数,并且继续运行直到进入点函数返回并且C/C++运行期库的启动代码调用退出为止。
每个线程必须拥有一个进入点函数,线程从这个进入点开始运行。即main、wmain、WinMain、wWin Main。
线程函数可以执行你想要它做的任何任务。最终,线程函数到达它的结尾处并且返回。这时线程终止运行,该堆栈的内存被释放,同时,线程的内核对象的使用计算被递减。如果使用计算降为0,线程的内核对象就被撤销。与进程内核对象的情况相同,线程内核对象的寿命至少可以达到它们相关联的线程那样长,不过,该对象的寿命可以远远超过线程本身的寿命。
内存管理
1 简述windows内存管理的几种方式和优缺点。
页式管理:基本原理是将各进程的虚拟空间划分为若干个长度相等的页;页式管理把内存空间按页的大小划分为片或者页面,然后把页式虚拟地址与内存地址建立一 一对应的页表;并用相应的硬件地址变换机构来解决地址变换问题。
采用请求调页或预调页技术来实现内外存存储器的统一管理。
优点:没有外碎片,每个内碎片不超过页的大小。缺点:程序全部装入内存,要求有相应的硬件支持。
段式管理 :把程序按内容或过程函数关系分成段,每段有自己的名字。一个用户作业或进程所包含的段对应一个二维线形虚拟空间,也就是一个二维虚拟存储器。
段式管理程序以段为单位分配内存,然后通过地址影射机构把段式虚拟地址转换为实际内存物理地址。
优点:分别编写和编译,可以针对不同类型的段采取不同的保护,可以按段为单位来进行共享,包括通过动态链接进行代码共享。缺点是产生碎片。
段页式管理: 是段式管理和页式管理方案结合而成的,具有二者的优点,同时复杂性和开销也就增加。
2 X86和X64的区别
X86指Intel制造的普通CPU(个人电脑以32位Intel的CPU为主),X64是X86_64的缩写,指X86基础上的改进版(加入64位地址扩展等性能),纯64位计算机架构用IA64表示,32位兼容的64位架构用amd64表示(AMD是这一架构的主要生产商)。
3 Belady's Anomaly出现在哪?
A、 内存管理算法 B 、内存换页算法C、预防死锁算法D、磁盘调度算法
分析:所谓Belady现象是指采用FIFO算法时,如果对一个进程未分配它所要求的全部页面,有时就会出现分配的页面数增多但缺页率反而提高的异常现象。原因是FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,即被置换的页面并不是进程不会访问的。这些页在FIFO算法下被反复调入和调出,并且有Belady现象。
4 什么是Thrashing?
A、非常频繁的换页活动 B、非常高的CPU执行活动C、一个极长的执行过程D、一个极大的虚拟内存法
分析:内存抖动Thrashing一般是内存分配算法不好,内存太小或者程序的算法不佳引起的页面频繁地从内存调入/调出的行为。
5 避免死锁的一个著名算法是: 银行家算法。(该算法将系统的状态分为安全状态和不安全状态,只要使系统处于安全状态,便可避免死锁发生)
6 虚拟存储器的基本思想:程序、数据、堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。
《程序员面试宝典》 第五版 欧立奇 P276~P289