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

嵌入式linux驱动之并发

嵌入式linux驱动之内核竞态_寒听雪落的博客-CSDN博客

一, linux 系统并发产生的原因很复杂,主要下面几个原因:
1、多线程并发访问,linux 是多任务(线程)的系统,所以多线程访问是最基本的原因。
2、抢占式并发访问,从内核2.6版本开始,linux 内核支持抢占,也就是说调度程序可以在任意时刻抢占正在运行的线程,从而运行其他的线程。
3、中断程序并发访问,学过STM32应该知道,硬件中断的权利可是很大的。
4、SMP(多核)核间并发访问,现在ARM架构的多核SOC很常见,多核CPU存在核间并发访问。并发访问带来的问题就是竞争,学过FreeRTOS和UCOS的同学应该知道临界区这个概念,所谓的临界区就是共享数据段,对于临界区必须保证一次只有一个线程访问,也就是要保证临界区是原子访问的,这里的原子访问就表示这一个访问是一个步骤,不能再进行拆分。如果多个线程同时操作临界区就表示存在竞争,我们在编写驱动的时候一定要注意避免并发和防止竞争访问。我们一般在编写驱动的时候就要考虑到并发与竞争,而不是驱动都编写完了然后再处理并发与竞争。

 二,驱动中原子操作函数分析

1,atomic64_t    //在 type.h 头文件中,对于原子变量的结构体的定义

typedef struct

{
        long counter;
}

2,atomic64_read函数

long long atomic64_read(const atomic64_t *v); //读取原子变量所使用到的函数

3,atomic64_set函数

void atomic64_set(atomic64_t *v, long long i);   //原子变量设置函数,用来设置原子变量的数值

参数分析:
•  *v:atomic64_t 结构体地址
•  i:需要设置的值
•  返回值:空

4,atomic64_add函数

void atomic64_##op(long long a, atomic64_t *v);

含义:原子变量加法函数,作用是把指定的 atomic64_t 结构体的值加上指定的数值。这个函数用op代替了add这个函数,因为其同样也承载了减法函数的功能,使用op来复用了函数。

参数分析:

•  a:指定的数
•  *v:atomic64_t 结构体
•  返回值:空

5,atomic64_sub函数

void atomic64_##op(long long a, atomic64_t *v);

含义:既然有加,那就也应该有减,这是原子变量的减法函数,使用方法和加法函数一样。

参数分析:
•  a:指定的数
•  *v:atomic64_t 结构体
•  返回值:空

6,atomic64_inc函数

 #define atomic64_inc(v) atomic64_add(1, (v))    //其中参数v是指atomic64_t 结构体

含义:原子变量自增函数,调用这个函数可让指定函数自增一。原理就是通过 atomic64_add 函数加一。

7,atomic64_dec函数

#define atomic64_dec(v) atomic64_sub(1, (v))    //其中参数v是指atomic64_t 结构体

含义:原子变量自减函数,与自增的原理一样,是通过 atomic64_sub 函数减一。

三,驱动程序源码

//添加头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>

#define ZYNQMP_GPIO_NR_GPIOS 118
#define MIO_PIN_51 (ARCH_NR_GPIOS - ZYNQMP_GPIO_NR_GPIOS + 51)
// #define MIO_PIN_38 (ARCH_NR_GPIOS - ZYNQMP_GPIO_NR_GPIOS + 38)

//设置一个设备全局变量
struct lock_device
{
	dev_t devno;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	atomic_t lock;
} lock_dev;

int lock_open(struct inode *inode, struct file *filp)
{
	printk("-lock_open-\n");
	if (!atomic_read(&lock_dev.lock)) //读取锁的状态
		atomic_inc(&lock_dev.lock);	//把原子变量加 1, 上锁
	else
		return -EBUSY; //若检测到已上锁,则返回设备忙
	return 0;
}
ssize_t lock_write(struct file *flip, const char __user *buf, size_t count, loff_t *fops)
{
	int flag = 0, i = 0;
	flag = copy_from_user(&i, buf, count); //使用copy_from_user读取用户态发送过来的数据
	printk(KERN_CRIT "flag = %d, i = %d, count = %d\n", flag, i, count);
	if (flag != 0)
	{
		printk("Kernel receive data failed!\n");
		return 1;
	}
	if (i == 48)
	{
		gpio_set_value(MIO_PIN_51, 0);
		// gpio_set_value(MIO_PIN_38, 0);
	}
	else
	{
		gpio_set_value(MIO_PIN_51, 1);
		// gpio_set_value(MIO_PIN_38, 1);
	}
	return 0;
}
int lock_close(struct inode *inode, struct file *filp)
{
	printk("-lock_close-\n");
	atomic_set(&lock_dev.lock, 0); //将变量设为0,意为解锁
	return 0;
}

const struct file_operations lock_fops = {
	.open = lock_open,
	.write = lock_write,
	.release = lock_close,
};

//实现装载入口函数和卸载入口函数
static __init int lock_drv_init(void)
{
	int ret = 0;
	printk("----^v^-----lock drv v1 init\n");

	//动态申请设备号
	ret = alloc_chrdev_region(&lock_dev.devno, 0, 1, "lock_device");
	if (ret < 0)
	{
		printk("alloc_chrdev_region fail!\n");
		return 0;
	}

	//设备初始化
	cdev_init(&lock_dev.cdev, &lock_fops);
	lock_dev.cdev.owner = THIS_MODULE;

	//自动创建设备节点
	//创建设备的类别
	//参数1----设备的拥有者,当前模块,直接填THIS_MODULE
	//参数2----设备类别的名字,自定义
	//返回值:类别结构体指针,其实就是分配了一个结构体空间
	lock_dev.class = class_create(THIS_MODULE, "lock_class");
	if (IS_ERR(lock_dev.class))
	{
		printk("class_create fail!\n");
		return 0;
	}

	//创建设备
	//参数1----设备对应的类别
	//参数2----当前设备的父类,直接填NULL
	//参数3----设备节点关联的设备号
	//参数4----私有数据直接填NULL
	//参数5----设备节点的名字
	lock_dev.device = device_create(lock_dev.class, NULL, lock_dev.devno, NULL, "lock_device");
	if (IS_ERR(lock_dev.device))
	{
		printk("device_create fail!\n");
		return 0;
	}

	//向系统注册一个字符设备
	cdev_add(&lock_dev.cdev, lock_dev.devno, 1);

	//MIO_PIN_51 38申请GPIO口
	ret = gpio_request(MIO_PIN_51, "led1");
	if (ret < 0)
	{
		printk("gpio request led1 error!\n");
		return ret;
	}

	//GPIO口方向设置成输出
	ret = gpio_direction_output(MIO_PIN_51, 1);
	if (ret != 0)
	{
		printk("gpio direction output MIO_PIN_51 fail!\n");
	}

	//将原子变量置0,相当于初始化
	atomic_set(&lock_dev.lock, 0);

	return 0;
}

static __exit void lock_drv_exit(void)
{
	printk("----^v^-----lock drv v1 exit\n");

	//释放按键GPIO
	gpio_free(MIO_PIN_51);
	// gpio_free(MIO_PIN_38);

	//注销字符设备
	cdev_del(&lock_dev.cdev);
	//删除设备节点
	device_destroy(lock_dev.class, lock_dev.devno);
	//删除设备类
	class_destroy(lock_dev.class);
	//注销设备号
	unregister_chrdev_region(lock_dev.devno, 1);
}

//申明装载入口函数和卸载入口函数
module_init(lock_drv_init);
module_exit(lock_drv_exit);

//添加GPL协议
MODULE_LICENSE("GPL");
MODULE_AUTHOR("subomb");

四,应用程序分析

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

int main(int argc, char *argv[])
{
    int fd, ret = 0;
    char *filename;
    char writebuf[1] = {0};
    filename = argv[1];
    fd = open(filename, O_RDWR); //打开设备
    if (fd < 0)
    {
        printf("Can't open file %s\n", filename);
        return -1;
    }
    //通过驱动程序接口,发送指令至驱动程序,此处 0 代表灯灭,1 代表灯亮
    memcpy(writebuf, argv[2], 1); //将内容拷贝到缓冲区
    ret = write(fd, writebuf, 1); //写数据
    if (ret < 0)
    {
        printf("Write file %s failed!\n", filename);
    }
    else
    {
        printf("Write file success!\n");
    }
    sleep(15);   //线程暂停15秒,留出时间测试
    printf("Finish.\n");
    ret = close(fd); //关闭设备
    if (ret < 0)
    {
        printf("Can't close file %s\n", filename);
        return -1;
    }
    return 0;
}

相关文章:

  • 织梦cms如何搭建网站/关键字搜索
  • 酷站网/广东省白云区
  • 个人网站下载/东莞网站建设优化排名
  • 合肥学网站设计/seo优化教学视频
  • 甘肃省建设部网站首页/短视频赚钱app软件
  • 杭州网站排名外包/怎样创建一个自己的网站
  • 【微信小程序入门到精通】— 事件处理中进行数据赋值以及传递参数
  • 【Linux】结合实例来看Linux系列指令
  • 读书心得二
  • Vue组件、Vue插件的创建使用
  • 如何将全国各省份人口数据绘制成地域分布图?Python 轻松解决
  • 从零使用TensorFlow搭建CNN(卷积)神经网络
  • 【Oracle数据库系列笔记】聚合函数与其他函数
  • wireshark 笔记
  • 图像运算和图像增强三
  • [Spring boot] Spring boot 实现Excel批量导入数据并将文件保存到本地
  • Kafka
  • 【云原生丨K8s系列20】 RBAC 的配置⽅法演示(上):创建⼀个只能访问某个 namespace 的⽤户