linux driver - atomic operations

linux driver - atomic operations

Atomic operations API

The Linux kernel defines a structure called atomic_t to complete the original operation of integer data. In use, atomic variables are used instead of integer variables. This structure is defined in the include/linux/types.h file and is defined as follows:

typedef struct {
    
    
    int counter;
}atomic_t;

If you want to use the atomic operation API function, you need to first define an atomic_t variable, as shown below:

atomic_t value;  /* 定义一个原子变量 value */

You can also assign an initial value to the atomic variable when defining it, as shown below

atomic_t value = ATOMIC_INIT(0) /* 定义原子变量 value 并赋初值为 0*/

The API for atomic variable operations provided by the Linux kernel is as follows:

function describe
ATOMIC_INIT(int i) Initialize when defining atomic variables
int atomic_read(atomic_t *v) Read the value of atomic variable v and return
void atomic_set(atomic_t *v, int i) Write i value to v
void atomic_add(int i, atomic_t *v) add i value to v
void atomic_sub(int i, atomic_t *v) Subtract the value of i from v
void atomic_inc(atomic_t *v) self-increasing
void atomic_dec(atomic_t *v) Decrease
int atomic_dec_return(atomic_t *v) Decrement and return the value of v
int atomic_inc_return(atomic_t *v) Increment and return the value of v
int atomic_sub_and_test(int i, atomic_t *v) Subtract i from v, returning true if the result is 0, false otherwise
int atomic_dec_and_test(atomic_t *v) Subtract 1 from v, returning true if the result is 0, false otherwise
int atomic_inc_and_test(atomic_t *v) Add 1 to v and return true if the result is 0, otherwise return false
int atomic_inc_and_test(atomic_t *v) Add 1 to v and return true if the result is 0, otherwise return false
int atomic_add_negative(int i, atomic_t *v) Add i to v and return true if the result is negative, false otherwise

If you use a 64-bit SOC, you must use 64-bit atomic variables. The Linux kernel also defines a 64-bit atomic structure, as shown below:

#ifdef CONFIG_64BIT
typedef struct {
    
    
	s64 counter;
} atomic64_t;
#endif
typedef __s64 s64;
__extension__ typedef __signed__ long long __s64;

Correspondingly, API functions for operating 64-bit atomic variables are also provided. The usage is the same as in the above table, except that the atomic_ prefix is ​​replaced by atomic64_ and int is replaced by long long. If you are using a 64-bit SOC, you must use 64-bit atomic operation functions.

Examples of using atomic variables are as follows:

atomic_t v = ATOMIC_INIT(0); /* 定义并初始化原子变量 v = 0 */
atomic_set(&v, 10); /* 设置 v = 10 */
atomic_read(&v); /* 读取 v 的值,肯定是 10 */
atomic_inc(&v); /* v 的值加 1,v = 11 */

Atomic bit manipulation API

Bit operations are also very common operations. The Linux kernel also provides a series of atomic bit operation API functions. However, atomic bit operations do not have an atomic_t data structure like atomic integer variables. Atomic bit operations directly operate on memory. The API function is shown below:

function describe
void set_bit(int nr, void *p) Set the nrth position of p address to 1
void clear_bit(int nr,void *p) Clear bit nr of address p
void change_bit(int nr, void *p) Flip the nrth bit of address p
int test_bit(int nr, void *p) Get the value of the nrth bit of p address
int test_and_set_bit(int nr, void *p) Set the nr-th bit of p address to 1 and return the original value of nr-bit
int test_and_clear_bit(int nr, void *p) Clear the nrth bit of p address and return the original value of nr bit
int test_and_change_bit(int nr, void *p) Flip the nrth bit of address p and return the original value of nr bits

Atomic operation driver

#include "linux/device/class.h"
#include "linux/export.h"
#include "linux/uaccess.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

#define CHRDEVBASE_NAME "chrdev_atom" /* 设备名 */
#define CHRDEVBASE_NUM 1              /* 设备数目 */

static char write_buf[100];
static char read_buf[100];

static char *string_test = "kernel data this tyustli test";

typedef struct {
    
    
    dev_t dev_id;          /* 设备号 */
    struct cdev c_dev;     /* cdev */
    struct class *class;   /* 类 */
    struct device *device; /* 设备 */
    int major;             /* 主设备号 */
    int minor;             /* 次设备号 */
    atomic_t lock;         /* 原子锁 */
} new_chrdev_t;

new_chrdev_t new_chrdev;

static int chrdevbase_open(struct inode *inode, struct file *file)
{
    
    
    /* 通过判断原子变量的值来检查当前驱动有没有被别的应用使用 */
    if (!atomic_dec_and_test(&new_chrdev.lock)) {
    
    
        atomic_inc(&new_chrdev.lock); /* 小于 0 的话就加 1,使其原子变量等于 0 */
        return -EBUSY;                /* 驱动被使用,返回忙 */
    }

    printk("k: chrdevbase open\r\n");

    return 0;
}

static ssize_t chrdevbase_read(struct file *file, char __user *buf,
                               size_t count, loff_t *ppos)
{
    
    
    unsigned long ret = 0;

    printk("k: chrdevbase read\r\n");
    memcpy(read_buf, string_test, strlen(string_test));

    ret = copy_to_user(buf, read_buf, count);
    if (ret == 0) {
    
    
        printk("k: read data success\r\n");
    } else {
    
    
        printk("k: read data failed ret = %ld\r\n", ret);
    }

    return ret;
}

static ssize_t chrdevbase_write(struct file *file, const char __user *buf,
                                size_t count, loff_t *ppos)
{
    
    
    unsigned long ret = 0;

    printk("k: chrdevbase write\r\n");

    ret = copy_from_user(write_buf, buf, count);
    if (ret == 0) {
    
    
        printk("k: write data success write data is: %s\r\n", write_buf);
    } else {
    
    
        printk("k: write data failed ret = %ld\r\n", ret);
    }

    return count;
}

static int chrdevbase_release(struct inode *inode, struct file *file)
{
    
    
    /* 关闭驱动文件的时候释放原子变量 */
    atomic_inc(&new_chrdev.lock);

    printk("k: chrdevbase release\r\n");

    return 0;
}

static struct file_operations chrdevbase_fops = {
    
    
    .owner = THIS_MODULE,
    .open = chrdevbase_open,
    .read = chrdevbase_read,
    .write = chrdevbase_write,
    .release = chrdevbase_release,
};

static int __init chrdevbase_init(void)
{
    
    
    int err = 0;

    atomic_set(&new_chrdev.lock, 1); /* 原子变量初始值为1 */

    err = alloc_chrdev_region(&new_chrdev.dev_id, 0, CHRDEVBASE_NUM,
                              CHRDEVBASE_NAME);
    if (err < 0) {
    
    
        printk("k: alloc chrdev region failed err = %d\r\n", err);
        return -1;
    }

    /* get major and minor */
    new_chrdev.major = MAJOR(new_chrdev.dev_id);
    new_chrdev.minor = MINOR(new_chrdev.dev_id);
    printk("k: newcheled major=%d,minor=%d\r\n", new_chrdev.major,
           new_chrdev.minor);

    new_chrdev.c_dev.owner = THIS_MODULE;
    cdev_init(&new_chrdev.c_dev, &chrdevbase_fops);
    err = cdev_add(&new_chrdev.c_dev, new_chrdev.dev_id, CHRDEVBASE_NUM);
    if (err < 0) {
    
    
        printk("k: cdev add failed err = %d\r\n", err);
        goto out;
    }

    new_chrdev.class = class_create(CHRDEVBASE_NAME);
    if (IS_ERR(new_chrdev.class)) {
    
    
        printk("k: class create failed\r\n");
        goto out_cdev;
    }

    new_chrdev.device = device_create(new_chrdev.class, NULL, new_chrdev.dev_id,
                                      NULL, CHRDEVBASE_NAME);
    if (IS_ERR(new_chrdev.device)) {
    
    
        printk("k: device create failed\r\n");
        goto out_class;
    }

    printk("k: base module init\r\n");

    return 0;

out_class:
    class_destroy(new_chrdev.class);
out_cdev:
    cdev_del(&new_chrdev.c_dev);
out:
    unregister_chrdev_region(new_chrdev.dev_id, CHRDEVBASE_NUM);

    return err;
}

static void __exit chrdevbase_exit(void)
{
    
    
    device_destroy(new_chrdev.class, new_chrdev.dev_id);
    class_destroy(new_chrdev.class);
    cdev_del(&new_chrdev.c_dev);
    unregister_chrdev_region(new_chrdev.dev_id, CHRDEVBASE_NUM);

    printk("k: base module exit!\r\n");
}

module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */

Atomic operation APP

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

static char usrdata[] = {
    
     "user data!" };

int main(int argc, char *argv[])
{
    
    
    int fd, retvalue;
    char *filename;
    unsigned char run_cnt = 0;
    char readbuf[100], writebuf[100];

    if (argc != 3) {
    
    
        printf("u: error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if (fd < 0) {
    
    
        printf("u: can't open file %s\r\n", filename);
        return -1;
    }

    /* 从驱动文件读取数据 */
    if (atoi(argv[2]) == 1) {
    
    
        retvalue = read(fd, readbuf, 50);
        if (retvalue < 0) {
    
    
            printf("u: read file %s failed!\r\n", filename);
        } else {
    
    
            /*  读取成功,打印出读取成功的数据 */
            printf("u: read data:%s\r\n", readbuf);
        }
    }

    /* 模拟占用驱动 25s,此时另一个线程去打开驱动 */
    while (1) {
    
    
        sleep(5);
        run_cnt++;
        if (run_cnt >= 5)
            break;
    }

    /* 向设备驱动写数据 */
    if (atoi(argv[2]) == 2) {
    
    
        memcpy(writebuf, usrdata, sizeof(usrdata));
        retvalue = write(fd, writebuf, 50);
        if (retvalue < 0) {
    
    
            printf("u: write file %s failed!\r\n", filename);
        }
    }

    /* 关闭设备 */
    retvalue = close(fd);
    if (retvalue < 0) {
    
    
        printf("u: can't close file %s\r\n", filename);
        return -1;
    }

    return 0;
}

Module installation

modprobe my_module

module run

/lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
/lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &

result

~ # /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
~ # k: chrdevbase open
k: chrdevbase read
k: read data success
u: read data:kernel data this tyustli test
~ # /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
~ # u: can't open file /dev/chrdev_atom

[2]+  Done(255)                  /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1
~ # k: chrdevbase release

[1]+  Done                       /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1

Guess you like

Origin blog.csdn.net/tyustli/article/details/134563156