• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    实现函数

    //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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    发送和等待信号

    生产者消费者问题

    //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
    
    • 1
    • 2
    • 3
    • 4

    实现函数

    //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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    结果

    test2

    //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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    结果

    互斥锁

    临界资源互斥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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    实现函数

    #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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    实例使用

  • 相关阅读:
    python 将一个文件中的空格替换成逗号 写入新文件
    高级进阶班1——补充(利用平凡解优化流程、通过记录结构找到可能性划分的边界情况、动态规划填表的斜率优化技巧、上中位数结构)
    用Python在PDF文档中插入单图像水印和平铺图像水印
    HOOK Native API
    Adobe Photoshop 自动化 脚本
    第十二章 配置 Apache 以与 Web 网关配合使用 (Windows)
    计算点云每个点的粗糙度(附open3d python代码)
    [思维][组合数学]Madoka and The Corruption Scheme Codeforces1717D
    DeepLearning[花书] 参考资料
    当年很流行,现在已经淘汰的前端技术有哪些?
  • 原文地址:https://blog.csdn.net/weixin_45131087/article/details/136751057