九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 3933|回复: 0
打印 上一主题 下一主题

x4412&ibox项目实战32-使用原子操作避免并发竞争(一)

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-5 12:20:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。Linux内核提供了一系列函数来实现内核中的原子操作,这些函数又分为两类,分别针对位和整型变量进行原子操作。它们的共同点是在任何情况下操作都是原子的,内核代码可以安全地调用它们而不被打断。位和整型变量原子操作都依赖底层CPU的原子操作来实现,因此所有这些函数都与 CPU 架构密切相关。
本小节讲述整型原子的操作,相关函数如下:
一:设置原子变量的值
  1. void atomic_set(atomic_t *v, int i); //设置原子变量的值为i
  2. atomic_t v = ATOMIC_ INIT(0); //定义原子变量v并初始化为0
复制代码
二:获取原子变量的值
  1. atomic_read(atomic_t *v); //返回原子变量的值
复制代码
三:原子变量加/减
  1. void atomic_add(int i, atomic_t *v); //原子变量增加i
  2. void atomic_sub(int i, atomic_t *v); //原子变量减少i
复制代码
四:原子变量自增/自减
  1. void atomic_inc(atomic_t *v); //原子变量增加1
  2. void atomic_dec(atomic_t *v); //原子变量减少1
复制代码
五:操作并测试
  1. int atomic_inc_and_test(atomic_t *v);
  2. int atomic_dec_and_test(atomic_t *v);
  3. int atomic_sub_and_test(int i, atomic_t *v);
复制代码
上述操作对原子变量执行自增、自减和减操作后测试其是否为0,为0则返回true,否则返回false。注意,这里的自增和自减并不是立即生效,而是读取了当前值之后才生效,类似于C语言的i++,而不是++i。
六:操作并返回
  1. int atomic_add_return(int i, atomic_t *v);
  2. int atomic_sub_return(int i, atomic_t *v);
  3. int atomic_inc_return(atomic_t *v);
  4. int atomic_dec_return(atomic_t *v);
复制代码
上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。注意,这里的自增和自减,以及加和减会立即生效,类似于C语言的++i,而不是i++。
有了以上操作函数,就可以很轻易的将它嵌进linux内核了。在kernel/drivers/char/x4412目录下新建x4412-atomic.c文件,编辑内容如下:
  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/errno.h>
  4. #include <linux/init.h>
  5. #include <linux/cdev.h>
  6. #include <asm/uaccess.h>
  7. #include <linux/device.h>
  8. static int x4412_atomic_major;
  9. static struct cdev x4412_atomic;
  10. static struct class *cdev_class;
  11. static atomic_t x4412_available = ATOMIC_INIT(1);
  12. int x4412_atomic_open(struct inode *inode, struct file *filp)
  13. {
  14.          int ret;
  15.          ret = atomic_read(&x4412_available);
  16.          printk("x4412-atomic open: default atomic value = %d\r\n",ret);
  17.          ret = atomic_dec_and_test(&x4412_available);
  18.          printk("x4412-atomic open: ret = %d\r\n",ret);
  19.          if(!ret)
  20.          {
  21.                    ret = atomic_read(&x4412_available);
  22.                    printk("x4412-atomic open error: old ret = %d\r\n",ret);
  23.                    atomic_inc(&x4412_available);
  24.                    ret = atomic_read(&x4412_available);
  25.                    printk("x4412-atomic open error: new ret = %d\r\n",ret);
  26.                    return -EBUSY;
  27.          }
  28.          return 0;
  29. }
  30. int x4412_atomic_release(struct inode *inode, struct file *filp)
  31. {
  32.          int ret;
  33.          atomic_inc(&x4412_available);
  34.          ret = atomic_read(&x4412_available);
  35.          printk("x4412-atomic close: ret = %d\r\n",ret);
  36.          return 0;
  37. }
  38. static const struct file_operations x4412_fops =
  39. {
  40.          .owner = THIS_MODULE,
  41.          .open = x4412_atomic_open,
  42.          .release = x4412_atomic_release,
  43. };
  44. static void x4412_setup_cdev(void)
  45. {
  46.          int err, devno = MKDEV(x4412_atomic_major, 0);
  47.          cdev_init(&x4412_atomic, &x4412_fops);
  48.          err = cdev_add(&x4412_atomic, devno, 1);
  49.          if (err)
  50.                    printk(KERN_NOTICE "Error %d adding x4412_atomic", err);
  51. }

  52. static void x4412_clear_cdev(void)
  53. {
  54.          cdev_del(&x4412_atomic);
  55.          unregister_chrdev_region(MKDEV(x4412_atomic_major, 0), 1);
  56. }
  57. int x4412_atomic_init(void)
  58. {
  59.          int result;
  60.          dev_t devno;
  61.          result = alloc_chrdev_region(&devno, 0, 1, "x4412-atomic");
  62.          x4412_atomic_major = MAJOR(devno);
  63.          if(result < 0)
  64.          {
  65.                    printk("register x4412-atomic error!\r\n");
  66.                    return result;
  67.          }
  68.          x4412_setup_cdev();
  69.          cdev_class = class_create(THIS_MODULE,"x4412-atomic");
  70.          if(IS_ERR(cdev_class))
  71.          {
  72.                    x4412_clear_cdev();
  73.                    return PTR_ERR(cdev_class);
  74.          }
  75.          device_create(cdev_class,NULL,MKDEV(x4412_atomic_major,0),NULL,"x4412-atomic");
  76.          return 0;
  77. }
  78. void x4412_atomic_exit(void)
  79. {
  80.          device_destroy(cdev_class,MKDEV(x4412_atomic_major,0));
  81.          class_destroy(cdev_class);
  82.          x4412_clear_cdev();
  83. }
  84. MODULE_AUTHOR("www.9tripod.com");
  85. MODULE_LICENSE("GPL");
  86. module_init(x4412_atomic_init);
  87. module_exit(x4412_atomic_exit);
复制代码
这里字符设备驱动的框架和前面的实验雷同,不再多述,直接进入主题,在file_operations结构体中,仅仅封装了open和release两个成员,用于测试原子变量足矣。
驱动的最前面定义了一个原子变量x4412_available,并初始化为1,在open函数中,通过atomic_dec_and_test函数检查该原子变量的值后再递减,若检查的值为1,表示没有执行单元打开本驱动,若为0,表示驱动已经被打开,则通过atomic_inc函数将原子变量递增后,返回失败。递增原子变量的目的是为了补齐原子变量,因为函数atomic_dec_and_test已经递减过一次了。在release函数中,也通过atomic_inc函数将原子变量递增,随后关闭驱动。
增加了这些原子变量操作函数后,保证了驱动永远只被一个执行单元打开,避免了竞态的发生。atomic_read函数仅用于读取当前原子变量的值,并通过printk函数打印出来,助于分析操作函数的特性。
在kernel/drivers/char/x4412/Kconfig中添加如下语句:
  1. config X4412_ATOMIC_DRIVER
  2.          tristate "x4412 atomic driver"
  3.          default m
  4.          help
  5.          compile for x4412 atomic driver,y for kernel,m for module.
复制代码
在kernel/drivers/char/x4412/Makefile中添加如下语句:
  1. obj-$(CONFIG_X4412_ATOMIC_DRIVER) += x4412-atomic.o
复制代码
       编译内核,将会在kernel/drivers/char/x4412目录下生成目标文件x4412-atomic.ko文件。
       ubuntu用户目录或samba目录下新建x4412-atomic-app.c文件,编辑内容如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>
  4. #define DEVICE_NAME  "/dev/x4412-atomic"

  5. int main(int argc,char **argv)
  6. {
  7.         int fd;
  8.         fd = open(DEVICE_NAME,O_RDWR);
  9.         if(fd < 0)
  10.         {
  11.                   printf("open device %s error \n",DEVICE_NAME);
  12.         }
  13.         else
  14.         {
  15.                    fd = open(DEVICE_NAME,O_RDWR);
  16.                    if(fd < 0)
  17.                    {
  18.                             printf("repeat open device %s error!\r\n",DEVICE_NAME);
  19.                    }
  20.                    usleep(5000);
  21.                    close(fd);
  22.         }
  23.         return 0;
  24. }
复制代码
       该应用程序执行了两次open动作,以模拟两个执行单元调用驱动的动作。加载驱动模块后使用应用程序测试,观察原子变量是否能够完成预期任务:
  1. [root@x4412 mnt]# insmod x4412-atomic.ko
  2. [root@x4412 mnt]# ./x4412-atomic-app
  3. [   45.177976] x4412-atomic open: default atomic value = 1
  4. [   45.182626] x4412-atomic open: ret = 1
  5. [   45.186137] x4412-atomic open: default atomic value = 0
  6. [   45.191448] x4412-atomic open: ret = 0
  7. [   45.194774] x4412-atomic open error: old ret = -1
  8. [   45.200229] x4412-atomic open error: new ret = 0
  9. repeat open device /dev/x4412-atomic error!
  10. [   45.210666] x4412-atomic close: ret = 1
  11. [root@x4412 mnt]#
复制代码
       结合驱动源码分析,在open函数中第一次读取原子变量时,其值为默认声明的1,执行完atomic_dec_and_test函数取得的原子变量值仍然是1,这时驱动被正常打开。再次使用open函数打开驱动时,发现原子变量已经变为0了,说明上一次atomic_dec_and_test函数的递减功能已经生效。再次执行该函数后,其取得的原子变量值为0,这时驱动中的if判断已经为真,atomic_read函数得到执行,由于第二次atomic_dec_and_test函数已经生效,因此这时读到的原子变量的值就变为-1了。执行atomic_inc函数后,得到的新的原子变量的值为0了。在应用程序执行close函数关闭驱动时,驱动的release函数得到执行,它会继续调用atomic_inc函数释放原子变量,最终原子变量又恢复为1,供下一个执行单元调用,整个驱动到此结束。
       可见,添加了原子变量后,驱动模块在同一时间将只允许被一个执行单元打开,只有当前执行单元退出后,才允许下一个执行单元调用,避免了竞态的发生。
在x4412开发板和ibox卡片电脑上能直接运行的映像:
x4412-atomic.ko (4.03 KB, 下载次数: 10)
x4412-atomic-app (6.06 KB, 下载次数: 11)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-6-23 11:39 , Processed in 0.021800 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表