韦东山uboot_内核_根文件系统学习笔记5.11-第005课_字符设备驱动_第011节_字符设备驱动程序之同步互斥阻塞

目的:同一时间只能有一个应用程序打开驱动程序

  1. 实现方法1:
static int canopen =1;
static int sixth_drv_open(struct inode *inode, struct file *file)
{
	//如果第一次执行本函数,则--canopen=0,跳过下面判断语句
	//否则,--canopen=-1,执行下面语句即返回-EBUSY
	if (--canopen != 0)
	{
		canopen++;
		return -EBUSY;
	}
	...
}

int sixth_drv_close(struct inode *inode, struct file *file)
{
	canopen++;
	...
}

分析漏洞:在于A程序执行过程中--canopen对应的汇编代码实际上是分为几步的(读出/修改/写回),但是linux执行过程中是多任务系统,程序随时在切换。这就使得多个程序随时可以互相打断,--canopen汇编代码执行过程中一旦被打断就会出现两个程序均可打开驱动程序。

  1. 实现方法2:原子操作
    如何实现代码执行过程中不允许打断?
    原子操作:指的是在执行过程中不会被别的代码路径所中断的操作。
    常用原子操作函数举例:
atomic_t v = ATOMIC_INIT(0);     //定义原子变量v并初始化为0
atomic_read(atomic_t *v);        //返回原子变量的值
void atomic_inc(atomic_t *v);    //原子变量增加1
void atomic_dec(atomic_t *v);    //原子变量减少1
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。

代码实现:

static atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量并初始化为1
static int sixth_drv_open(struct inode *inode, struct file *file)
{
	if (!atomic_dec_and_test(&canopen))
	{
		atomic_inc(&canopen);
		return -EBUSY;
	}
}
int sixth_drv_close(struct inode *inode, struct file *file)
{
	atomic_inc(&canopen);
	...
}
  1. 实现方法3:信号量
    信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。①操作之前首先申请信号量;②当获取不到信号量时,进程进入休眠/等待状态;③申请到了信号量后,才可以继续向下操作,执行临界区代码;④操作完毕需要释放信号量,如果有其它应用程序等待申请信号量,则会唤醒它。

常用操作:

//定义信号量
struct semaphore sem;
//初始化信号量
void sema_init (struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);//初始化为0

static DECLARE_MUTEX(button_lock);     //定义互斥锁

//获得信号量
void down(struct semaphore * sem);
int down_interruptible(struct semaphore * sem); //如果获取不到就会休眠,休眠状态可以被打断的
/*试图获取信号量,若无法获得则直接返回1而不睡眠。返回0则表示获取到了信号量。
down_trylock接口用于试着获取一个信号量,但是,此接口不会引起调用者的睡眠。
不管有无可用信号量,都马上进行返回,如果返回0,则获取信号量成功,如果返回1,则获取失败。
所以,在调用此接口时,必须进行返回的值的查看,看是否获取成功。*/
int down_trylock(struct semaphore * sem);
//释放信号量
void up(struct semaphore * sem);

代码实现:

static DECLARE_MUTEX(button_lock);     //定义信号量
{		#define DECLARE_MUTEX(name)		__DECLARE_SEMAPHORE_GENERIC(name,1)
					//定义了一个结构体name并初始化
					#define __DECLARE_SEMAPHORE_GENERIC(name,count)	struct semaphore name = __SEMAPHORE_INIT(name,count)
							#define __SEMAPHORE_INIT(name, cnt)	{.count	= ATOMIC_INIT(cnt),.wait= __WAIT_QUEUE_HEAD_INITIALIZER((name).wait),	}
}

static int sixth_drv_open(struct inode *inode, struct file *file)
{
	...
		/* 获取信号量 
		如果第一个app就可以申请信号量,此时第二个app就无法申请到信号量就会休眠*/
		down(&button_lock);
	...
}

int sixth_drv_close(struct inode *inode, struct file *file)
{
	...
	/*释放信号量*/
	up(&button_lock);
	...
}

执行效果:可以看到连续执行的两个app程序,一个S状态(应用程序主动sleep),一个D状态(休眠状态,什么时候唤醒呢?前一个app程序执行驱动程序的close函数释放掉信号量的时候就会唤醒)

  1. 阻塞操作

①阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。(例如检测按键按下:如果没有按键按下的时候就会一直等待,直到有按键按下才会返回)
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

eg:例如:A拿着一支鱼竿在河边钓鱼,并且一直在鱼竿前等,在等的时候不做其他的事情,十分专心。只有鱼上钩的时,才结束掉等的动作,把鱼钓上来。

阻塞代码
eg:fd = open("...", O_RDWR );

②非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。

eg:B也在河边钓鱼,但是B不想将自己的所有时间都花费在钓鱼上,在等鱼上钩这个时间段中,B也在做其他的事情(一会看看书,一会读读报纸,一会又去看其他人的钓鱼等),但B在做这些事情的时候,每隔一个固定的时间检查鱼是否上钩。一旦检查到有鱼上钩,就停下手中的事情,把鱼钓上来。

非阻塞代码
eg:fd = open("...", O_RDWR | O_NONBLOCK);

驱动程序的实现:

static int sixth_drv_open(struct inode *inode, struct file *file)
{
	...
	//struct file *file是内核提供的结构
	//满足该条件,阻塞操作
	if (file->f_flags & O_NONBLOCK)
	{
		if (down_trylock(&button_lock))
			return -EBUSY;//若无法获取这个信号量则返回
	}
	//非阻塞操作
	else
	{
		/* 获取信号量 */
		down(&button_lock);
	}
	...
}


ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	...
	/*如果非阻塞,则判断有无按键发生*/
	if (file->f_flags & O_NONBLOCK)
	{
		if (!ev_press)
			return -EAGAIN;
	}
	/*如果阻塞,则...*/
	else
	{
		/* 如果没有按键动作, 休眠 */
		wait_event_interruptible(button_waitq, ev_press);
	}
	...
}

阻塞方式代码测试程序的实现:

int main(int argc, char **argv)
{
	...
	fd = open("/dev/buttons", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
		return -1;
	}
	while (1)
	{
		//对于阻塞方式,调用read函数如果没有合适的值就会休眠
		//所以,只要有值返回一定是正确的!
		ret = read(fd, &key_val, 1);
		printf("key_val: 0x%x, ret = %d\n", key_val, ret);
	}
	...
}

非阻塞方式代码测试程序的实现:

int main(int argc, char **argv)
{
	...
	fd = open("/dev/buttons", O_RDWR| O_NONBLOCK);
	if (fd < 0)
	{
		printf("can't open!\n");
		return -1;
	}
	while (1)
	{
		//对于非阻塞方式,调用read函数如果没有合适的值会返回继续向下处理
		ret = read(fd, &key_val, 1);
		printf("key_val: 0x%x, ret = %d\n", key_val, ret);
		sleep(5);//休眠5s
	}
	...
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值