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);
}
实例使用
相关推荐
TeYiToKu8 分钟前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
互联网打工人no115 分钟前
每日一题——第一百二十四题
c语言
爱吃生蚝的于勒18 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~21 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
洋24041 分钟前
C语言常用标准库函数
c语言·开发语言
徐嵌1 小时前
STM32项目---畜牧定位器
c语言·stm32·单片机·物联网·iot
xinghuitunan2 小时前
蓝桥杯顺子日期(填空题)
c语言·蓝桥杯
Half-up2 小时前
C语言心型代码解析
c语言·开发语言
懒大王就是我3 小时前
C语言网络编程 -- TCP/iP协议
c语言·网络·tcp/ip
半盏茶香3 小时前
【C语言】分支和循环详解(下)猜数字游戏
c语言·开发语言·c++·算法·游戏