11.进程的同步与互斥

11.进程的同步与互斥

计数信号量及其初始化

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

c 复制代码
//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

实现函数

c 复制代码
//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);
}

发送和等待信号

生产者消费者问题

c 复制代码
//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

实现函数

c 复制代码
//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函数

c 复制代码
//如果一开始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

c 复制代码
//init.c
    int 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

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

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

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

c 复制代码
//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

实现函数

c 复制代码
#include "cpu/irq.h"
#include "ipc/mutex.h"

/**
 * 锁初始化
 */
void mutex_init (mutex_t * mutex) { //初始化函数
    mutex->locked_count = 0; //上锁次数初始值为0
    mutex->owner = (task_t *)0; //拥有者清0
    list_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);
}
实例使用
相关推荐
USER_A0012 分钟前
【C语言】第五期——函数
c语言
power-辰南2 小时前
高并发系统架构设计全链路指南
分布式·系统架构·高并发·springcloud
晴空了无痕2 小时前
现代任务调度系统架构深度解析——以TaskSchedulerController为核心的弹性任务管理方案
unity·系统架构·游戏引擎
Nerd Nirvana3 小时前
软考—系统架构设计(案例 | 论文)
linux·系统架构·软件工程·软考·计算机基础
程序员古德3 小时前
《论云上自动化运维及其应用》审题技巧 - 系统架构设计师
系统架构·项目经验·软考论文·云上自动化运维·衡量指标·实践应用
李白同学6 小时前
【C语言】结构体内存对齐问题
c语言·开发语言
楼台的春风7 小时前
【MCU驱动开发概述】
c语言·驱动开发·单片机·嵌入式硬件·mcu·自动驾驶·嵌入式
waicsdn_haha16 小时前
Visual Studio Code 2025 安装与高效配置教程
c语言·ide·windows·vscode·微软·编辑器·win7
----云烟----17 小时前
C/C++ 中 volatile 关键字详解
c语言·开发语言·c++
BanLul17 小时前
进程与线程 (三)——线程间通信
c语言·开发语言·算法