原子操作指的是在执行过程中不会被别的代码路径所中断的操作。Linux内核提供了一系列函数来实现内核中的原子操作,这些函数又分为两类,分别针对位和整型变量进行原子操作。它们的共同点是在任何情况下操作都是原子的,内核代码可以安全地调用它们而不被打断。位和整型变量原子操作都依赖底层CPU的原子操作来实现,因此所有这些函数都与 CPU 架构密切相关。 本小节讲述整型原子的操作,相关函数如下: 一:设置原子变量的值 - void atomic_set(atomic_t *v, int i); //设置原子变量的值为i
- atomic_t v = ATOMIC_ INIT(0); //定义原子变量v并初始化为0
复制代码二:获取原子变量的值 - atomic_read(atomic_t *v); //返回原子变量的值
复制代码三:原子变量加/减 - void atomic_add(int i, atomic_t *v); //原子变量增加i
- void atomic_sub(int i, atomic_t *v); //原子变量减少i
复制代码四:原子变量自增/自减 - void atomic_inc(atomic_t *v); //原子变量增加1
- void atomic_dec(atomic_t *v); //原子变量减少1
复制代码五:操作并测试 - int atomic_inc_and_test(atomic_t *v);
- int atomic_dec_and_test(atomic_t *v);
- int atomic_sub_and_test(int i, atomic_t *v);
复制代码上述操作对原子变量执行自增、自减和减操作后测试其是否为0,为0则返回true,否则返回false。注意,这里的自增和自减并不是立即生效,而是读取了当前值之后才生效,类似于C语言的i++,而不是++i。 六:操作并返回 - int atomic_add_return(int i, atomic_t *v);
- int atomic_sub_return(int i, atomic_t *v);
- int atomic_inc_return(atomic_t *v);
- int atomic_dec_return(atomic_t *v);
复制代码上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。注意,这里的自增和自减,以及加和减会立即生效,类似于C语言的++i,而不是i++。 有了以上操作函数,就可以很轻易的将它嵌进linux内核了。在kernel/drivers/char/x4412目录下新建x4412-atomic.c文件,编辑内容如下: - #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/cdev.h>
- #include <asm/uaccess.h>
- #include <linux/device.h>
- static int x4412_atomic_major;
- static struct cdev x4412_atomic;
- static struct class *cdev_class;
- static atomic_t x4412_available = ATOMIC_INIT(1);
- int x4412_atomic_open(struct inode *inode, struct file *filp)
- {
- int ret;
- ret = atomic_read(&x4412_available);
- printk("x4412-atomic open: default atomic value = %d\r\n",ret);
- ret = atomic_dec_and_test(&x4412_available);
- printk("x4412-atomic open: ret = %d\r\n",ret);
- if(!ret)
- {
- ret = atomic_read(&x4412_available);
- printk("x4412-atomic open error: old ret = %d\r\n",ret);
- atomic_inc(&x4412_available);
- ret = atomic_read(&x4412_available);
- printk("x4412-atomic open error: new ret = %d\r\n",ret);
- return -EBUSY;
- }
- return 0;
- }
- int x4412_atomic_release(struct inode *inode, struct file *filp)
- {
- int ret;
- atomic_inc(&x4412_available);
- ret = atomic_read(&x4412_available);
- printk("x4412-atomic close: ret = %d\r\n",ret);
- return 0;
- }
- static const struct file_operations x4412_fops =
- {
- .owner = THIS_MODULE,
- .open = x4412_atomic_open,
- .release = x4412_atomic_release,
- };
- static void x4412_setup_cdev(void)
- {
- int err, devno = MKDEV(x4412_atomic_major, 0);
- cdev_init(&x4412_atomic, &x4412_fops);
- err = cdev_add(&x4412_atomic, devno, 1);
- if (err)
- printk(KERN_NOTICE "Error %d adding x4412_atomic", err);
- }
-
- static void x4412_clear_cdev(void)
- {
- cdev_del(&x4412_atomic);
- unregister_chrdev_region(MKDEV(x4412_atomic_major, 0), 1);
- }
- int x4412_atomic_init(void)
- {
- int result;
- dev_t devno;
- result = alloc_chrdev_region(&devno, 0, 1, "x4412-atomic");
- x4412_atomic_major = MAJOR(devno);
- if(result < 0)
- {
- printk("register x4412-atomic error!\r\n");
- return result;
- }
- x4412_setup_cdev();
- cdev_class = class_create(THIS_MODULE,"x4412-atomic");
- if(IS_ERR(cdev_class))
- {
- x4412_clear_cdev();
- return PTR_ERR(cdev_class);
- }
- device_create(cdev_class,NULL,MKDEV(x4412_atomic_major,0),NULL,"x4412-atomic");
- return 0;
- }
- void x4412_atomic_exit(void)
- {
- device_destroy(cdev_class,MKDEV(x4412_atomic_major,0));
- class_destroy(cdev_class);
- x4412_clear_cdev();
- }
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_LICENSE("GPL");
- module_init(x4412_atomic_init);
- 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中添加如下语句: - config X4412_ATOMIC_DRIVER
- tristate "x4412 atomic driver"
- default m
- help
- compile for x4412 atomic driver,y for kernel,m for module.
复制代码在kernel/drivers/char/x4412/Makefile中添加如下语句: - obj-$(CONFIG_X4412_ATOMIC_DRIVER) += x4412-atomic.o
复制代码 编译内核,将会在kernel/drivers/char/x4412目录下生成目标文件x4412-atomic.ko文件。 在ubuntu用户目录或samba目录下新建x4412-atomic-app.c文件,编辑内容如下: - #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #define DEVICE_NAME "/dev/x4412-atomic"
-
- int main(int argc,char **argv)
- {
- int fd;
- fd = open(DEVICE_NAME,O_RDWR);
- if(fd < 0)
- {
- printf("open device %s error \n",DEVICE_NAME);
- }
- else
- {
- fd = open(DEVICE_NAME,O_RDWR);
- if(fd < 0)
- {
- printf("repeat open device %s error!\r\n",DEVICE_NAME);
- }
- usleep(5000);
- close(fd);
- }
- return 0;
- }
复制代码 该应用程序执行了两次open动作,以模拟两个执行单元调用驱动的动作。加载驱动模块后使用应用程序测试,观察原子变量是否能够完成预期任务: - [root@x4412 mnt]# insmod x4412-atomic.ko
- [root@x4412 mnt]# ./x4412-atomic-app
- [ 45.177976] x4412-atomic open: default atomic value = 1
- [ 45.182626] x4412-atomic open: ret = 1
- [ 45.186137] x4412-atomic open: default atomic value = 0
- [ 45.191448] x4412-atomic open: ret = 0
- [ 45.194774] x4412-atomic open error: old ret = -1
- [ 45.200229] x4412-atomic open error: new ret = 0
- repeat open device /dev/x4412-atomic error!
- [ 45.210666] x4412-atomic close: ret = 1
- [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)
|