操作系统进程互斥的软件实现算法(单标志法、双标志检查法、双标志后检查法以及皮尔森算法)

操作系统进程互斥

       在多道批处理系统中,多个进程是并发执行的,而并发执行的进程不可避免地需要共享一些系统资源(比如内存、打印机、摄像头等)。这样会带来什么问题呢?实际上,有些资源在一个时间段内只允许一个进程使用,诸如各种物理设备、变量、数据、内存缓冲区等,这些称之为临界资源 —— 也就是说,一方面,并发执行的进程需要共享资源;另一方面,临界资源的访问又必须是互斥地进行(不能同时共享),很显然,这会导致资源访问上的矛盾。
       这就提出了进程互斥的概念,利用进程互斥来解决临界资源同时访问的冲突问题。

互斥四个原则

  • 空闲让进:临界区空闲时,说明没有进程使用临界资源,此时应该让想要进入临界区的进程立刻进来
  • 忙则等待:如果已经有进程进入临界区,则其它同样想要进入的进程只能等着
  • 有限等待:不能让进程一直干等着,要保证他在有限的时间内可以进入临界区
  • 让权等待::当进程不能进入自己的临界区时,应该立刻释放处理机,防止进程陷入“忙等”状态

软件实现进程互斥 (四个算法)

单标志法

       设置公用整型变量turn,用于指示允许进入临界区的进程编号,turn为零,则允许P0进程进入临界区。

特点:能够确保进程互斥访问临界区,但是不能保证满足"空闲让进"原则,并且两进程必须交替进入临界区。
个人形象理解(若有问题还请留言讨论呀):单标志法实现互斥,即主要通过turn这个变量来处理,即从初始值为0开始,先允许P0访问进程,然后访问结束之后立即将访问权交给P1手中;这样存在的问题就是,进程没有考虑自身下次是否还会继续使用该资源。若下次在P1进程之前还想访问该临界资源会出现一直得不到资源的情况;
       好比如:现在是桌上只有一双筷子,有A跟B两个人,一开始先把筷子给A,A吃完后直接就把筷子洗干净给B了,然后说你吃完再把筷子洗干净给我,结果B无语了,他也没说要用筷子吃东西,然后A就是说不管,你必须吃完过后再把筷子给我,结果A自己又想吃的时候结果没有筷子用,因为筷子还在B那里呢,B还在纳闷A怕不是有什么大病。

int turn = 0;
P0 进程:                        P1 进程:
while (turn != 0);              while (turn != 1); // 进入区
critical section;               critical section;  // 临界区
turn = 1;                       turn = 0;		   // 退出区
remainder section;              remainder section; // 剩余区
双标志先检查法

       设置一个数组flag[2],表示继承是否进入临界区,flag[i]的值为FALSE,表示Pi进程未进入临界区,为TRUE则表示Pi进入临界区。

特点:不能确保进程互斥访问临界区,但是不能保证满足"忙则等待"原则,进程不必交替进入,可以连续使用。
个人形象理解(若有问题还请留言讨论呀):其实感觉这个算法有点鸡肋,不过还是为后面不断完善提供了基础,这个算法的主要就是通过flag来对进程是否将要访问临界资源进行标记,即进入临界区之前,先检查对方进程是否想要访问进程,如果对方进程想要进入临界区,则自己可以等会,否则,则自己进入临界区,一进入就把标志置为true;
       好比如:现在同样是桌上只有一双筷子,有A跟B两个人,但是现在这两个人会察言观色了,会先看对方会不会想要先用筷子,然后再判断下一步是使用筷子还是接着等待。一开始,两个人都不说自己想不想用筷子,就等对方先表态,毕竟要多谦让,最后彼此看这么久都不说话,估计就是不想用了,然后彼此都默认对方不想用筷子。这个时候就会出现问题了,两个人都默认彼此不想用筷子,然后自己就下意识去拿筷子,结果刚好对方也想用,这就尴尬了,那最后是谁用呢,这个时候就会出现资源被同时访问的问题。

bool flag[2]= {false,false};

P0 进程:                        P1 进程:
while (flag[1]);                while (flag[0]); 	// 进入区
flag[0] = true;                 flag[1] = true;		// 进入区
critical section;               critical section;	// 临界区
flag[0] = false;                flag[1] = false;	// 退出区
remainder section;              remainder section;  // 剩余区
双标志后检查法

       设置一个数组flag[2],这里与前面不同之处就是,先设置自己的标志位,再检测对方的标志状态,若对方的标志位为true则等待呗。

特点:能够确保进程互斥访问临界区,但是存在两个进程都进入不了临界区的"饥饿"现象。违背了"空闲让进"和"有限等待";
个人形象理解(若有问题还请留言讨论呀):这个算法同样是通过flag来对进程是否将要访问临界资源进行标记,在进入临界区之前,先设置自己的标志位,再检查对方进程是否想要访问进程,如果对方进程想要进入临界区,则自己可以等会,否则,则自己进入临界区;
       好比如:现在还是桌上只有一双筷子,但是现在就不是A跟B了,换成孔融1号和孔融2号,为什么给他们这样取名字呢,后面就知道啦!现在这两个人呢,在想用筷子的时候都会先说出来表明自己的态度,然后再看对方会不会想要先用筷子,然后再判断下一步是使用筷子还是接着等待。如果一开始两个人同时表明自己想要筷子的话,对方都会考虑到礼仪问题,谦让给对方用,毕竟谁叫他们叫孔融呢,但是这样出现的问题就是明明有筷子可以用但是因为谦让而僵持住。结果两个人就只能饿着了,在操作系统里面这里就出现了"死等",即会存在进程产生"饥饿"。

bool flag[2]= {false,false};

P0 进程:                        P1 进程:
flag[0] = true;   			    flag[1] = true;		// 进入区
while (flag[1]);                while (flag[0]); 	// 进入区
critical section;               critical section;	// 临界区
flag[0] = false;                flag[1] = false;	// 退出区
remainder section;              remainder section;  // 剩余区
Peterson 算法

       设置一个数组flag[2],这里与前面不同之处就是,先设置自己的标志位,再检测对方的标志状态,若对方的标志位为true则等待呗。

特点:Peterson 算法实际上同时结合了单标志法和双标志后检查法,它的核心就是:在一开始还是和后检查法一样,抢先进行“上锁”,但是上锁之后又将 turn 置为对方线程,表示自己虽然想要进入临界区,但是不介意“将这个机会让给对方”。尽管如此,由于 while 的限制条件增加了,而 turn 又是公用的,所以保证了最后只会有一方的 while 满足条件。既做到了互斥访问资源,也避免了双方都访问不到资源。

P0 进程:                        P1 进程:
flag[0] = true;turn = 1;        flag[1] = true;turn = 0;  // 进入区             
while (flag[1] && turn == 1);   while (flag[0] && turn == 0);// 进入区
critical section;               critical section;	// 临界区
flag[0] = false;                flag[1] = false;	// 退出区
remainder section;              remainder section;	// 剩余区
  • 19
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
好的,我可以为您解答这个问题。 首先,时间片轮转调度算法是一种常见的进程调度算法,它的实现基于一个时间片的概念,每个进程被分配一个时间片,当时间片用完后,系统会强制切换到下一个就绪进程。以下是使用 C 语言实现时间片轮转调度算法的示例代码: ```c #include <stdio.h> #define QUANTUM 2 // 定义时间片长度为 2 struct process { int pid; int burst_time; int remaining_time; }; int main() { int n, i, j; struct process p[10]; printf("Enter the number of processes: "); scanf("%d", &n); for (i = 0; i < n; i++) { printf("Enter the burst time for process %d: ", i + 1); scanf("%d", &p[i].burst_time); p[i].pid = i + 1; p[i].remaining_time = p[i].burst_time; } printf("\nProcess\tBurst Time\n"); for (i = 0; i < n; i++) { printf("%d\t%d\n", p[i].pid, p[i].burst_time); } printf("\n"); int time = 0; int flag = 0; while (1) { flag = 0; for (i = 0; i < n; i++) { if (p[i].remaining_time > 0) { flag = 1; if (p[i].remaining_time > QUANTUM) { time += QUANTUM; p[i].remaining_time -= QUANTUM; printf("Process %d executed for %d seconds\n", p[i].pid, QUANTUM); } else { time += p[i].remaining_time; printf("Process %d executed for %d seconds\n", p[i].pid, p[i].remaining_time); p[i].remaining_time = 0; } } } if (flag == 0) { break; } } printf("\nTotal execution time: %d seconds\n", time); return 0; } ``` 接下来,我们来模拟生产者消费者进程实现同步与互斥操作。生产者消费者问题是一种经典的线程同步问题,它涉及两种类型的进程:生产者和消费者。生产者进程负责生产数据,而消费者进程负责消费数据。为了防止生产者和消费者之间的竞争条件,我们需要使用同步和互斥机制。以下是使用 C 语言模拟生产者消费者进程的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; int count = 0; int in = 0; int out = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t full = PTHREAD_COND_INITIALIZER; pthread_cond_t empty = PTHREAD_COND_INITIALIZER; void *producer(void *arg) { int i; for (i = 0; i < 20; i++) { pthread_mutex_lock(&mutex); while (count == BUFFER_SIZE) { pthread_cond_wait(&empty, &mutex); } buffer[in] = i; printf("Produced item %d\n", buffer[in]); in = (in + 1) % BUFFER_SIZE; count++; pthread_cond_signal(&full); pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } void *consumer(void *arg) { int i; for (i = 0; i < 20; i++) { pthread_mutex_lock(&mutex); while (count == 0) { pthread_cond_wait(&full, &mutex); } int item = buffer[out]; printf("Consumed item %d\n", item); out = (out + 1) % BUFFER_SIZE; count--; pthread_cond_signal(&empty); pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } int main() { pthread_t producer_thread, consumer_thread; pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); return 0; } ``` 在这个示例中,我们使用了互斥锁和条件变量来实现同步和互斥操作。生产者进程通过互斥锁保护共享缓冲区,并使用条件变量来通知消费者进程缓冲区已经被更新。消费者进程也通过互斥锁保护共享缓冲区,并使用条件变量来通知生产者进程缓冲区已经被消费。这样可以保证生产者和消费者之间的竞争条件得到有效的解决。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我蘑菇先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值