操作系统-----一文学懂进程同步和进程互斥

前言

当进程运行时,对于各种资源的访问非常频繁,因此掌握进程同步和进程互斥极为关键,下面让我带领大家逐一突破它们。

进程同步

引入进程同步之前,先举一个编写Java程序的小例子:

int result=10;//定义并赋值
System.out.println(result);//输出

上面的代码,“定义并赋值”操作必须在“输出”操作之前,不然程序将会报错,若把两种操作看作两个进程,那么这就是进程同步。
什么是进程同步?
定义:在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。

进程互斥

引入进程互斥之前,先举一个生活中的小例子:
若校园内的只有一辆环校车,并且有多个位置,司机需要一个位置,还有多个位置供同学乘坐,刚好有两位同学需要乘坐环校车,那么两个同学可以同时这趟环校车,即这方式便是同时共享同一资源。
若校园内的只有一辆环校车,并且只有两个位置,司机需要一个位置,只有一个位置供同学乘坐,刚好有两位同学需要乘坐环校车,那么必有一个同学需要等待环校车的下一趟,即这方式便是互斥共享同一资源,进程互斥的原理与这个例子类似。
什么是进程互斥?
定义:在多道程序环境下,对临界资源的访问,需要互斥的进行,即同一时间段内只能允许一个进程访问该资源。

操作系统中,进程互斥由四个部分构成:
进入区:检查是否可进入临界区,若可进入,需要“上锁”。
临界区:访问临界资源的那段代码。
退出区:负责“解锁”。
剩余区:其余代码部分。

do{
	entry section;      //进入区
	critical section;	//临界区
	exit section;		//退出区
	remainder section;  //剩余区
}while(true)

解释:进入区和退出区实现进程互斥的必要操作,进程进入之前,需要“上锁”,不允许它在访问资源时,其余进程也进来访问,进程退出之后,需要“解锁”,该进程用完后,需要打开权限供下一个用户使用,因此这两个区域存在是非常必要的。
临界区即是进程访问核心区,进程在这里可以访问到它需要的资源。
那么大家可能会疑惑,为什么还要定义个剩余区呢?
其实,这个区域的代码与进程无关,若全部逻辑代码都放在临界区,该代码必将冗余,不符合编程代码规范,便多定义个剩余区。

操作系统中,进程互斥需要遵循以下规则:

  1. 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区;
  2. 忙则等待:当已有进程进入临界区时,其他试图进入临界区的进程必须等待;
  3. 有限等待:对请求等待的进程,应保证在有限时间内能够进入临界区(保证不会出现饥饿现象);
  4. 让权等待:当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。

解释:其实进程互斥的需要遵循的规则,是对进程互斥定义的详细描述。

信号量机制

实现进程互斥的方法:
软件实现:
在这里插入图片描述
硬件实现:
在这里插入图片描述
以上多种实现进程互斥的方法,只是简单给出了定义,如果大家要进一步研究,则需要在网上查找其相关资料进行学习;上面那么多种都有一些问题,为了解决该问题,荷兰学者Dijkstra提出了一种卓有成效的实现进程互斥的方法,即信号量机制,并且该方式来可实现进程同步操作。

信号量机制可分为整型信号量和记录型信号量:
整型信号量:
在这里插入图片描述
记录型信号量:
在这里插入图片描述
记录型信号量的解释:
在这里插入图片描述
小总结:
在这里插入图片描述

用信号量实现进程同步、互斥和前驱关系

下面是用信号量实现进程同步:

在这里插入图片描述

下图是用信号量实现进程互斥:
在这里插入图片描述
下图是实现信号量实现进程的前驱关系:
在这里插入图片描述
小总结:
在这里插入图片描述

总结

从上面学习中,大家不难发现,进程的互斥和同步这两种操作在生活中无处不在,对于进程互斥的遵守规则,我认为可以适当运用于生活中,这也可以达到学以致用的目的,同时也可以让我们生活变得更好!

该文章的编写参考王道考研的操作系统课程。
文中若内容出现不足之处,望读者在评论区踊跃发表观点!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者消费者模型是计算机科学一个经典的问题,也是进程同步互斥的一个重要应用。该模型描述了一个生产者和一个消费者共同使用一个有限缓冲区的情景,生产者向缓冲区添加数据,而消费者则从缓冲区取出数据。 为了避免在共享缓冲区时产生数据不一致的情况,需要进行进程同步互斥。其进程同步是指在多个进程之间协调事件发生的时间,而互斥则是指在同一时间只允许一个进程访问共享资源。 以下是一个基于信号量的生产者消费者模型的示例: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; // 缓冲区 int in = 0; // 生产者写入数据的位置 int out = 0; // 消费者读取数据的位置 sem_t empty; // 信号量,表示缓冲区空余的位置数 sem_t full; // 信号量,表示缓冲区已有数据的位置数 sem_t mutex; // 信号量,用于实现互斥访问缓冲区 void *producer(void *arg) { int item; while(1) { item = rand() % 1000; // 生产者随机生成一个数据项 sem_wait(&empty); // 等待缓冲区有空余位置 sem_wait(&mutex); // 进入互斥区,保证对缓冲区的访问是互斥的 buffer[in] = item; printf("Producer produces item %d at buffer[%d]\n", item, in); in = (in + 1) % BUFFER_SIZE; sem_post(&mutex); // 退出互斥区 sem_post(&full); // 增加已有数据的位置数 sleep(rand() % 2); // 生产者随机休眠一段时间 } } void *consumer(void *arg) { int item; while(1) { sem_wait(&full); // 等待缓冲区有数据 sem_wait(&mutex); // 进入互斥区 item = buffer[out]; printf("Consumer consumes item %d from buffer[%d]\n", item, out); out = (out + 1) % BUFFER_SIZE; sem_post(&mutex); // 退出互斥区 sem_post(&empty); // 增加空余位置数 sleep(rand() % 2); // 消费者随机休眠一段时间 } } int main() { pthread_t producer_thread, consumer_thread; sem_init(&empty, 0, BUFFER_SIZE); // 初始化信号量 sem_init(&full, 0, 0); sem_init(&mutex, 0, 1); pthread_create(&producer_thread, NULL, producer, NULL); // 创建生产者线程 pthread_create(&consumer_thread, NULL, consumer, NULL); // 创建消费者线程 pthread_join(producer_thread, NULL); // 等待生产者线程结束 pthread_join(consumer_thread, NULL); // 等待消费者线程结束 sem_destroy(&empty); // 销毁信号量 sem_destroy(&full); sem_destroy(&mutex); return 0; } ``` 在上述代码,empty 表示缓冲区空余的位置数,初始值为 BUFFER_SIZE;full 表示缓冲区已有数据的位置数,初始值为 0;mutex 用于实现互斥访问缓冲区,初始值为 1。在生产者和消费者访问缓冲区时,都需要先进行信号量操作,保证进程同步互斥。其,sem_wait 和 sem_post 分别表示等待和增加信号量的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值