目录
1.静态库和动态库如何制作及使用,区别是什么
2.说一说进程调度算法有哪些?
3. 什么是虚拟继承?他是如何解决菱形继承数据冗余和二义性问题的?
4. 什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程
5.说一说进程通信的方式有哪些?
6.说一说进程有多少种状态,如何转换
1.静态库和动态库如何制作及使用,区别是什么
静态库和动态库是用于代码重用和共享的两种常见的库文件格式。
静态库是一组预编译的目标文件的归档文件,其中包含了函数和数据的实现。制作静态库需要先将源代码编译成目标文件,然后使用静态库管理工具(如ar)将这些目标文件打包成一个归档文件(通常以.a为后缀)。使用静态库时,链接器会将整个静态库的代码和数据复制到最终可执行文件中,因此最终可执行文件会变得较大。每次使用静态库时,都会将其完整复制到可执行文件中,因此多个可执行文件共享同一个静态库时会造成冗余。
动态库是在运行时加载的共享目标文件,其中包含了函数和数据的实现。制作动态库需要将源代码编译成共享目标文件(通常以.so为后缀),并生成一个描述该动态库的链接器脚本。使用动态库时,链接器不会将整个动态库的代码和数据复制到最终可执行文件中,而只是在运行时动态加载所需函数和数据。这样可以减小最终可执行文件的大小,并且多个可执行文件可以共享同一个动态库,减少冗余。
区别:
1. 静态库在链接时被完整地复制到最终可执行文件中,而动态库在运行时被动态加载。
2. 静态库使得最终可执行文件变大,而动态库使得最终可执行文件变小。
3. 静态库每次使用都会复制到可执行文件中,可能造成冗余,而动态库可以被多个可执行文件共享,减少冗余。
2.说一说进程调度算法有哪些?
1. 先来先服务(FCFS)调度算法 先来先去服务调度算法是一种最简单的调度算法,也称为先进先出或严格排队方案。每次调度都是从后备作业(进程)队列中选择一个或多个最先进入该队列的作业(进程),将它们调入内存,为它们分配资源、创建进程,当每个进程就绪后,它加入就绪队列。当前正运行的进程停止执行,选择在就绪队列中存在时间最长的进程运行。
2. 短作业优先(SJF)调度算法 短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业(进程),将它们调入内存运行,短进程优先(SPF)调度算法从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给它,使之立即执行,直到完成或者发生某件事而阻塞时,才释放处理机。
3. 优先级调度算法 优先级调度算法又称优先权调度算法,该算法既可以用于作业调度,也可以用于进程调度,该算法中的优先级用于描述作业运行的紧迫程度。在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最髙的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列;在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它,使之投入运行。 4. 高响应比优先调度算法 高响应比优先调度算法主要用于作业调度,该算法是对 FCFS 调度算法和 SJF 调度算法的一种综合平衡,同时考虑每个作业的等待时间和估计的运行时间。在每次进行作业调度时,先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业投入运行。
5. 时间片轮转调度算法 时间片轮转调度算法主要适用于分时系统。每次调度时,把 CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几 ms 到几百 ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。
6. 多级反馈队列调度算法 多级反馈队列调度算法是时间片轮转调度算法和优先级调度算法的综合和发展,通过动态调整进程优先级和时间片大小,多级反馈队列调度算法可以兼顾多方面的系统目标
3. 什么是虚拟继承?他是如何解决菱形继承数据冗余和二义性问题的?
虚拟继承是C++中用于解决多重继承中数据冗余和二义性问题的一种机制。当一个类从多个基类派生,并且这些基类之间有共同的基类时,虚拟继承可以确保共同基类只被派生类继承一次。
通过使用虚拟继承,可以避免在派生类中存在多个相同的共同基类子对象。相反,虚拟继承通过创建一个共享基类子对象来解决数据冗余的问题。这样,无论派生类的层次结构中有多少个共同基类,共享基类子对象只会出现一次。
此外,虚拟继承还可以解决多个基类之间的二义性问题。当一个类从多个基类派生,并且这些基类拥有相同的成员函数或变量时,编译器无法确定应该使用哪个基类的成员。虚拟继承通过在派生类中创建一个虚拟基类子对象来解决这个问题。这样,在调用共同成员时,编译器可以通过虚拟基类子对象进行访问。
以下是示例代码,演示了如何使用虚拟继承解决数据冗余和二义性问题:
#include <iostream>class Base { public:int x; };class Derived1 : virtual public Base { public:int getX() {return x;} };class Derived2 : virtual public Base { public:void setX(int value) {x = value;} };class Derived3 : public Derived1, public Derived2 { public:void printX() {std::cout << "x = " << getX() << std::endl;} };int main() {Derived3 d;d.setX(5);d.printX();return 0; }
输出结果为:
x = 5
在这个例子中,Base 类作为一个虚拟基类被 Derived1 和 Derived2 虚拟继承。Derived3 类通过 Derieved1 和 Derived2 类进行派生,并且可以直接访问 Base 类的成员 x,而不会发生数据冗余和二义性问题。
4. 什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程
孤儿进程是指在父进程结束后,子进程仍然在运行,但失去了父进程的依赖和控制。这种情况下,孤儿进程会被操作系统的init进程接管,并成为init进程的子进程。
僵尸进程是指一个子进程在结束时,其父进程没有及时处理子进程的终止状态,导致子进程的资源(如进程表项等)无法释放,成为僵尸进程。僵尸进程会占用系统的进程表项,当僵尸进程过多时,可能导致系统进程表项耗尽。
解决僵尸进程的一种常见方法是通过父进程调用wait或waitpid函数来等待子进程的终止,并获取子进程的终止状态。当父进程调用wait或waitpid时,操作系统会回收僵尸进程的资源。另一种方法是使用信号机制,在父进程中捕获SIGCHLD信号,并在信号处理函数中调用waitpid函数来回收子进程资源。
5.说一说进程通信的方式有哪些?
进程通信是指不同进程之间进行信息交流和数据共享的过程。常见的进程通信方式有以下几种:
1. 管道(Pipe):管道是一种半双工的通信方式,适用于具有亲缘关系的进程之间的通信。管道可以是匿名管道(只能在具有共同祖先的进程之间使用)或有名管道(可在不具有亲缘关系的进程之间使用)。
2. 信号(Signal):信号是一种软件中断,用于通知进程发生了某个事件。进程可以通过注册信号处理函数来捕捉和处理信号。
3. 消息队列(Message Queue):消息队列是一种存放在内核中的消息链表,用于不同进程之间的通信。每个消息都有一个标识符和一个类型,接收进程可以根据标识符和类型选择性地接收消息。
4. 共享内存(Shared Memory):共享内存是指多个进程共享一块公共内存区域。进程可以直接访问该内存区域,实现数据共享。
5. 套接字(Socket):套接字是一种网络通信机制,可以在不同主机之间进行进程通信。套接字提供了一组网络API,用于建立、连接、发送和接收数据等操作。
6. 信号量(Semaphore):信号量是一种特殊的变量,用于控制对共享资源的访问。进程可以使用信号量来实现进程间的同步和互斥。
7. 文件(File):进程可以通过读写文件来实现数据的共享和传输。可以使用文件锁机制来保证多个进程对文件的互斥访问。
6.说一说进程有多少种状态,如何转换
进程有五种基本状态,分别是创建(Create)、就绪(Ready)、运行(Running)、阻塞(Blocked)和终止(Terminated)。
进程的状态转换如下:
1. 创建(Create):进程被创建并分配资源。
2. 就绪(Ready):进程已经准备好执行,但还未被调度。
3. 运行(Running):进程正在CPU上执行。
4. 阻塞(Blocked):进程由于某些原因暂时无法执行,如等待输入/输出完成或等待某个事件发生。
5. 终止(Terminated):进程执行完成或被操作系统终止。进程在不同状态之间的转换如下:
1. 创建(Create)-> 就绪(Ready):进程被创建并准备好执行。
2. 就绪(Ready)-> 运行(Running):进程被调度并开始在CPU上执行。
3. 运行(Running)-> 阻塞(Blocked):进程遇到了某个阻塞事件,无法继续执行。
4. 运行(Running)-> 就绪(Ready):进程的时间片用完,需要调度其他进程执行。
5. 阻塞(Blocked)-> 就绪(Ready):阻塞事件解除,进程可以继续执行。
6. 运行(Running)-> 终止(Terminated):进程执行完成,或者被操作系统终止。