10. Concurrency: Hardware synchronization primitives

Table of Contents

Concurrency

source

Adverse effects on the program

avoid concurrency

Atomic integer operation interface

Atomic integer variable

ATOMIC_INIT() macro

atomic_set()

atomic_read()

atomic_add()/atomic_sub()

atomic_inc() / atomic_dec()

Bit atomic operation function

set_bit()

clear_bit()

change_bit()

Experimental session

dts_led.c file

app.c file

Makefile

Implementation process


Concurrency

root

Multi-thread, multi-process scheduling

various interruptions

Adverse impact on the program

Tampering with shared data

Incomplete action

Synchronization, deadlock, data competition, system scheduling overhead, etc.

Avoid concurrency

Hardware synchronization primitives: A set of atomic operations provided by computer hardware, which are indivisible and avoid parallel operation execution errors.

Atomic Integer Operation Interface

Atomic integer variable

typedef struct
{
int counter;
} atomic_t;

ATOMIC_INIT() macro

//Define an atomic integer variable and initialize the assignment
atomic_t data = ATOMIC_INIT(int i);

atomic_set()

//Used to set the value of integer atomic variable
atomic_set(atomic_t *v, int i);

atomic_read()

//Get the value of the atomic variable
int atomic_read(atomic_t *v);

atomic_add()/atomic_sub()

//Add i to integer atomic variable
static inline void atomic_add(int i, atomic_t *v);
// Subtract i from the integer atomic variable
static inline void atomic_sub(int i, atomic_t *v); 

atomic_inc() / atomic_dec()

//Integer atomic variable increases by 1
static inline void atomic_inc(atomic_t *v);
//Integer atomic variable decreases by 1
static inline void atomic_dec(atomic_t *v);

Bit atomic operation function

set_bit()

/*
 * Set a certain position to 1
 * nr: Which position of the operand is to be used?
 * addr: the address of the number to be operated on
 */
set_bit(int nr, unsigned long *addr);

clear_bit()

/*
 * Set a certain position to 0
 * nr: Which position of the operand is to be used?
 * addr: the address of the number to be operated on
 */
clear_bit(int nr, unsigned long *addr);

change_bit()

/*
 * Reverse a bit
 * nr: Which position of the operand is to be used?
 * addr: the address of the number to be operated on
 */
int test_bit(int nr, unsigned long *addr);

Experimental session

dts_led.c file

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#include <asm/mach/map.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>

#define DEV_NAME "rgb_led"
#define DEV_CNT (1)

int rgb_led_red;
int rgb_led_green;
int rgb_led_blue;

static atomic_t test_atomic = ATOMIC_INIT(1);

/* Define the device number of the character device */
static dev_t led_devno;
/* Define character device structure */
static struct cdev led_chrdev;
/* Save the created class */
struct class *class_led;
/* Save the created device */
struct device *device;
/* rgb_led device tree node structure */
struct device_node *rgb_led_device_node;

static int led_chrdev_open(struct inode *inode, struct file *filp)
{
        if(atomic_read( & amp;test_atomic)){
                atomic_set( & amp;test_atomic, 0);
        }else{
                printk("driver on using! open failed!!!\
");
                return -EBUSY;
        }
        printk("open form driver\
");
        return 0;
}

static ssize_t led_chrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
        int ret, error;
        unsigned char receive_data[10]; //Used to save received data
        unsigned int write_data = 0;

        if(cnt > 10) cnt = 10;

        error = copy_from_user(receive_data, buf, cnt);
        if(error < 0) return -1;

        ret = kstrtoint(receive_data, 16, & amp;write_data);
        if(ret) return -1;

        /* Set GPIO1_04 output level */
        if(write_data & 0x04){
                gpio_set_value(rgb_led_red, 0);
        }else{
                gpio_set_value(rgb_led_red, 1);
        }

        /* Set GPIO4_20 output level */
        if(write_data & 0x02){
                gpio_set_value(rgb_led_green, 0);
        }else{
                gpio_set_value(rgb_led_green, 1);
        }

/* Set GPIO4_19 output level */
        if(write_data & 0x01){
                gpio_set_value(rgb_led_blue, 0);
        }else{
                gpio_set_value(rgb_led_blue, 1);
        }

        return cnt;
}

static int led_chrdev_release(struct inode *inode, struct file *filp)
{
        atomic_set( & amp;test_atomic, 1);
        printk(KERN_ALERT "finished!!!\
");

        return 0;
}

static struct file_operations led_chrdev_fops = {
        .owner = THIS_MODULE,
        .open = led_chrdev_open,
        .write = led_chrdev_write,
        .release = led_chrdev_release,
};

static int led_probe(struct platform_device *pdv)
{
        int ret = -1; //Save error status code
        unsigned int register_data = 0;

        printk(KERN_ALERT "match successful!\
");

        /* Get the device tree node of rgb_led */
        rgb_led_device_node = of_find_node_by_path("/rgb_led");
        if(rgb_led_device_node == NULL){
                printk(KERN_ERR "get rgb_led failed!\
");
                return -1;
        }

        /* Get the red led GPIO pin number */
        rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
        if(rgb_led_red < 0){
                printk(KERN_ERR "rgb_led_red failed!\
");
                return -1;
        }

/* Get the green led GPIO pin number */
        rgb_led_green = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
        if(rgb_led_green < 0){
                printk(KERN_ERR "rgb_led_green failed!\
");
                return -1;
        }

        /* Get the blue led GPIO pin number */
        rgb_led_blue = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
        if(rgb_led_blue < 0){
                printk(KERN_ERR "rgb_led_blue failed!\
");
                return -1;
        }

        /* Set GPIO to output mode and default to high level */
        gpio_direction_output(rgb_led_red, 1);
        gpio_direction_output(rgb_led_green, 1);
        gpio_direction_output(rgb_led_blue, 1);

        /* first step
         * Use dynamic allocation to obtain the device number, the secondary device number is 0
         * The device name is rgb-leds, which can be viewed through the command cat /proc/devices
         * DEV_CNT is 1, currently only one device number is applied for
         */
        ret = alloc_chrdev_region( & amp;led_devno, 0, DEV_CNT, DEV_NAME);
        if(ret < 0){
                printk("fail to alloc led_devno\
");
                goto alloc_err;
        }

        /* second step
         * Associated character device structure cdev and file operation structure file_operations
         */
        led_chrdev.owner = THIS_MODULE;
        cdev_init( & amp;led_chrdev, & amp;led_chrdev_fops);

        /* third step
         * Add device to cdev_map hash table
         */
        ret = cdev_add( & amp;led_chrdev, led_devno, DEV_CNT);
        if(ret < 0){
                printk("fail to add cdev\
");
                goto add_err;
        }

/* Step 4: Create class */
        class_led = class_create(THIS_MODULE, DEV_NAME);

        /* Step 5: Create device */
        device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);

        return 0;

alloc_err:
        return -1;
add_err:
        //When adding a device fails, you need to log out the device number
        unregister_chrdev_region(led_devno, DEV_CNT);
        printk("error!\
");
}

static const struct of_device_id rgb_led[] = {
        {.compatible = "fire,rgb_led"},
        {/* sentinel */}
};

/* Define platform device structure */
struct platform_driver led_platform_driver = {
        .probe = led_probe,
        .driver = {
                .name = "rgb-leds-platform",
                .owner = THIS_MODULE,
                .of_match_table = rgb_led,
        }
};

static int __init led_platform_driver_init(void)
{
        int DriverState;

        DriverState = platform_driver_register( & amp;led_platform_driver);
        printk(KERN_ALERT "DriverState is %d\
", DriverState);

        return 0;
}

static void __exit led_platform_driver_exit(void){
        /* Destroy device */
        device_destroy(class_led, led_devno);
        /* Delete device number */
        cdev_del( & amp;led_chrdev);
        /* Unregister character device */
        unregister_chrdev_region(led_devno, DEV_CNT);
        /* Destroy class */
        class_destroy(class_led);

        platform_driver_unregister( & amp;led_platform_driver);

        printk(KERN_ALERT "led_platform_driver exit\
");
}

module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");

app.c file

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
        if(argc != 2){
                printf("commend error!\
");
                return -1;
        }

        int fd = open("/dev/rgb_led", O_RDWR);
        if(fd < 0){
                printf("open file:/dev/rgb_led failed!!!\
");
                return -1;
        }

        int error = write(fd, argv[1], sizeof(argv[1]));
        if(error < 0){
                printf("write file error!\
");
                close(fd);
        }

        sleep(10);

        error = close(fd);
        if(error < 0){
                printf("close file error!\
");
        }

        return 0;
}

Makefile

KERNEL_DIR=/home/couvrir/Desktop/ebf_linux_kernel_6ull_depth1/build_image/build
  
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH CROSS_COMPILE

obj-m:=dts_led.o
all:app
        $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules

app:
        arm-linux-gnueabihf-gcc app.c -o App

.PHONY:clean copy
clean:
        $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
        sudo rm /home/couvrir/desktop/sharedir/*.ko App
copy:
        sudo cp *.ko App /home/couvrir/Desktop/sharedir

Execution process

virtual machine:

Execute make and make copy. Generate .ko file.

Development board (executed in the mounting directory):

sudo insmod dts_led.ko

ls /dev/rgb_led

sudo /mnt/App 1 &

sudo /mnt/App 2

sudo /mnt/App 1 &

sudo /mnt/App 2

sudo rmmod dts_led.ko

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. CS entry skill treeLinux introductionFirst introduction to Linux 34948 people are learning the system