11.进程的同步与互斥

11.进程的同步与互斥

计数信号量及其初始化

和王道里面学的PV操作一摸一样,带个count变量,带个阻塞队列

//D:\code\x86\code\start\start\source\kernel\include\ipc\sem.h
#ifndef OS_SEM_H
#define OS_SEM_H#include "tools/list.h"/*** 进程同步用的计数信号量*/
typedef struct _sem_t {int count;				// 信号量计数list_t wait_list;		// 等待的进程列表
}sem_t;void sem_init (sem_t * sem, int init_count);#endif //OS_SEM_H

实现函数

//D:\code\x86\code\start\start\source\kernel\ipc\sem.c
#include "cpu/irq.h"
#include "core/task.h"
#include "ipc/sem.h"/*** 信号量初始化*/
void sem_init (sem_t * sem, int init_count) {sem->count = init_count;list_init(&sem->wait_list);
}

发送和等待信号

生产者消费者问题

//D:\code\x86\code\start\start\source\kernel\include\ipc\sem.h
void sem_wait (sem_t * sem); //相当于P操作
void sem_notify (sem_t * sem); //相当于V操作
int sem_count (sem_t * sem); //放回count

实现函数

//D:\code\x86\code\start\start\source\kernel\ipc\sem.c
/*** 申请信号量*/
void sem_wait (sem_t * sem) {irq_state_t  irq_state = irq_enter_protection(); //临界区的保护if (sem->count > 0) {sem->count--; //大于0的情况} else {// 从就绪队列中移除,然后加入信号量的等待队列task_t * curr = task_current(); //获取当前进程task_set_block(curr); //阻塞当前任务,移出就绪队列list_insert_last(&sem->wait_list, &curr->wait_node); //插入等待队列后面task_dispatch();}irq_leave_protection(irq_state);
}/*** 释放信号量*/
void sem_notify (sem_t * sem) {irq_state_t  irq_state = irq_enter_protection();if (list_count(&sem->wait_list)) {// 有进程等待,则唤醒加入就绪队列list_node_t * node = list_remove_first(&sem->wait_list);task_t * task = list_node_parent(node, task_t, wait_node);task_set_ready(task);task_dispatch();} else {sem->count++;}irq_leave_protection(irq_state);
}/*** 获取信号量的当前值*/
int sem_count (sem_t * sem) {irq_state_t  irq_state = irq_enter_protection();int count = sem->count;irq_leave_protection(irq_state);return count;
}

test函数

//如果一开始count给的初值为2,其他进程又没有释放这个资源,那么抢占资源的任务就会执行两次
//init.c
void init_task_entry(void) {int count = 0;for (;;) {sem_wait(&sem);log_printf("init task: %d", count++);// task_switch_from_to(&init_task, task_first_task());// sys_yield(); //自动放弃CPU让}
}
// 放在开中断前,以避免定时中断切换至其它任务,而此时信号量还未初始化
sem_init(&sem, 2);
结果

test2

//init.cint count = 0;for (;;) {log_printf("first task: %d", count++);// 发消息给init task,可以打印了sem_notify(&sem);sys_msleep(1000);// task_switch_from_to(task_first_task(), &init_task);        // sys_yield();}
结果

互斥锁

临界资源互斥fan

之前是应用自己操作开关中断,太危险了,我们把互斥部分封装起来

这很像简化版读者写者问题

但是这里同一进程可以给临界区上很多把锁

//D:\code\x86\code\start\start\source\kernel\include\ipc\mutex.h
#ifndef MUTEX_H
#define MUTEX_H#include "core/task.h"
#include "tools/list.h"/*** 进程同步用的计数信号量*/
typedef struct _mutex_t {task_t * owner; //锁的拥有者是谁int locked_count; //这把锁已经上锁了多少次,和信号量计数不一样list_t wait_list; //这把锁的等待队列
}mutex_t;void mutex_init (mutex_t * mutex); //初始化
void mutex_lock (mutex_t * mutex); //上锁
void mutex_unlock (mutex_t * mutex); //解锁#endif //MUTEX_H

实现函数

#include "cpu/irq.h"
#include "ipc/mutex.h"/*** 锁初始化*/
void mutex_init (mutex_t * mutex) { //初始化函数mutex->locked_count = 0; //上锁次数初始值为0mutex->owner = (task_t *)0; //拥有者清0list_init(&mutex->wait_list); //队列初始化
}
/*** 申请锁*/
void mutex_lock (mutex_t * mutex) {irq_state_t  irq_state = irq_enter_protection();task_t * curr = task_current();if (mutex->locked_count == 0) { //没有上锁的状态,是不是已经上锁了// 没有任务占用,占用之mutex->locked_count = 1; //上锁mutex->owner = curr; //归属} else if (mutex->owner == curr) { //已经上锁的状态,为自己,则二度上锁// 已经为当前任务所有,只增加计数mutex->locked_count++;} else { //其他进程申请// 有其它任务占用,则进入队列等待task_t * curr = task_current();task_set_block(curr); //阻塞起来(和王道里面一样没有外界资源,就阻塞起来)list_insert_last(&mutex->wait_list, &curr->wait_node); //放到这个锁的资源的等待队列里面task_dispatch(); //任务切换}irq_leave_protection(irq_state);
}/*** 释放锁*/
void mutex_unlock (mutex_t * mutex) { irq_state_t  irq_state = irq_enter_protection();// 只有锁的拥有者才能释放锁task_t * curr = task_current(); //获取当前进程if (mutex->owner == curr) { //锁都拥有者是不是自己if (--mutex->locked_count == 0) { //减到0才释放锁,自己上多道锁,自己也需要解锁n次才能打开这个锁// 减到0,释放锁mutex->owner = (task_t *)0;// 如果队列中有任务等待,则立即唤醒并占用锁if (list_count(&mutex->wait_list)) { //是不是有进程在等list_node_t * task_node = list_remove_first(&mutex->wait_list); //从等待队列移除task_t * task = list_node_parent(task_node, task_t, wait_node);task_set_ready(task); //插入就绪队列里面// 在这里占用,而不是在任务醒后占用,因为可能抢不到mutex->locked_count = 1;//把锁交出来mutex->owner = task; //owner换人了task_dispatch(); //任务切换}}}irq_leave_protection(irq_state);
}
实例使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2869516.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

linux源配置:ubuntu、centos

1、ubuntu源配置 1)先查电脑版本型号: lsb_release -c2)再编辑源更新,源要与上面型号对应 参考:https://midoq.github.io/2022/05/30/Ubuntu20-04%E6%9B%B4%E6%8D%A2%E5%9B%BD%E5%86%85%E9%95%9C%E5%83%8F%E6%BA%90/ /etc/apt/…

插入排序:一种简单而有效的排序算法

插入排序:一种简单而有效的排序算法 一、什么是插入排序?二、插入排序的步骤三、插入排序的C语言实现四、插入排序的性能分析五、插入排序的优化六、总结 在我们日常生活和工作中,排序是一种非常常见的操作。比如,我们可能需要对一…

Echo框架:高性能的Golang Web框架

Echo框架:高性能的Golang Web框架 在Golang的Web开发领域,选择一个适合的框架是构建高性能和可扩展应用程序的关键。Echo是一个备受推崇的Golang Web框架,以其简洁高效和强大功能而广受欢迎。本文将介绍Echo框架的基本特点、使用方式及其优势…

Golang协程详解

一.协程的引入 1.通过案例文章引入并发,协程概念 见:[go学习笔记.第十四章.协程和管道] 1.协程的引入,调度模型,协程资源竞争问题 通过上面文章可以总结出Go并发编程原理: 在一个处理进程中通过关键字 go 启用多个协程,然后在不同的协程中完成不同的子任…

智慧公厕建设的重要性

智慧公厕建设一直被视为提升城市管理水平的重要举措。作为一个关键的城市基础设施,智慧公厕不仅可以改善公共卫生管理,还能提升城市居民的社会民生生活质量。此外,智慧公厕建设还能促进城市的可持续发展,降低能源消耗,…

科研绘图一:箱线图(添加贝赛尔曲线)

R语言绘图系列—箱线图贝赛尔曲线 (一): 科研绘图一:箱线图(添加贝赛尔曲线) 文章目录 R语言绘图系列---箱线图贝赛尔曲线(一): 科研绘图一:箱线图(添加贝赛尔曲线&…

数据结构/C++:红黑树

数据结构/C:红黑树 概念实现基本结构插入uncle为红色节点uncle为黑色节点 总代码展示 概念 红黑树是一种二叉搜索树,一般的二叉搜索会发送不平衡现象,导致搜索效率下降,于是学者们开始探索如何让二叉搜索树保持平衡,这…

unity学习(60)——选择角色界面--MapHandler2-MapHandler.cs

1.新建一个脚本&#xff0c;里面有static变量loadingPlayerList using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Assets.Scripts.Model {internal class LoadData{public static List<Pl…

R语言中的常用基础绘图函数 直方图,箱线图,条形图,散点图

目录 R语言中的绘图参数 绘图函数 1.plot函数绘制散点图 2.hist函数绘制直方图 如何修饰直方图? 如何在直方图上标注各组频数&#xff1f; 使用text函数把某些信息标注在直方图上 如何在直方图上添加概率密度曲线&#xff1f; 3.boxplot函数绘制箱线图 4.barplot函数…

JVM虚拟机:通过jconsole远程连接解决JVM报错

本文重点 前面我们介绍过的一些工具都是使用命令行的方式来帮助我们完成&#xff0c;本文我们将使用一种图形化界面的方式来远程连接&#xff0c;然后完成关于JVM的检测任务。 jconsole jconsole是一个JVM的检测工具&#xff0c;这个工具任何安装了Java的电脑上都有的&#…

手机网络连接性能API接口:查询手机网络连接性能状态

手机在网状态查询服务是一项非常方便的服务&#xff0c;可以帮助我们随时了解一个手机号码的在网状态。不论是查询自己的手机号码&#xff0c;还是查询他人的手机号码&#xff0c;这个服务都可以帮助我们获取准确的信息。今天&#xff0c;我想和大家介绍一个非常好用的手机在网…

GitHub Actions持续部署

一、概述 1.1Github Action介绍 什么是Github Action ? GitHub Actions是GitHub提供的CI/CD&#xff08;持续集成/持续部署&#xff09;服务。它允许你在GitHub仓库中自动化、定制和执行你的软件开发工作流。你可以发现、创建和分享用于执行任何你想要的工作的操作&#xff0…

《计算机视觉中的深度学习》之目标检测算法原理

参考&#xff1a;《计算机视觉中的深度学习》 概述 目标检测的挑战&#xff1a; 减少目标定位的准确度减少背景干扰提高目标定位的准确度 目标检测系统常用评价指标&#xff1a;检测速度和精度 提高精度&#xff1a;有效排除背景&#xff0c;光照和噪声的影响 提高检测速度…

Spark-Transformation以及Action开发实战

文章目录 创建RDDTransformation以及ActionTransformation开发Action开发RDD持久化共享变量创建RDD RDD是Spark的编程核心,在进行Spark编程是,首要任务就是创建一个初始的RDDSpark提供三种创建RDD方式:集合、本地文件、HDFS文件 集合:主要用于本地测试,在实际部署到集群运…

FAN3224TMX门极驱动器中文资料PDF数据手册引脚图参数价格图片功能特性

产品概述&#xff1a; FAN3223-25 系列双 4A 门极驱动器以较短的开关间隔提供高峰值电流脉冲&#xff0c;用于在低侧开关应用中驱动 N 沟道增强模式 MOSFET。该驱动器提供 TTL 或 CMOS 输入阈值。内部电路将输出保持在低电平&#xff0c;直到电源电压处于运行范围内&#xff0…

在Docker容器中配置`code-server`以访问宿主机的Docker环境

在Docker容器中配置code-server以访问宿主机的Docker环境 部分内容使用gpt生成&#xff0c;但经过测试可用。 要在code-server容器内部安全地管理和访问宿主机的Docker环境&#xff08;主要是为了访问宿主机的texlive&#xff09;&#xff0c;遵循以下步骤能够确保流畅的集成和…

R语言深度学习-5-深度前馈神经网络

本教程参考《RDeepLearningEssential》 本篇我们将学习如何建立并训练深度预测模型。我们将关注深度前馈神经网络 5.1 深度前馈神经网络 我们还是使用之前提到的H2O包&#xff0c;详细可以见之前的博客&#xff1a;R语言深度学习-1-深度学习入门&#xff08;H2O包安装报错解决…

[scikit-learn] 第一章 初识scikit-learn及内置数据集介绍

文章目录 菜鸡镇贴&#xff01;&#xff01;&#xff01;scikit-learn 简要介绍scikit-learn 安装scikit-learn 数据集介绍数据集API介绍LoadersSamples generator 导入数据集demo 菜鸡镇贴&#xff01;&#xff01;&#xff01; scikit-learn 简要介绍 ​ Scikit learn是一个…

RK3568平台开发系列讲解(基础篇)内核是如何发送事件到用户空间

🚀返回专栏总目录 文章目录 一、相关接口函数二、udevadm 命令三、实验沉淀、分享、成长,让自己和他人都能有所收获!😄 一、相关接口函数 kobject_uevent 是 Linux 内核中的一个函数, 用于生成和发送 uevent 事件。 它是 udev 和其他设备管理工具与内核通信的一种方式。…

SDN网络简单认识(1)——概述

一、概述 软件定义网络&#xff08;Software Defined Networking&#xff0c;SDN&#xff09;是一种网络架构理念&#xff0c;旨在使网络灵活和可编程&#xff0c;从而更好地支持动态和高度可扩展的计算环境。SDN通过抽象网络的控制层&#xff08;决策层&#xff09;和数据层&a…