Wei Dongshan uboot_kernel_root file system study notes 5.11-lesson 005_character device driver_section 011_character device driver synchronization mutual exclusion blocking

Purpose: Only one application can open the driver at the same time

  1. Implementation method 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++;
	...
}

Analysis of loopholes: The --canopencorresponding assembly code during the execution of A program is actually divided into several steps (read/modify/write back), but the linux execution process is a multitasking system, and the program is switched at any time. This allows multiple programs to interrupt each other at any time --canopen. Once the assembly code is interrupted during execution, both programs can open the driver.

  1. Implementation method 2: Atomic operation
    How to implement code execution without interruption?
    Atomic operations: refer to operations that will not be interrupted by other code paths during execution.
    Examples of commonly used atomic operation functions:
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。

Code:

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. Implementation method 3:
    semaphore Semaphore is a common method used to protect critical sections. Only processes that get the semaphore can execute critical section code. ①Apply for the semaphore before the operation; ②When the semaphore cannot be obtained, the process enters the dormant/waiting state; ③After the semaphore is applied for, it can continue to operate downward and execute the critical section code; ④The semaphore needs to be released after the operation is completed , If there are other applications waiting to apply for the semaphore, it will wake it up.

Common operations:

//定义信号量
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);

Code:

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

Execution effect: You can see two app programs that are executed continuously, one S state (the application actively sleeps) and one D state (sleep state, when will it wake up? The previous app program executes the driver's close function to release the semaphore Will wake up when

  1. Blocking operation

① Blocking operation
refers to suspending the process if resources cannot be obtained when performing device operations, and then performing operations until the operable conditions are met. (For example, detecting button press: if there is no button pressed, it will wait until there is a button pressed.) The
suspended process enters the sleep state and is removed from the run queue of the scheduler until it waits. The condition is met.

eg: For example: A is fishing by the river with a fishing rod, and has been waiting in front of the fishing rod. While waiting, he does not do other things and is very attentive. Only when the fish is hooked, the waiting action is ended and the fish is caught.

Blocking code
eg:fd = open("...", O_RDWR );

②Non-blocking operation The
process does not hang when the device cannot be operated. It either gives up or keeps inquiring until it can be operated.

eg: B is also fishing by the river, but B does not want to spend all of his time fishing. During the period of waiting for the fish to be hooked, B is also doing other things (reading a book for a while, reading a newspaper for a while , I went to watch other people’s fishing, etc.), but when B was doing these things, he would check whether the fish was hooked at regular intervals. Once you check that a fish has taken the bait, stop what you are doing and catch the fish.

Non-blocking code
eg:fd = open("...", O_RDWR | O_NONBLOCK);

The realization of the driver:

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

Implementation of blocking code test program:

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

Implementation of non-blocking code test program:

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
	}
	...
}

Guess you like

Origin blog.csdn.net/xiaoaojianghu09/article/details/104339412