os生产者-消费者/读者-写者问题

文章发布时间:

最后更新时间:

页面浏览: 加载中...

进程同步

并发性带来了异步性,有时候需要用进程同步来解决异步性。
有的进程之间需要相互配合完成工作,各进程的工作推进需要遵循一定的先后顺序。

信号量机制实现同步

信号量机制是一种用于进程同步的机制,它可以用来控制进程的执行顺序。
以下是实现code4需要code2完成的信号量机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
semaphore s = 1;
P1()
{
code1;
code2;
V(s);
code3;
}

P2()
{
P(s);
code4;
code5;
}

管程机制实现同步

管程机制是一种用于进程同步的机制,它可以用来控制进程的执行顺序。管程有点类似C语言中封装的思想,将多个进程的共享数据和操作封装在一个管程中,然后通过调用管程中的方法来实现进程的同步。管程中的方法只能同时被一个进程调用,其他进程只能等待。这个是硬件上实现的,程序员不需要考虑。
以下是实现code4需要code2完成的管程机制:

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
class monitor
{
condition c;
void code2()
{
code2;
c.signal();
}
void code4()
{
c.wait();
code4;
}
}
monitor M
{
condition c;
code1;
code2;
c.signal();
code3;
}
P1()
{
M.code1;
M.code2;
M.c.signal();
M.code3;
}
P2()
{
M.c.wait();
M.code4;
M.code5;
}

进程互斥

进程互斥是指多个进程在同一时刻只能有一个进程执行,其他进程必须等待。
互斥代码分为四个部分:

  1. 进入区:检查是否可以进入临界区,若可进入,需要“上锁”
  2. 临界区:进程执行的代码(访问临界资源的代码)
  3. 退出区:将进程从临界区中移除,“解锁”
  4. 剩余区:进程剩余的代码。

进程互斥需要遵守的原则

空闲让进:临界区空闲,应允许一个进程访问。
忙则等待:临界区被访问时,其他试图访问的进程需要等待。
有限等待:需要在有限的时间内进入临界区,保证不会饥饿。
让权等待:进程不能进入临界区时,应立即释放处理器,避免忙等待。

信号量机制实现互斥

信号量机制是一种用于进程互斥的机制,它可以用来控制进程的执行顺序。
以下是实现code1互斥:

1
2
3
4
5
6
7
semaphore mutex = 1;
P1()
{
P(mutex);
code1;
V(mutex);
}

其中P操作(wait)时S.value–,V操作(signal)时S.value++。

管程机制实现互斥

管程机制是一种用于进程互斥的机制,它可以用来控制进程的执行顺序。
以下是实现code1互斥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class monitor
{
condition c;
void code1()
{
c.wait();
code1;
c.signal();
}
}
monitor M
{
condition c;
code1;
c.signal();
}
P1()
{
M.c.wait();
M.code1;
M.c.signal();
}

生产者-消费者问题

生产者生产数据,消费者消费数据。
生产者-消费者问题是一个经典的进程同步问题,它可以用来解决多进程之间的同步问题。

生产者 v —–full—-> p 消费者
生产者 p <—-empty—- v 消费者

互斥的P操作先执行会导致死锁,所以先执行同步的P操作。

代码:

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
semaphore mutex = 1; // 互斥信号量
semaphore empty = n; // 缓冲区大小
semaphore full = 0; // 缓冲区中产品数量

Producer()
{
while (true)
{
produce an item in nextp;//生产一个产品
P(empty);
if(empty == 0)
{
// 生产者阻塞
}
P(mutex);// 加锁
add the item to buffer;//将产品放入缓冲区(互斥访问缓冲区)
V(mutex);// 解锁
V(full);
}
}

Consumer()
{
while (true)
{
P(full);// 和生产者同步
if(full < 0)
{
// 消费者阻塞
}
P(mutex);// 加锁
remove an item from buffer; //从缓冲区取出一个产品(互斥访问缓冲区)
V(mutex);// 解锁
V(empty);

consume the item; //消费产品
}
}

读者-写者问题

读者-写者问题是一个经典的进程同步问题,它可以用来解决多进程之间的同步问题。
读-读 不互斥
写-其他 互斥
代码:

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
semaphore rw = 1; // 对文件互斥访问
int readcount = 0; // 读进程数量
semaphore mutex = 1; // 互斥信号量
semaphore w = 1; // 实现“写优先”(先来先服务)

Writer()
{
P(w); // 写优先
P(rw); // 对文件互斥访问
Write();
V(rw);
V(w);
}

Reader()
{
while(1)
{
P(w); // 写优先,此时写进程阻塞
P(mutex);
if(readcount == 0)
P(rw); // 对文件互斥访问
readcount++;
V(mutex);
V(w); // 写优先,此时写进程等待,先来先服务,之后来的读进程要等待写进程执行完
Read();
P(mutex);
readcount--;
if(readcount == 0)
V(rw); // 对文件互斥访问
V(mutex);
}
}
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
43
44
45
46
47
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.h>
void func_A(){//观察cpu及内核信息
FILE *cpuinfo, *version;
cpuinfo = fopen(“/proc/cpuinfo”, “r”);
version = fopen(“/proc/version”, “r”);

}
void func_B()
{

} //系统最近一次启动以来经历的时间和所创建的进程
//数、三个状态的时间花费 uptime & stat
void func_C()
{

} //系统内存使用情况、平均负载 meminfo&loadavg

void main(int argc, char *argv[])
{
char c1,c2;
if(argc==1) //观察cpu及内核信息
{
func_A();
return;
}
if(argc>1)
{
sscanf(argv[1],"%c%c",&c1,&c2);
if(c1!=‘-’) return;
if(c2=='s') //系统最近一次启动以来经历的时间和所创建的进程
{//数、三个状态的时间花费
func_B();
return;
}
if(c2=='l') //内存的使用情况
{
if(argc<4) return;
Internal=atoi(argv[2]);//将string对象转化成int对象
duration=atoi(argv[3]);
func_C(); return;
}
}
}