所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!
关于 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
⑤ 结果一般是登录失败