Linux driven learning record-9. Concurrency and competition experiment

The experiment in this chapter is modified from the code of "led driver of device tree" in Chapter 6, and the device tree does not need to be changed.

1, Atomic operation

1. Driver

Only one led can be operated at a time. When the driver is operating the led, other drivers cannot access the led.
Idea: define an atomic shaping variable in the device structure and initialize the entry function to 1. When using open to open the driver, first check whether the value of the atomic variable is 1. If yes, set the private data together with the subtraction to ensure the normal operation of the driver. If it is not 1, it means that the driver is occupied, the APP file is returned, and opening the driver fails. In the close device function release, add one to release the atomic variable.
The following procedure is modified from gpioled.c, the capture part.

/* gpioled Equipment structure */
struct gpioled_dev{
	dev_t devid;			/* Equipment number 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* class 		*/
	struct device *device;	/* equipment 	 */
	int major;				/* Main equipment No	  */
	int minor;				/* Secondary equipment No   */
	struct device_node	*nd; /* Device node */
	int led_gpio;			/* led GPIO number used		*/
	atomic_t num;           /*Atomic shaping variable*/
};
struct gpioled_dev gpioled;
/*The pointer and structure are described here
open The function input parameter filp is a struct file type pointer, that is, the address of the file structure.
In addition, if a is a structure address and b is an integer structure variable, there are:
a->b Equivalent to (* a).b, and a - > b is an integer number, which is an entity rather than a pointer;
'->'The left must be a pointer and the '.' must be preceded by an entity. After exporting b, the whole is the type of b.
Back to the open function, the atomic function inputs are the addresses (pointers) of atomic variables. Gpioled is the defined structure entity. Here, use gpioled.num to get the atomic variable, and add & to get the address of the atomic variable.
file The structure has a variable private_data is also a structure pointer. The pointer filp uses' - > 'to obtain private_data (structure pointer type) so that it is equal to the address of gpoiled.
*/
static int led_open(struct inode *inode, struct file *filp)
{
	if(atomic_dec_and_test(&gpioled.num)){ /*If the self subtracting one is 0, it turns out to be 1, which is operable*/
		filp->private_data = &gpioled; /* Set private data */
		return 0;
	}
	else{/*Otherwise, minus one is 0, you need to add one to return a negative value, and the driver is not turned on*/
		atomic_inc(&gpioled.num);
		return -1;
	}
}
/*
Here we first define gpioled_ The dev structure pointer to the variable dev to make it equal to private_data (pointer). The input of atomic function is the address. First get the atomic variable with dev - > and then add & to get the address.
*/
static int led_release(struct inode *inode, struct file *filp)
{
	struct gpioled_dev *dev = filp->private_data;
	atomic_inc(&dev->num);
	return 0;
}

static int __init led_init(void)
{
	atomic_set(&gpioled.num, 1);
	/* Set the GPIO used by the LED */
	
	/* Register character device driver */
	return 0;
}

2. Test

int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	unsigned char databuf[1];

	filename = argv[1];

	/* Turn on the led driver */
	fd = open(filename, O_RDWR);

	databuf[0] = atoi(argv[2]);	/* What to do: turn on or off */

	/* Write data to / dev/led file */
	retvalue = write(fd, databuf, sizeof(databuf));
	
	while(1){
		sleep(5);//Delay 5s
		cnt++;
		printf("App running times:%d\r\n", cnt);
		if(cnt >= 5) break; //5 times 25s
	}
	retvalue = close(fd); /* Close file */

	return 0;
}

When the driver card is in while, it is not close d, and the atomic variable is still 0. Execute APP again, and the driver cannot be opened.

2, Spin lock experiment

1. Driver

The critical area of spin lock protection is as short as possible, so it is not advisable to lock in the open function and unlock in the release function. Idea: apply for a spin lock flag bit of 1, judge whether the flag is 0 in the open function, if so, return a negative value, and the opening fails. If it is 1, it will be locked, and the flag will decrease by 1 to unlock. Then set private data. The release function locks, and the flag automatically adds 1 to unlock.

/* gpioled Equipment structure */
struct gpioled_dev{
	/**/
	spinlock_t spinlock;	/* spinlock*/
	int flag;               /*Spin lock state*/
};
struct gpioled_dev gpioled;	/* led equipment */
static int led_open(struct inode *inode, struct file *filp)
{
	unsigned long flag;
	spin_lock_irqsave(&gpioled.spinlock, flag);
	if(gpioled.flag == 0) {
		spin_unlock_irqsave(&gpioled.spinlock, flag);
		return -1;
	}
	gpioled.flag --;
	spin_unlock_irqsave(&gpioled.spinlock, flag);
	filp->private_data = &gpioled;
	return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
	spin_lock_irqsave(&gpioled.lock, flag);
	gpioled.flag = 1;
	spin_unlock_irqsave(&gpioled.spinlock, flag);
	return 0;
}
static int __init led_init(void)
{
	int ret = 0;
	unsigned long flag;
	spin_lock_init(&gpioled.lock);
	spin_lock_irqsave(&gpioled.lock, flag);
	gpioled.flag = 1;
	spin_unlock_irqsave(&gpioled.spinlock, flag);
	/* Set the GPIO used by the LED */

	/* Register character device driver */

	return 0;
}

2. Test

As in the previous section.

3, Semaphore

1. Driver

Semaphores cause sleep, so the critical area of semaphore protection has no time limit. You can apply for semaphores in the open function and release semaphores in the release function.

#include <linux/semaphore.h>
struct gpioled_dev{
	/**/
	struct semaphore sema;	/* semaphore*/
};

struct gpioled_dev gpioled;	/* led equipment */
static int led_open(struct inode *inode, struct file *filp)
{
	if(down_trylock(&gpioled.sema))
		return -1;
	filp->private_data = &gpioled;
	return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
	struct gpioled_dev *dev = filp->private_data
	up(&dev->sema);
	return 0;
}
static int __init led_init(void)
{
	int ret = 0;
	sema_init(&gpioled.sema, 1);
	/* Set the GPIO used by the LED */

	/* Register character device driver */
	return 0;
}

2. Test

Same as last section.

4, Mutex experiment

1. Driver

In the semaphore experiment in the previous section, set the semaphore to 1, which is equivalent to a mutex. For mutex, it is recommended to use a special mutex.

/* gpioled Equipment structure */
struct gpioled_dev{
	/**/
	struct mutex mutex;	
};

struct gpioled_dev gpioled;	/* led equipment */
static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &gpioled;
	/*Gets a mutex that can be interrupted by a signal*/
	if(mutex_lock_interruptible(&gpioled.mutex))
		return -1;
	return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
	struct gpioled_dev *dev = filp->private_data
	mutex_unlock(&dev.mutex);
	return 0;
}
static int __init led_init(void)
{
	int ret = 0;
	mutex_init(&gpioled.mutex);
	/* Set the GPIO used by the LED */

	/* Register character device driver */
	return 0;
}

2. Test

As in the previous section.

5, Summary

Atomic operations can only be used for shaping, so spin locks and semaphores are widely used.
Spin locking will lead to dead cycle. Blocking is not allowed during locking, so the critical area is required to be small. The semaphore allows a large critical period. Mutexes are special cases where the semaphore is 1.
Read / write spinlocks and read / write semaphores are conditional relaxed spinlocks and semaphores. They allow multiple execution units to read concurrently from shared resources, but cannot write concurrently.

Tags: Linux Linux Driver

Posted on Sun, 24 Oct 2021 15:50:57 -0400 by empnorton