0%

操作系统


进程和线程有什么区别?

  • 进程(Process)是系统进行资源分配和调度的基本单位,线程(Thread)是 CPU 调度和分配的基本单位;
  • 线程依赖于进程而存在,一个进程至少有一个线程;
  • 进程有自己的独立地址空间,线程共享所属进程的地址空间;
  • 进程是拥有系统资源的一个独立单位,而线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),和其他线程共享本进程的相关资源如内存、I/O、CPU 等;
  • 在进程切换时,涉及到整个当前进程 CPU 环境的保存环境的设置以及新被调度运行的 CPU 环境的设置,而线程切换只需保存和设置少量的寄存器的内容,并不涉及存储器管理方面的操作,可见,进程切换的开销远大于线程切换的开销;
  • 线程之间的通信更方便,同一进程下的线程共享全局变量等数据,而进程之间的通信需要以进程间通信(IPC)的方式进行;
  • 多线程程序只要有一个线程崩溃,整个程序就崩溃了,但多进程程序中一个进程崩溃并不会对其它进程造成影响,因为进程有自己的独立地址空间,因此多进程更加健壮。
同一进程中的线程可以共享哪些数据?
展开
  • 进程代码段
  • 进程的共有数据(全局变量、静态变量…)
  • 进程打开的文件描述符
  • 进程的当前目录
  • 信号处理器/信号处理函数:对收到的信号的处理方式
  • 进程的 ID 与进程组 ID
线程独占哪些资源?
展开
  • 线程 ID
  • 一组寄存器的值
  • 线程自身的栈(堆是共享的)
  • 错误返回码:线程可能会产生不同的错误返回码,一个线程的错误返回码不应该被其它线程修改;
  • 信号掩码/信号屏蔽字(Signal mask):表示是否屏蔽/阻塞相应的信号(SIGKILL,SIGSTOP 除外)

进程间通信有哪些方式?

  1. 管道(Pipe)
展开
  • 半双工通信
    • 管道是半双工的,数据只能在一个方向上流动,即数据从一端写入,从另一端读出。
    • 如果需要双向通信,则必须建立两个管道,分别用于两个方向的数据传输。
  • 数据传输机制
    • 一个进程将数据写入管道的写端,数据会被添加到管道缓冲区的末尾。
    • 另一个进程从管道的读端读取数据,每次读取时,数据是从缓冲区的头部取出。
    • 这意味着管道的读取时先进先出的,确保数据的顺序性。
  • 使用场景
    • 管道只能用于具有亲缘关系的进程之间进行通信,如父子进程或兄弟进程,它们共享相同的文件描述符表
  1. 命名管道
  2. 消息队列
  3. 信号(Signal)
  4. 共享内存
  5. 信号量(Semaphore):初始化操作、P 操作、V 操作;
    1. P 操作:信号量 - 1,检测是否小于 0,小于则进程进入阻塞状态;
    2. V 操作:信号量 + 1,若小于等于 0,则从队列中唤醒一个等待的进程进入就绪态
  6. 套接字(Socket)

进程同步问题

进程的同步是目的,而进程间通信是实现进程同步的手段

管程 Monitor
展开

管程将共享变量以及对这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,这样只能通过管程提供的某个过程才能访问管程中的资源。进程只能互斥地使用管程,使用完之后必须释放管程并唤醒入口等待队列中的进程。

  1. HOARE 管程:当一个进程试图进入管程时,在入口等待队列等待。若 P 进程唤醒了 Q 进程,则 Q 进程先执行,P 在紧急等待队列中等待。
    1. wait 操作:执行 wait 操作的进程进入条件变量链末尾,唤醒紧急等待队列或者入口队列中的进程;
    2. signal 唤醒条件变量链中的进程,自己进入紧急等待队列,若条件变量链为空,则继续执行。
  2. MESA 管道:将 HOARE 中的 signal 换成了 notify (或者 broadcast 通知所有满足条件的),进行通知而不是立马交换管程的使用权,在适合的时候,条件队列首位的进程可以进入,进入之前必须用 while 检查条件是否合适。优点:没有额外的进程切换

生产者-消费者问题

展开

问题描述:使用一个缓冲区来存放数据,只有缓冲区没有满,生产者才可以写入数据;只有缓冲区不为空,消费者才可以读出数据

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 伪代码描述 
// 定义信号量 full记录缓冲区物品数量 empty代表缓冲区空位数量 mutex为互斥量
semaphore full = 0, empty = n, mutex = 1;

// 生产者进程
void producer(){
do{
P(empty);
P(mutex);

// 生产者进行生产

V(mutex);
V(full);
} while(1);
}

void consumer(){
do{
P(full);
P(mutex);

// 消费者进行消费

V(mutex);
V(empty);
} while(1);
}
哲学家就餐问题
展开

问题描述:有五位哲学家围绕着餐桌坐,每一位哲学家要么思考,要么吃饭。为了吃饭,哲学家必须拿起两双筷子(分别放于左右两端)。不幸的是,筷子的数量和哲学家相等,所以每只筷子必须由两位哲学家共享。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#define N 5  // number of philosopher
#define LEFT (i + N - 1)%N // number of i's left neighbors
#define RIGHT (i + 1)%N // number of i's right neighbors
#define THINKING 0
#define HUNGRY 1
#define EATING 2
typedef int semaphore;
int state[N]; // array to keep track of everyone's state
semaphore mutex = 1; // mutual exclusion of critical region
semaphore s[N];

void philosopher(int i) {
while (TRUE) {
think();
take_forks(i);
eat();
put_forks(i);
}
}

void take_forks(int i) {
down(&mutex); // enter critical region
state[i] = HUNGRY; // record that i is hungry
test_forks(i); // try to acquire two forks
up(&mutex); // exit critical region
down(&s[i]); // block if forks are not acquired
}

void put_forks(int i) {
down(&mutex); // enter critical region
state[i] = THINKING; // record that has finished eating
test_forks(LEFT); // see if left neighbor can now eat
test_forks(RIGHT); // see if right neighbor can now eat
up(&mutex); // exit critical region
}

void test_forks(int i) {
if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
state[i] = EATING;
up(&s[i]);
}
}
临界区的概念?
展开

各个进程中对临界资源(互斥资源/共享变量,一次只能给一个进程使用)进行操作的程序片段

同步与互斥的概念?
展开
  • 同步:多个进程因为合作而使得进程的执行有一定的先后顺序。比如某个进程需要另一个进程提供的消息,获得消息之前进入阻塞态;
  • 互斥:多个进程在同一时刻只有一个进程能进入临界区
并发、并行、异步的区别?
展开
  • 并发:在一个时间段中同时有多个程序在运行,但其实任一时刻,只有一个程序在 CPU 上运行,宏观上的并发是通过不断地切换实现的。
  • 多线程:并发运行的一段代码,是实现异步的手段。
  • 并行(和串行相比):在多 CPU 系统中,多个程序无论在宏观还是微观上都是同时执行的。
  • 异步(和同步相比):同步是顺序执行,异步是在等待某个资源的时候继续做自己的事。

进程有哪几种状态?

  • 就绪状态:进程已获得除处理以外的所需资源,等待分配处理机资源
  • 运行状态:占用处理机资源运行,处于此状态的进程数小于等于 CPU 数
  • 阻塞状态:进程等待某种条件,在条件满足之前无法执行。
-------------本文结束感谢您的阅读-------------