驱动开发住常见的一个问题:就是一个驱动可以被多个进程同时打开,使用,这样会导致驱动功能混乱。

./app & ./app & ./app   可以同时打开这个设备,可通过文件描述符调用驱动的其他接口操作这个设备。

如果想只一个进程使用这个设备,只需要在open接口函数做打开判断。

示例:
static int  open_flag = 0;
//打开设备时候执行的程序
static int  chrdev_open(struct inode *pinode, struct file *pfile)
{
   //打开判断
    if(open_flag)
        return -EBUSY;
    open_flag = 1;   //设置    设备已经被打开的标志
     
    printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);

return 0;
}

//关闭设备时执行的程序
static int chrdev_release(struct inode *pinode, struct file *pfile)
{
    open_flag = 0;   //设置    设备已经被打开的标志
    printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
    return 0;
}

以上的效果实现,但是不安全。
分析:
open_flag = 1;   这条语句编译汇编 最少得3条。 只需要执行时候没有一次完成,中途被其他进程打开,刚刚这个进程又调用open打开这个设备。这样,新进程也可以打开成功。

怎么样才可以安全做一次性执行完成这三个步骤?

关系统中断;    -->关中断后不会任务调度情况,这样就是安全。
open_flag = 1;     
开系统中断;    -->重新恢复系统调度

示例:
unsigned long flags;
raw_local_irq_save(flags);     //关中断
open_flag = 1;                       //设置    设备已经被打开的标志
raw_local_irq_restore(flags); //开中断

Linux内核已经提供相关的一些,很方便实现这样的效果。原子操作,信号量,互斥信号量,自旋锁。

原子操作:

1.概念:

原子,是最小的可以独立存在的物质单元,是不可分割。把一个完整的,不可中断的操作过程称为原子操作。Linux系统中的原子本质就是对一个Int类型变量进行操作,但是,它整个操作操作进行控制,保证这个过程是一次性完成。

2. 数据结构

Types.h (include\linux)

typedef struct {volatile int counter;
} atomic_t;

本质 就是一个int类型变量,可以这个机制很简单,内核只提供对这个变量的+,-,读,赋值操作相关的接口。volatile 修饰字段告诉 gcc 不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。

3. 相关的API

Atomic.h (include\linux)
Atomic.h (include\asm-generic)

ATOMIC_INIT(v);

宏原型 #define ATOMIC_INIT(i) { (i) }
宏功能 初始化为原子变量为 i
宏参数 i: 值
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_read(atomic_t * v);

宏原型 #define atomic_read(v) (*(volatile int *)&(v)->counter)
宏功能 对原子类型的变量进行原子读操作,它返回原子类型的变量 v 的值。
宏参数 v: 原子变量指针
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_set(atomic_t * v, int i);

函数原型 #define atomic_set(v,i)    (((v)->counter) = (i))
函数功能

给v增加值i

参数 v:原子变量指针     i:增加的值
返回值
头文件 arch/arm/include/asm/atomic.h

void atomic_sub(int i, atomic_t *v)

函数原型 #define atomic_sub(i, v)    (void) atomic_sub_return(i, v)
函数功能

给v减值i

参数 v:原子变量指针     i:减的值
返回值
头文件 arch/arm/include/asm/atomic.h

atomic_sub_and_test(i, v)

宏原型 #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
宏功能 该函数从原子类型的变量 v 中减去 1,并判断结果是否为 0,如果为 0,返回真,否则返回
假。
宏参数 v: 原子变量指针;
宏返回值 非 0:相减结果为 0; 0: 相减结果不为 0。(因为宏对就的是函数,所以有返回值)
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_inc(v);

宏原型 #define atomic_sub(i, v)    (void) atomic_sub_return(i, v)
宏功能 对原子类型变量 v 原子加 1
宏参数 v: 原子变量指针;
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_dec(v)

宏原型 #define atomic_sub(i, v)    (void) atomic_sub_return(i, v)
宏功能 对原子类型的变量 v 原子减 1
宏参数 v: 原子变量指针;
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_dec_and_test(v)

宏原型 #define atomic_dec_and_test(v)    (atomic_sub_return(1, v) == 0)
宏功能 对原子类型的变量 v 原子减 1,并判断结果是否为 0,如果为 0,返回真,否则返回假。
宏参数 v: 原子变量指针;
宏返回值 非 0:相减结果为 0; 0: 相减结果不为 0。(因为宏对就的是函数,所以有返回值)
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_inc_and_test(v)

宏原型 #define atomic_inc_and_test(v)    (atomic_add_return(1, v) == 0)
宏功能 对原子变量v增加1,并判断结果是否为0,如果为0,返回真,否则返回假
宏参数 v: 原子变量指针;
宏返回值 非 0:增加 1 结果为 0; 0: 增加 1 结果不为 0。(因为宏对就的是函数,所以有返回值)
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_add_negative(i,v)

宏原型 #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
宏功能 对原子类型的变量 v 增加 i,并判断结果是否为负数,如果是,返回真,否则返回假。
宏参数 v: 原子变量指针; i:要加的值
宏返回值 非 0:加 i 后结果为负数; 0: 减去 i 后结果为不负数。(因为宏对就的是函数,所以有返
回值)
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

int atomic_add_return(int i, atomic_t *v)

函数原型 static inline int atomic_add_return(int i, atomic_t *v)
函数功能 对原子类型的变量 v 增加 i,并且返回 v 的值
函数参数 v: 原子变量指针; i:要加的值
函数返回值 加 i 后的原子变量值
所在头文件 arch\arm\include\asm\atomic.h
函数定义文件 arch\arm\include\asm\atomic.h

int atomic_sub_return(int i, atomic_t *v)

函数原型 static inline int atomic_sub_return(int i, atomic_t *v)
函数功能 原子类型的变量 v 中减去 i,并且返回 v 的值
函数参数 v: 原子变量指针; i:要减去的值
函数返回值 减去 i 后的原子变量值
所在头文件 arch\arm\include\asm\atomic.h
函数定义文件 arch\arm\include\asm\atomic.h

atomic_inc_return(v)

宏原型 #define atomic_inc_return(v)    (atomic_add_return(1, v))
宏功能 对原子类型的变量 v 增加 1 并且返回 v 的值
宏参数 v: 原子变量指针;
宏返回值 原子变量增加 1 后的值; (因为宏对应的就是函数,所以有返回值)
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

atomic_dec_return(v)

宏原型 #define atomic_dec_return(v)    (atomic_sub_return(1, v))
宏功能 对原子类型的变量 v 减 1 并且返回 v 的值。
宏参数 v: 原子变量指针;
宏返回值 原子变量减 1 后的值; (因为宏对应的就是函数,所以有返回值)
所在头文件 arch\arm\include\asm\atomic.h
宏定义文件 arch\arm\include\asm\atomic.h

为了说明原子操作的作用和用法,现在举个例子,使用原子变量实现设备只能被一个进程打开。

#include <linux/module.h> /* Needed by all modules */
#include <linux/init.h> /* Needed for the module-macros */
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/atomic.h>
#define LEDS_MAJOR 255
#define DEVICE_NAME "miscdev_atomic"
static atomic_t miscdev_atomic = ATOMIC_INIT(1); //定义并初始化一个原子变量
static int first_miscdev_open(struct inode *pinode, struct file *pfile)
{
printk (KERN_EMERG "Linux miscdevice:%s is call\r\n",__FUNCTION__);
if( !atomic_dec_and_test(&miscdev_atomic) ) {
printk(KERN_ERR DEVICE_NAME " is already open!\n");
atomic_inc(&miscdev_atomic);
return -EBUSY;
}
return 0;
}
int first_miscdev_release (struct inode *inode, struct file *file)
{
printk (KERN_EMERG "Linux miscdevice:%s is call\r\n",__FUNCTION__);
atomic_inc(&miscdev_atomic); //释放原子变量
return 0;
}
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.open = first_miscdev_open,
.release= first_miscdev_release,
};
static struct miscdevice misc =
{
.minor = LEDS_MAJOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
/* 模块装载执行函数 */
static int __init first_miscdev_init(void)
{
int ret;
ret = misc_register(&misc); //注册混杂设备
if(ret <0)
printk (KERN_EMERG DEVICE_NAME"\t err\r\n");
printk (KERN_EMERG DEVICE_NAME"\tinitialized\n");
return ret;
}
/* 模块卸载执行函数 */
static void __exit first_miscdev_exit(void)
{
misc_deregister(&misc);
printk(KERN_EMERG "Goodbye,cruel world!, priority = 0\n");
}
module_init(first_miscdev_init);
module_exit(first_miscdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XYD");
MODULE_DESCRIPTION("This the samlpe drv_test");

从上面可以看到,其实只是在原来的字符设备驱动的 open 函数中增加了原子操作判断相关的代码,在关闭函数中增了释放原子操作的代码。 其余的代码基本不用改动,这样就可以保证这个驱动只能被一个进程使用,保证了驱动的安全性。当然,原子操作代码不仅仅是可以放在 open 函数上,可以根据需要放在不同的地方,比如放在 write 函数中,可以实现驱动可以被多进程打开,但是在同时刻只能被一个进程写操作,读操作可以被多进程读,因为读操作不会破坏数据, 而写操作会, 所以, 有时候这处功能恰好是我们想要的结果,就可以这样写。原子操作提供了很多 API,以上只以使用其中一种写法,读者可以在理解了的基础上使用其他 API 来实现相同的功能。

linux内核原子操作使用简介相关推荐

  1. 【Linux】linux内核原子操作的实现

    所谓原子操作,就是"不可中断的一个或一系列操作". 硬件级的原子操作:在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是"原子操作&qu ...

  2. linux内核原子操作的实现

    所谓原子操作,就是"不可中断的一个或一系列操作". 硬件级的原子操作:在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是"原子操作&qu ...

  3. Linux 内核原子操作

    本文主要介绍了原子操作的一些相关用法. 目录 文章目录 前言 一.原子操作 二.整型原子操作 1.设置原子变量的值 2.获取原子变量的值 3.原子变量加.减 4.原子变量自增.自减 5.操作 三.位原 ...

  4. Linux内核原子操作(1)基本原理

    概述 原子(atomic)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operation)意为"不可被中断的一个或一系列操作",可以保证指令 ...

  5. Linux 内核引导选项简介 *********很多常用的受益匪浅

    内核引导选项大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项.比如,如果你想知道可以向 AHA154 ...

  6. Linux 内核引导参数简介

    概述 内核引导参数大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导参数多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导参数.比如,如果你想知道可以向 AHA ...

  7. 君君学Linux设备驱动第三天之linux内核简简简介

    内核这东西不是一篇博客,日志能说清楚的,但是简要总结一下有利于后面的学习...... 一 内核的演变和发展 Linux是unix的一种克隆系统.它的发展依赖于五大支柱:unix系统(分时操作系统),m ...

  8. linux内核编译 kbuild,Linux内核编译系统kbuild简介

    前言 这篇文章并非原创,是偶然在linuxjournal上面看到的一篇文章,感觉写得比较清晰,例子详尽,所以这里对文章进行简单整理,算是一个笔记.本文主要是关于kbuild的简单介绍,不会介绍linu ...

  9. linux cache控制 内核,linux内核之bcache简介 [转]

    bcache是按照SSD特性来设计的,只按擦除桶大小进行分配,使用btree和日志混合方法来跟踪缓存数据,缓存数据可以是桶上的任意一个扇区.bcache最大程度上减少了随机写的代价,它按顺序填充一个桶 ...

最新文章

  1. python生成试卷制卷系统_Python 读写文件 小应用:生成随机的测验试卷文件
  2. python以下字符串合法的是_以下字符串合法的是().
  3. B2B行业网站10种经营模式研究及组合方案
  4. 高质量程序程序设计指南摘录
  5. (转)线程安全的CopyOnWriteArrayList介绍
  6. 留意TCP/IP筛选和IP安全策略
  7. 如何用staruml画包图_StarUML的9种图
  8. android中播放gif动画之二
  9. jar包冲突常用的解决方法
  10. 打印异常堆栈_通过异常堆栈丢失谈即时编译优化
  11. ssh链接数设置问题
  12. iphone编辑过的录音怎么还原_ios录音误剪怎么恢复
  13. 桌面客户端上登入Gmai 邮箱
  14. machine learning measurements
  15. C++ 引用与引用作为函数的参数
  16. k8s入门:部署应用到 k8s 集群
  17. download.js 实现浏览器下载 而不是打开
  18. Chrome version must be 70 and 73
  19. 基础实验——三轴传感器
  20. 服务器看门狗芯片电路图,看门狗电路简介(低成本)

热门文章

  1. 完全限定域名(fully qualified domain name,FQDN,笔记)
  2. Eclipse控制台中的中文输出乱码问题
  3. DVWA靶场通关教程
  4. windows下查看错误码与错误信息
  5. 简述计算机桌面背景更换的流程,苹果电脑桌面壁纸怎么更换【详细步骤】
  6. git pull 报错 Your local changes would be overwritten by merge. Commit, stash or revert them to procee
  7. 智能优化与机器学习结合算法实现数据分类matlab代码清单
  8. Java实现回文判断
  9. 【转载】配置文件 .yml 写法小结
  10. 图像的二值化和灰度化