Linux driven learning record-14. Blocking and experiment

Blocking and non blocking are common access modes for Linux driver development. Programming should be considered. This introduction is blocked.

1, Blocking introduction

Blocking operation refers to suspending the process until the resource can be accessed when the device operation cannot obtain the device resource. The suspended operation enters sleep. When the non blocking process cannot operate the device, it will not hang, either give up or keep querying until it can operate.
The difference is that when the read and write functions in the application are executed, if the device is inoperable, the application will get stuck here. At the same time, the xxread and xxwrite functions in the driver block the process until the resources can be obtained and then return to the application. Non blocking means that the driver's xxxread and xxwrite functions will immediately return when they find that the device is not desirable, and the application will continue to execute.
Blocking may seem inefficient, but if it is not blocking, the application will keep polling and waste cpu resources. Blocking access makes the process that can't get resources go to sleep, and makes the cpu resources give up to other processes.
When a blocked process goes into sleep, make sure that there is i a place to wake up the dormant process, otherwise it will sleep forever. The wake-up is usually placed in the interrupt.
The example code is as follows:

int fd;
int data = 0;
fd = open("/dev/xxx+dev", O_EDWR);    //Blocking mode on
ret = read(fd, &data, sizeof(data));  //Read until a value is returned

2, Waiting queue

The Linux driver uses a wait queue to wake up dormant processes.
The following is the flow diagram:

1. Define the waiting queue header

Queue header structure

struct __wait_queue_head{
spinlock_t lock;
struct list_head task_list;
typedef struct __wait_queue_head wait_queue_head_t;

wait_queue_head_t my_queue;  //definition

2. Initialize queue header

void init_waitqueue_head(wait_queue_head_t &my_queue)

You can also use the macro DECLARE_WAIT_QUEUE_HEAD (name), which directly defines and initializes the waiting queue header of name.

3. Define waiting queue

struct __wait_queue{
usigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
typedef struct __wait_queue wait_queue_t;

Using macro DECLARE_WAITQUEUE(name, tsk). Name indicates the name of the queue item. tsk indicates which process the queue belongs to. It is generally current.

4. Add and remove waiting queues

void add_wait_queue( wait_queue_head_t *q, // Waiting queue header to join
					wait_queue_t *wait)    //Waiting queue item
void remove_wait_queue(wait_queue_head_t *q,
						wait_queue_t *wait)

5. Wait for wake-up

void wake_up (wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)

The first one can wake up TASK_INTERRUPTIBLE and task_ The second process can only wake up task_ Interrupt process.

6. Waiting events

In addition to active wake-up, you can set a waiting queue to wait for an event. When the event is satisfied, you can wake up the device

wait_event(wq, condition)Wait for the waiting queue with wq as the head of the waiting queue to wake up, provided that the condition condition is true, otherwise it will be blocked all the time,
wait_event_timeout(wq, condition, timeout)The function is the same as before. You can add timeout in units of jiffies. If 0 is returned, the timeout occurs and the condition is false. If 1 is returned, the condition is true
wait_event_interruptible(wq, conditon)Same as before, but the process sets TASK_INTERRUPTIBLE, can be interrupted by signal.
wait_event_interruptible_timeout(wq, condition, timeout)As before, the process sets TASK_INTERRUPTIBLE, can be interrupted by signal.

3, Driver

The main program idea is as follows: after calling the read function, define a waiting queue. If the flag bit pressed by the key is zero at this time, add the waiting queue to the queue header, set the task state (sleep), and switch tasks: schedule (). Now the program card is here!
When the process is awakened in the interrupt program, the program will continue to execute. Next, it is necessary to judge whether the key press flag is true. If so, the return value will be returned. And remove the waiting queue from the queue header.
Some backbone codes are retrieved as follows:

struct imx6uirq_dev {
    dev_t devid;
    wait_queue_head_t  r_wait;  //Define queue header
void timer_function(unsigned long arg)

	/* Wake up process */
	if(atomic_read(&dev->releasekey)) {	/* Complete a key press process */
		/* wake_up(&dev->r_wait); */
		wake_up_interruptible(&dev->r_wait);  //Wake up process
static int keyio_init(void)
	/* Create timer */
     imx6uirq.timer.function = timer_function;

	/* Initialize wait queue header */
	return 0;
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
	int ret = 0;
	unsigned char keyvalue = 0;
	unsigned char releasekey = 0;
	struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

	DECLARE_WAITQUEUE(wait, current);	/* Define a waiting queue */
	if(atomic_read(&dev->releasekey) == 0) {	/* No key pressed */
		add_wait_queue(&dev->r_wait, &wait);	/* Add wait queue to wait queue header */
		__set_current_state(TASK_INTERRUPTIBLE);/* Set task status */
		schedule();							/* Make a task switch */
		if(signal_pending(current))	{			/* Judge whether it is wake-up caused by signal */
			ret = -ERESTARTSYS;
			goto wait_error;
		__set_current_state(TASK_RUNNING);      /* Set the current task to run */
	    remove_wait_queue(&dev->r_wait, &wait);    /* Delete the corresponding queue item from the waiting queue header */

	keyvalue = atomic_read(&dev->keyvalue);
	releasekey = atomic_read(&dev->releasekey);

	if (releasekey) { /* A key is pressed */	
		if (keyvalue & 0x80) {
			keyvalue &= ~0x80;
			ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
		} else {
			goto data_error;
		atomic_set(&dev->releasekey, 0);/* Press the flag to clear */
	} else {
		goto data_error;
	return 0;

	set_current_state(TASK_RUNNING);		/* Set the task to running status */
	remove_wait_queue(&dev->r_wait, &wait);	/* Remove waiting queue */
	return ret;

	return -EINVAL;

4, Compile test

Same as before.

Tags: Linux Linux Driver

Posted on Thu, 04 Nov 2021 11:18:59 -0400 by skattabrain