总体概述
本次lab的主要内容是拓展同步机制,实现同步互斥实例
本次lab的重要知识点在于对同步互斥原理的理解
任务完成情况
- Exercise 1 –> Y
- Exercise 2 –> Y
- Exercise 3 –> Y
- Exercise 4 –> Y
- Challenge 1 –> Y
- Challenge 2 –> Y
具体完成情况如下所示:
Exercise 1 调研
调研Linux中实现的同步机制
主流的Linux内核中包括了几乎所有现代操作系统具有的同步机制,这些同步机制包括:原子操作,信号量(semaphore),读写信号量(rw_semaphore),spinlock,BKL(Big Kernel Lock),rwlock、,brlock(只包含在2.4内核中),RCU(只包含在2.6内核中)和seqlock(只包含在2.6内核中)。linux操作系统中主要是现代同步机制如下:
原子操作
所谓的原子操作,就是该操作不会在执行完毕前被其他所有其他任务或事件打断,也就是说,一个原子操作就是最小的执行单位,不可能有更小的执行单位。
原子操作需要硬件的支持,因此是架构相关的,其API和原子类型的定义都在内核源码的includ/asm/aatomic.h中,使用汇编代码实现。
原子操作主要用于实现资源计数,非常多引用数(refcnt)就是通过原子操作实现的。原子类型的定义如下:
typedef struct
{
volatile int counter;
}
atomic_t;
volatile
字段告诉gcc编译器不要对该数据类型进行优化处理,对他的访问都是对内存的访问,而不是对寄存器的访问。
原子操作的API包括:
atomic_read(atomic_t * v); //该函数对原子类型的变量进行原子读操作,他返回原子类型的变量v的值。
atomic_set(atomic_t * v, int i); //该函数设置原子类型的变量v的值为i。
void atomic_add(int i, atomic_t *v); //该函数给原子类型的变量v增加值i。
atomic_sub(int i, atomic_t *v); //该函数从原子类型的变量v中减去i。
int atomic_sub_and_test(int i, atomic_t *v);//该函数从原子类型的变量v中减去i,并判断结果是否为0,如果为0,返回真,否则返回假。
void atomic_inc(atomic_t *v); //该函数对原子类型变量v原子地增加1。
void atomic_dec(atomic_t *v); //该函数对原子类型的变量v原子地减1。
int atomic_dec_and_test(atomic_t *v); //该函数对原子类型的变量v原子地减1,并判断结果是否为0,如果为0,返回真,否则返回假。
int atomic_inc_and_test(atomic_t *v); //该函数对原子类型的变量v原子地增加1,并判断结果是否为0,如果为0,返回真,否则返回假。
int atomic_add_negative(int i, atomic_t *v); //该函数对原子类型的变量v原子地增加I,并判断结果是否为负数,如果是,返回真,否则返回假。
int atomic_add_return(int i, atomic_t *v); //该函数对原子类型的变量v原子地增加i,并且返回指向v的指针。
int atomic_sub_return(int i, atomic_t *v); //该函数从原子类型的变量v中减去i,并且返回指向v的指针。
int atomic_inc_return(atomic_t * v); //该函数对原子类型的变量v原子地增加1并且返回指向v的指针。
int atomic_dec_return(atomic_t * v); //该函数对原子类型的变量v原子地减1并且返回指向v的指针。
信号量
Linux内核实现了信号量用以同同步互斥机制。
信号量在创建时要设置一个初始值,表示同时能有几个任务能方法问该信号量保护的共享资源,初始值设为1就变成互斥锁(Mutex),即同时只能有一个任务能访问信号量保护的共享资源。
一个任务要想访问共享资源,该任务必须得到信号量,获取信号量的操作将信号量的值减1,若当前信号量为负数,则表明不能获得信号量,该任务必须挂起在信号量的等待队列等待该信号量可用,若当前信号量的值为非负数,表明能获取信号量,因而可以访问共享资源。
当任务访问完共享资源和后,必须释放信号量,释放信号量通过将信号量的值加一实现。如果信号量的值为非正数,表明有任务等待当前心好累,因此也将唤醒等待队列中的一个任务。
信号量的API有:
DECLARE_MUTEX(name) //该宏声明一个信号量name并初始化他的值为1,即声明一个互斥锁。
DECLARE_MUTEX_LOCKED(name) //该宏声明一个互斥锁name,但把他的初始值设置为0,即锁在创建时就处在已锁状态。因此对于这种锁,一般是先释放后获得。
void sema_init (struct semaphore *sem, int val); //该函用于数初始化设置信号量的初值,他设置信号量sem的值为val。
void init_MUTEX (struct semaphore *sem); //该函数用于初始化一个互斥锁,即他把信号量sem的值设置为1。
void init_MUTEX_LOCKED (struct semaphore *sem);
//该函数也用于初始化一个互斥锁,但他把信号量sem的值设置为0,即一开始就处在已锁状态。
void down(struct semaphore * sem);
//该函数用于获得信号量sem,他会导致睡眠