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);
}
实例使用
相关推荐
小僧景贤16 分钟前
嵌入式C语言 第十一篇:成长路线|嵌入式C工程师完整进阶学习路径(从零到工程级落地|含周期+实战项目)
c语言·开发语言·学习
灵晔君34 分钟前
【Linux】进程(四)——进程虚拟地址空间
linux·c语言·开发语言
天空'之城1 小时前
C 语言工业级通用组件手写 25:简易日志系统
c语言·开发语言·日志系统·嵌入式调试·调试打印
热心网友俣先生2 小时前
2026年华数杯C 题 超详细解题思路
c语言·开发语言·人工智能
youngerwang13 小时前
【软件系统架构案例分析 Day 8】可用性战术:双活数据中心设计
系统架构·可用性战术·双活数据中心
youngerwang16 小时前
【软件系统架构案例分析 Day 10】安全性战术:零信任架构在企业内网的应用
系统架构·零信任架构·安全性战术·企业内网intranet
用户昵称10017 小时前
C/C++编程-工程实践-本地存储log的工程意义
c语言·开发语言
ai_coder_ai18 小时前
大模型应用系统架构设计及其应用
系统架构
老师我太想进步了20261 天前
C语言版讲解函数递归
c语言·开发语言
LONGZETECH1 天前
大众 ID.4 CROZZ 整车级虚拟拆解:新能源汽车高压实训的数字化解决方案
c语言·开发语言·架构·汽车·汽车仿真教学软件