当前位置: 首页 > news >正文

Linux内核并发与竞争-原子操作

一.原子操作的概念

首先看一下原子操作,原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作。假如现在要对无符号整形变量 a 赋值,值为 3,对于 C 语言来讲很简单,直接就是: a=3

但是 C 语言要先编译为成汇编指令, ARM 架构不支持直接对寄存器进行读写操作,比如要借助寄存器 R0、 R1 等来完成赋值操作。假设变量 a 的地址为 0X3000000,“a=3”这一行 C语言可能会被编译为如下所示的汇编代码:

ldr r0, =0X30000000 /* 变量 a 地址 */

ldr r1, = 3 /* 要写入的值 */

str r1, [r0] /* 将 3 写入到 a 变量中 */

示例代码 只是一个简单的举例说明,实际的结果要比示例代码复杂的多。从上述代码可以看出, C 语言里面简简单单的一句“a=3”,编译成汇编文件以后变成了 3 句,那么程序在执行的时候肯定是按照示例代码 中的汇编语句一条一条的执行。假设现在线程 A要向 a 变量写入 10 这个值,而线程 B 也要向 a 变量写入 20 这个值,我们理想中的执行顺序如图 所示:

按照图所示的流程,确实可以实现线程 A 将 a 变量设置为 10,线程 B 将 a 变量设置为 20。但是实际上的执行流程可能如下图所示:

按照图 所示的流程,线程 A 最终将变量 a 设置为了 20,而并不是要求的 10!线程B 没有问题。这就是一个最简单的设置变量值的并发与竞争的例子,要解决这个问题就要保证示例代码 中的三行汇编指令作为一个整体运行,也就是作为一个原子存在。 Linux 内核提供了一组原子操作 API 函数来完成此功能, Linux 内核提供了两组原子操作 API 函数,一组是对整形变量进行操作的,一组是对位进行操作的,我们接下来看一下这些 API 函数。

二.原子操作的函数介绍

1.原子整形操作API函数

Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作,在使用中用原子变量来代替整形变量,此结构体定义在 include/linux/types.h 文件中,定义如下:

typedef struct {
    int counter;
} atomic_t;

2.原子位操作API函数

三.实验

我们这个还是之前的思路,如果不牵扯到硬件操作,那么我们就在ubuntu pc做实验,这样高效,比较方便

driver

#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/device.h>



#define CHRDEVBASE_MAJOR    200
uint8_t kernel_buffer[1024] = {0};
static struct class *hello_class;
atomic_t lock;

static int hello_world_open(struct inode * inode, struct file * file)
{
    int count;
    printk("hello_world_open\r\n");
    
    count = atomic_read(&lock);
    printk("count:%d\r\n",count);
    if(count)
    {
        printk("already open ,return fail\r\n");
    }
    else
        atomic_inc(&lock);
    
    return 0;
}

static int hello_world_release (struct inode * inode, struct file * file)
{
    printk("hello_world_release\r\n");
    atomic_dec(&lock);
    return 0;
}


static const struct file_operations hello_world_fops = {
    .owner        = THIS_MODULE,
    .open        = hello_world_open,
    .release = hello_world_release,
    .read        = NULL,
    .write    = NULL,
};


static int __init hello_driver_init(void)
{
    int ret;
    printk("hello_driver_init\r\n");
    
    atomic_set(&lock,0);
    

    ret = register_chrdev(CHRDEVBASE_MAJOR,"hello_driver",&hello_world_fops);

    hello_class = class_create(THIS_MODULE,"hello_class");

    device_create(hello_class,NULL,MKDEV(CHRDEVBASE_MAJOR,0),NULL,"hello"); /* /dev/hello */

    return 0;
}

static void __exit hello_driver_cleanup(void)
{
    printk("hello_driver_cleanup\r\n");
    device_destroy(hello_class,MKDEV(CHRDEVBASE_MAJOR,0));
    class_destroy(hello_class);
    unregister_chrdev(CHRDEVBASE_MAJOR,"hello_driver");
    
}


module_init(hello_driver_init);
module_exit(hello_driver_cleanup);
MODULE_LICENSE("GPL");


app

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>


/* mknod /dev/chrdevbase c 200 0 */

uint8_t buffer[512] = {0};

int main(int argc, char *argv[])
{
    int fd;
    int ret;
    
    fd  = open(argv[1], O_RDWR);

    printf("sleep\r\n");
    sleep(60);
    printf("wake up\r\n");
    
    close(fd);

    
}

Makefile

KERNELDIR := /lib/modules/$(shell uname -r)/build
CURRENT_PATH := $(shell pwd)
obj-m := atomic_driver.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
    $(CROSS_COMPILE)gcc -o test_app test_app.c
clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
    rm -rf test_app

参考:

1.【韦东山】嵌入式Linux应用开发完全手册V4.0_韦东山全系列视频文档-IMX6ULL开发板.docx

2.【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf

相关文章:

  • 【Spring】注解实现IOC操作,你理解了吗?
  • 【C++:STL之栈和队列 | 模拟实现 | 优先级队列 】
  • 面试官问:如何确保缓存和数据库的一致性?
  • Java零基础教程——数据类型
  • SharkTeam:Move合约开发与合约安全
  • 刚刚,体验了一把Bing chat很爽
  • 【计算机网络】Linux环境中的TCP网络编程
  • 【SpringBoot】SpringBoot常用注解
  • 【性能】性能测试理论篇_学习笔记_2023/2/11
  • 【Hello Linux】 Linux基础命令
  • 【Python小游戏】通过这款专为程序员设计的《极限车神》小游戏,你的打字速度可以赢过专业录入员,这个秘密98%的人都不知道哦~(爆赞)
  • C++之多态【详细总结】
  • Android 逆向工具大整理,碉堡了
  • 【C++】类和对象(中)
  • 亿级高并发电商项目-- 实战篇 --万达商城项目 二(Zookeeper、Docker、Dubbo-Admin等搭建工作
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • Apache的80端口被占用以及访问时报错403
  • C++11: atomic 头文件
  • dva中组件的懒加载
  • Java多线程(4):使用线程池执行定时任务
  • overflow: hidden IE7无效
  • QQ浏览器x5内核的兼容性问题
  • supervisor 永不挂掉的进程 安装以及使用
  • vue-router 实现分析
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 动态魔术使用DBMS_SQL
  • 后端_MYSQL
  • 蓝海存储开关机注意事项总结
  • 力扣(LeetCode)21
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 码农张的Bug人生 - 初来乍到
  • 区块链共识机制优缺点对比都是什么
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 用Canvas画一棵二叉树
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • ​LeetCode解法汇总518. 零钱兑换 II
  • ​queue --- 一个同步的队列类​
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​如何在iOS手机上查看应用日志
  • # 数据结构
  • (1)常见O(n^2)排序算法解析
  • (11)MATLAB PCA+SVM 人脸识别
  • (13):Silverlight 2 数据与通信之WebRequest
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (二)丶RabbitMQ的六大核心
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • .NET构架之我见
  • .NET微信公众号开发-2.0创建自定义菜单
  • @private @protected @public