了解内容
比如我们想任意时刻只有一个系统app能正常调用驱动中open函数,就可以在open函数中实现互斥。
Linux操作系统中提供实现互斥的方法有:原子操作、自旋锁、信号量、互斥体等。
原子操作
原理是:声明一个变量(比如初始0),访问互斥资源时改值(+1),离开时还原值(-1);在访问之前判断该值是否为初始值(0),就可以知道该资源是否被占用。被占用时直接返回。
因为在文件中声明了变量(静态全局),所以原子操作在驱动程序文件的范围中生效;可以使诸如open的函数或者某内容互斥。
一些函数
1、定义一个原子变量,并初始化
1
static atomic_t v = ATOMIC_INIT(0);
2、原子变量自减1/自加1
1
2
atomic_dec(&v); //自减1
atomic_inc(&v); //自加1
3、读取原子变量的值/设置原子变量的值
1
2
atomic_read(&v); //读取
atomic_set(&v,n); //设置值为n
4、原子变量自减1,并与0比较,如果为0则返回true,否则返回false
1
atomic_dec_and_test(&v);
#### 例程
驱动例程
atomic_demo.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");
static atomic_t atomic_v = ATOMIC_INIT(1);
static int major;//主设备号,副设备号注册全部
static struct class *my_class;
static int demo_open(struct inode *node, struct file *filp){
if(!atomic_dec_and_test(&atomic_v)){//减1后判断结果是否0,为0返回真
atomic_inc(&atomic_v); //加1
printk(KERN_EMERG "busy!!!\n");
return -EBUSY;
}
printk(KERN_EMERG "open success!\n");
return 0;
}
static ssize_t demo_write (struct file *filp, const char __user *buf, size_t size, loff_t *off){
char write_str[64];
size_t len;
len = copy_from_user(write_str, buf, size);
printk(KERN_EMERG "write \"%s\" ok!\n",write_str);
return len;
}
static int demo_release(struct inode *node, struct file *filp){
atomic_set(&atomic_v,1); //复原原子值,设1
return 0;
}
static struct file_operations demo_ops = {
.owner = THIS_MODULE,
.open = demo_open,
.write = demo_write,
.release = demo_release,
};
static int demo_init(void){
//"register_chrdev"是逐渐废除的函数,会一次性注册256个子设备
//推荐使用"register_chrdev_region" + "cdev_init" + "cdev_add"的模式
//这里为了演示的简单,就使用了该函数
major = register_chrdev(0, "demo", &demo_ops);
my_class = class_create(THIS_MODULE, "demo_class");
device_create(my_class, NULL, MKDEV(major, 0), NULL, "demo_dev");
return 0;
}
static void demo_exit(void){
unregister_chrdev(major, "demo");//逐渐废除的函数,演示暂用
}
module_init(demo_init);
module_exit(demo_exit);
系统app例程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
char *node = "/dev/demo_dev";
int fd;
fd = open(node,O_RDWR);
if(fd < 0)
goto fail;
if(argc >= 2)
write(fd,argv[1],sizeof(argv[1]));
return 0;
fail:
printf("Something is fail!\n");
return 0;
}
测试结果
编写简单Makefile:参考这里
编译生成模块后,拷贝到开发板中:
-
首先加载模块
-
加载后会生成
/dev/demo_dev
设备节点 -
顺序执行两次传参不同的测试程序,可以看到对资源的访问正常
-
模拟并行执行两次传参不同的测试程序,可以看到互斥访问已经起作用了
其他
由于每个互斥资源都要声明一个整形原子变量,而实际该变量的值也只有两个状态,有点浪费和不易管理,所以建议使用位原子代替整形原子,也就是对整形变量的每一位单独操作。
1、定义一个整形变量,并初始化
1
static volatile unsigned long int value_bit = 0;
2、位原子置位/清零
1
2
set_bit(0,&value_bit); //第0位置位
clear_bit(0,&value_bit); //第0位清零
3、读取位原子的值
1
test_bit(0,&value_bit); //读取第0位
其他(后续补充)
主要介绍原子操作、自旋锁、互斥体。