在出现了同步问题后,如何解决呢?可以在访问公共变量的时候,设置一个锁变量,这个锁变量有两个状态,一个是上锁的状态,另一个是释放状态,如果锁变量没有上锁,则可以访问变量,同时将锁置为上锁状态。待访问变量结束后,将锁置为释放状态,允许下一个进程访问变量。示例的程序如下:
int lock = 0;
int cnt = 0;
int app1(void)
{
int tmp;
while(lock) ;
lock = 1;
printf("app1 count:%d\n",cnt);
tmp = cnt + 1;
Proc_delay(100);
printf("app1 count:%d\n",tmp);
cnt = tmp;
lock = 0;
return 0;
}
int app2(void)
{
int tmp;
while(lock) ;
lock = 1;
printf("app2 count:%d\n",cnt);
tmp = cnt + 1;
Proc_delay(100);
printf("app2 count:%d\n",tmp);
cnt = tmp;
lock = 0;
return 0;
}
修改为这样后,就可以得到想要的结果了,但是,这还是存在隐患,因为
while(lock) ;
lock = 1;
这段代码有两个问题,
1、这实际上还是存在被打断的可能,虽然从检测到赋值间隔很短,但其仍是需要几条机器指令才能完成的。
2、这处于一个忙等待的状态,如果不能保证其他程序占有锁的时间很短,则会造成CPU时间浪费在循环检测下。
为了解决这两个问题,就提出了一个概念,就是需要在检测到锁空闲,然后赋值这两步保证不被打断。这种不能被打断的多步连续操作,称为原子操作,一般的操作系统将其称为原语。在单CPU,在现在要说单cpu单核心,的情况下,可以通过关中断来实现,因为在关闭中断后,除非进程自行放弃CPU,是不可能被打断的。
根据这一概念,Lenix实现了锁操作
void Proc_sleep(void)
{
proc_current->proc_stat = PROC_STAT_SLEEP;
Proc_sched();
}
void Proc_wakeup(void)
{
int i;
Disable_irq();
for( i = 1 ; i < PROC_MAX ; i++)
{
if( PROC_STAT_SLEEP == proc_pool[i].proc_stat )
proc_pool[i].proc_stat = PROC_STAT_RUN;
}
Enable_irq();
}
void Lck_lock(lock_t * lck)
{
Disable_irq();
while( *lck )
Proc_sleep();
*lck = 1;
Enable_irq();
}
void Lck_free(lock_t * lck)
{
*lck = 0;
Proc_wakeup();
}
锁的实现要依靠进程状态转换操作支持,在Lck_lock中没有转入睡眠,由于关了中断,就变成死循环了。有了锁机制之后,原先的代码就变成一下的样子
int lock = 0;
int cnt = 0;
int app1(void)
{
int tmp;
Lck_lock(&lock);
printf("app1 count:%d\n",cnt);
tmp = cnt + 1;
Proc_delay(100);
printf("app1 count:%d\n",tmp);
cnt = tmp;
Lck_free(&lock);
return 0;
}
int app2(void)
{
int tmp;
Lck_lock(&lock);
printf("app2 count:%d\n",cnt);
tmp = cnt + 1;
Proc_delay(100);
printf("app2 count:%d\n",tmp);
cnt = tmp;
Lck_free(&lock);
return 0;
}
嗯,这个代码看起来比较规范了。