Linux内核的竞态与并发——原子操作实例

《原子操作实例》


所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!

关于 LINUX驱动 的其它文章请点击这里:     LINUX驱动


本文为 Linux内核的竞态与并发(中断屏蔽、原子操作、自旋锁、信号量、互斥体的互斥机制) 中原子操作的实例

其它部分:

Linux内核的竞态与并发——信号量实例
Linux内核的竞态与并发——自旋锁实例


原子操作概念

    原子操作底层表现为一条汇编指令(ldrex、strex)。所以他们在执行过程中不会被别的代码路径所中断。

    事务的原子性就是要么做完 要么不做。而如何实现的原子性不被打断,不需要去关注,内核中实现的原子操作都是与CPU架构息息相关的,只需要掌握原子的使用方法即可。

很好理解,用上厕所的例子来说明。厕所就是共享资源,去上厕所的行为被称作代码路径。
原子操作就是大家每次上厕所都用时非常短,短到什么程度呢,只要一条汇编指令的时间。当然拉的量也非常少(只改变一个整型或者是位)。所以就不存在抢厕所的问题了。

● 位原子操作

  // arch/arm/include/asm/bitops.h
  set_bit(nr, void *addr)      // addr内存中的nr位置1
  clear_bit
  change_bit
  test_bit
  ...

● 整型原子操作
    使用步骤:

//1)定义原子变量     atomic_t tv;  //就是用原子变量来代替整形变量
	//核心数据结构:
	typedef struct {
    
    
		int counter;
	} atomic_t;
//2) 设置初始值的两种方法   
 	tv = ATOMIC_INIT(0);    //① 定义原子变量 v 并初始化为0
 	atomic_set(&tv, i)      //② 设置原子变量的值为 i
//3) 操作原子变量
	int atomic_read(atomic_t *v)       //返回原子变量的值        
	atomic_add(int i, atomic_t *v);    //v += i
	atomic_sub(int i, atomic_t *v);    //v -= i
	atomic_inc(atomic_t *v);           //v++;
	atomic_dec(atomic_t *v)            //v--  
	...   

实例

btn_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

dev_t dev;
struct cdev btn_cdev;
struct class *cls = NULL;

atomic_t btn_tv;       /*[1] 定义整型原子变量*/

int btn_open(struct inode *inode, struct file *filp) 
{
    
    
    if(!atomic_dec_and_test(&btn_tv)) {
    
     //保持减1操作的完整性
        atomic_inc(&btn_tv);			//[3] 保持为0
        return -EBUSY;
    }
    return 0;  //第一次进来return 0,打开成功且btn_tv=0;
}

int btn_close(struct inode *inode, struct file *filp)
{
    
    
    atomic_inc(&btn_tv);  //btn_tv=1,保持可以打开状态
    return 0;
}

struct file_operations btn_fops =
{
    
    
    .owner = THIS_MODULE,
    .open  = btn_open,
    .release = btn_close,
};

int __init btn_drv_init(void)
{
    
    
    alloc_chrdev_region(&dev, 100, 1, "mybuttons");        /*设备号的动态申请注册*/
    cdev_init(&btn_cdev, &btn_fops);                       /*初始化cdev*/
    cdev_add(&btn_cdev, dev, 1);                           /*注册cdev*/
    cls = class_create(THIS_MODULE, "buttons");            /*设备文件的自动创建*/
    device_create(cls, NULL, dev, NULL,"mybuttons");
   
    atomic_set(&btn_tv, 1);                                /*[2] 整型原子变量赋值为1*/
    return 0;
}

void __exit btn_drv_exit(void)
{
    
    
    /*销毁设备文件*/
    device_destroy(cls, dev);
    class_destroy(cls);
    /*注销cdev*/
    cdev_del(&btn_cdev);
    /*注销设备号*/
    unregister_chrdev_region(dev, 1);
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);

Makefile

obj-m  += btn_drv.o
all:
       make -C /home/chuckchee/driver/kernel M=$(PWD) modules
       cp *.ko ../../rootfs
       arm-cortex_a9-linux-gnueabi-gcc test.c -o test
       cp test ../../rootfs
clean:
       make -C /home/chuckchee/driver/kernel M=$(PWD) clean

test.c应用测试代码

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    
    
    int fd = open("/dev/mybuttons", O_RDWR); //读写方式打开

    if(fd < 0) {
    
    
        perror("open failed:");
        return -1;
    }
    printf("open successed: using device 20s...\n");
    sleep(20);
    printf("close device\n");
    close(fd);
    return 0;
}

实验步骤

① insmod btn_drv.ko //设备文件自动形成
② ./test
③ 远程登录:telnet 192.168.1.6
④ ./test
⑤ 结果一般是登录失败

猜你喜欢

转载自blog.csdn.net/qq_16504163/article/details/109332193