一、核心数据结构
typedef struct {
int counter;
} atomic_t;
#ifdef CONFIG_64BIT
typedef struct {
long counter;
} atomic64_t;
#endif
二、Types of atomic variables
Two different atomic variables are there.
- Atomic variables that operates on Integers
- Atomic variables that operates on Individual Bits
三、Atomic Integer Operations
atomic64_t etx_global_variable = ATOMIC64_INIT(0); long atomic64_read(atomic64_t *v); void atomic64_set(atomic64_t *v, int i); void atomic64_add(int i, atomic64_t *v); void atomic64_sub(int i, atomic64_t *v); void atomic64_inc(atomic64_t *v); void atomic64_dec(atomic64_t *v); int atomic64_sub_and_test(int i, atomic64_t *v); int atomic64_add_negative(int i, atomic64_t *v); long atomic64_add_return(int i, atomic64_t *v); long atomic64_sub_return(int i, atomic64_t *v); long atomic64_inc_return(int i, atomic64_t *v); long atomic64_dec_return(int i, atomic64_t *v); int atomic64_dec_and_test(atomic64_t *v); int atomic64_inc_and_test(atomic64_t *v);
32位版本直接去掉函数名/参数类型中的64.
四、Atomic Bitwise Operations
Function | Description |
void set_bit(int nr, void *addr) | Atomically set the nr -th bit starting from addr |
void clear_bit(int nr, void *addr) | Atomically clear the nr -th bit starting from addr |
void change_bit(int nr, void *addr) | Atomically flip the value of the nr -th bit starting from addr |
int test_and_set_bit(int nr, void *addr) | Atomically set the nr -th bit starting from addr and return the previous value |
int test_and_clear_bit(int nr, void *addr) | Atomically clear the nr -th bit starting from addr and return the previous value |
int test_and_change_bit(int nr, void *addr) | Atomically flip the nr -th bit starting from addr and return the previous value |
int test_bit(int nr, void *addr) | Atomically return the value of the nr -th bit starting from addr |
int find_first_zero_bit(unsigned long *addr, unsigned int size) | Atomically returns the bit-number of the first zero bit, not the number of the byte containing a bit |
int find_first_bit(unsigned long *addr, unsigned int size) | Atomically returns the bit-number of the first set bit, not the number of the byte containing a bit |
补充知识:
And also non-atomic bit operations also available. What is the use of that when we have atomic bit operations? When we have code that is already locked by mutex/spinlock then we can go for this non-atomic version. This might be faster in that case. The below functions are available for non-atomic bit operations.
Function | Description |
void _set_bit(int nr, void *addr) | Non-atomically set the nr -th bit starting from addr |
void _clear_bit(int nr, void *addr) | Non-atomically clear the nr -th bit starting from addr |
void _change_bit(int nr, void *addr) | Non-atomically flip the value of the nr -th bit starting from addr |
int _test_and_set_bit(int nr, void *addr) | Non-atomically set the nr -th bit starting from addr and return the previous value |
int _test_and_clear_bit(int nr, void *addr) | Non-atomically clear the nr -th bit starting from addr and return the previous value |
int _test_and_change_bit(int nr, void *addr) | Non-atomically flip the nr -th bit starting from addr and return the previous value |
int _test_bit(int nr, void *addr) | Non-atomically return the value of the nr -th bit starting from addr |
五、参考代码
/***************************************************************************//**
* \file driver.c
*
* \details Simple Linux device driver (Atomic Variables)
*
* \Tested with Linux raspberrypi 5.10.27-v7l-embetronicx-custom+
*
*******************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h> //kmalloc()
#include<linux/uaccess.h> //copy_to/from_user()
#include <linux/kthread.h> //kernel threads
#include <linux/sched.h> //task_struct
#include <linux/delay.h>
atomic_t etx_global_variable = ATOMIC_INIT(0); //Atomic integer variable
unsigned int etc_bit_check = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static struct task_struct *etx_thread1;
static struct task_struct *etx_thread2;
/*************** Driver functions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp,
const char *buf, size_t len, loff_t * off);
/******************************************************/
int thread_function1(void *pv);
int thread_function2(void *pv);
/*
** kernel thread function 2
*/
int thread_function1(void *pv)
{
unsigned int prev_value = 0;
while(!kthread_should_stop()) {
atomic_inc(&etx_global_variable);
prev_value = test_and_change_bit(1, (void*)&etc_bit_check);
pr_info("Function1 [value : %u] [bit:%u]\n", atomic_read(&etx_global_variable), prev_value);
msleep(1000);
}
return 0;
}
/*
** kernel thread function 2
*/
int thread_function2(void *pv)
{
unsigned int prev_value = 0;
while(!kthread_should_stop()) {
atomic_inc(&etx_global_variable);
prev_value = test_and_change_bit(1,(void*) &etc_bit_check);
pr_info("Function2 [value : %u] [bit:%u]\n", atomic_read(&etx_global_variable), prev_value);
msleep(1000);
}
return 0;
}
//File operation structure
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.release = etx_release,
};
/*
** This function will be called when we open the Device file
*/
static int etx_open(struct inode *inode, struct file *file)
{
pr_info("Device File Opened...!!!\n");
return 0;
}
/*
** This function will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{
pr_info("Device File Closed...!!!\n");
return 0;
}
/*
** This function will be called when we read the Device file
*/
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off)
{
pr_info("Read function\n");
return 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t etx_write(struct file *filp,
const char __user *buf, size_t len, loff_t *off)
{
pr_info("Write Function\n");
return len;
}
/*
** Module Init function
*/
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
pr_info("Cannot allocate major number\n");
return -1;
}
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
pr_info("Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
pr_info("Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
pr_info("Cannot create the Device \n");
goto r_device;
}
/* Creating Thread 1 */
etx_thread1 = kthread_run(thread_function1,NULL,"eTx Thread1");
if(etx_thread1) {
pr_err("Kthread1 Created Successfully...\n");
} else {
pr_err("Cannot create kthread1\n");
goto r_device;
}
/* Creating Thread 2 */
etx_thread2 = kthread_run(thread_function2,NULL,"eTx Thread2");
if(etx_thread2) {
pr_err("Kthread2 Created Successfully...\n");
} else {
pr_err("Cannot create kthread2\n");
goto r_device;
}
pr_info("Device Driver Insert...Done!!!\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
cdev_del(&etx_cdev);
return -1;
}
/*
** Module exit function
*/
static void __exit etx_driver_exit(void)
{
kthread_stop(etx_thread1);
kthread_stop(etx_thread2);
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
pr_info("Device Driver Remove...Done!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple device driver - Atomic Variables");
MODULE_VERSION("1.27");