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);
}