驱动学习(七)驱动互斥
1. 什么是驱动互斥?
一类设备对应一个驱动程序,一类设备可以有多个子设备,当多个子设备同时访问一个驱动程序时,会造成竞态。简单来说就是厕所只有一个,不可以同时上。
2. 什么是竞态?
竞态就是两个子设备同时访问了一个驱动程序,就是他俩同时进厕所了,坑位只有一个,这这这,不像话啊。
模拟条件:一个驱动,两个设备文件,一起访问。
后果是什么?会造成数据错乱,这里我没有还原出来,但是对于内核级别来说是致命的。
3. 如何解决多个子设备对同一个驱动的竞态访问问题呢?
Linux内核提供了多种互斥方法,有互斥锁、信号量、原子变量、自旋锁
3.1 互斥锁
如何防止你在上厕所时,别人从外面把门拉开?加把锁!当然不锁门的当我没说。
应用:
mutex_t mutex;//应用层互斥锁
1 定义互斥锁
struct mutex Mutex;//内核互斥锁
2 初始化互斥锁
mutex_init(Mutex);-->在初始化函数中
3 加锁
mutex_lock(&Mutex);-->在打开函数中锁住资源
4 解锁
mutex_unlock(&Mutex);--->在关闭函数中释放资源
测试
#####驱动代码
1 #include <linux/module.h> //模块驱动的头文件
2 #include <linux/cdev.h> //设备信息描述的头文件
3 #include <linux/fs.h> //静态申请设备号头文件
4 #include <linux/kdev_t.h> //设备号用到的头文件和宏函数
5 #include <linux/uaccess.h>
6 #include <linux/device.h>
7 #include "common.h"
8
9 #define BUF_SIZE 100
10
11 int major = 0; //主设备号
12 int min = 0; //次设备号
13 int deviceNum = 0; //完整设备号
14 struct cdev* pCdev = NULL; //描述设备信息的结构体
15 char *deviceName = "mutex";//设备名
16 char buff[BUF_SIZE] = "chrdev-test-2022-7-12";
17 struct class *pClass = NULL; //设备文件类指针
18 int devNum = 2; //设备文件数量
19 struct mutex Mutex; //互斥锁
20
21 int testOpen(struct inode *pNode,struct file *pFile)
22 {
23 printk("------into test open------\n");
24 mutex_lock(&Mutex);
25 printk("------leave test open------\n");
26 return 0;
27 }
28 int testClose(struct inode *pNode,struct file *pFile)
29 {
30 printk("------into test close------\n");
31 mutex_unlock(&Mutex);
32 printk("------leave test close------\n");
33 return 0;
34 }
35 ssize_t testRead(struct file *pFile,char __user *buf,size_t count,loff_t *pOffset)
36 {
37 int res = -1;
38 printk("------into testRead------\n");
39 if(count > BUF_SIZE-1)
40 {
41 count = BUF_SIZE - 1;
42 }
43 res = copy_to_user(buf,buff,count);
44 if(res)
45 {
46 printk("copy_to_user error\n");
47 return -EFAULT;
48 }
49 printk("copy_to_user ok\n");
50 printk("\t buff = %s\t\n",buff);
51 printk("------leave testRead------\n");
52 return count;
53 }
54
55 ssize_t testWrite(struct file *pFile,const char __user *buf,size_t count,loff_t *pOffset)
56 {
57 int res = -1;
58 printk("------into testWrite------\n");
59 if(count > BUF_SIZE-1)
60 {
61 count = BUF_SIZE - 1;
62 }
63 res = copy_from_user(buff,buf,count);
64 if(res)
65 {
66 printk("copy_from_user error\n");
67 return -EFAULT;
68 }
69 printk("copy_from_user ok\n");
70 printk("\t buff = %s\t\n",buff);
71 printk("------leave testWrite------\n");
72 return count;
73 }
74
75 long testIoctl(struct file *pFile,unsigned int cmd,unsigned long arg)
76 {
77 switch(cmd)
78 {
79 case TEST_CMD:
80 {
81 printk("test cmd-------arg = %ld \n",arg);
82 }
83 break;
84 case TEST_CMD1:
85 {
86 printk("test cmd1-------arg = %ld \n",arg);
87 }
88 break;
89 case TEST_CMD2:
90 {
91 printk("test cmd2-------arg = %ld \n",arg);
92 }
93 break;
94 default:
95 printk("error cmd \n");
96 }
97 return 0;
98 }
99
100 struct file_operations fp_arr =
101 {
102 .owner = THIS_MODULE,
103 .open = testOpen,
104 .read = testRead,
105 .write = testWrite,
106 .release = testClose,
107 .unlocked_ioctl = testIoctl
108 };
109
110 int driverr_init(void) //模块初始化函数
111 {
112 int res = 0;
113 int i = 0;
114 struct device *pDevTmp = NULL;
115 printk("*********into driver init\n");
116 //动态申请设备号
117 res = alloc_chrdev_region(&deviceNum,min,devNum,deviceName);
118 if(res)
119 {
120 printk("alloc_chrdev_region error\n");
121 return res;
122 }
123 printk("alloc_chrdev_region OK!\n");
124 printk("major = %d minor = %d \n",MAJOR(deviceNum),MINOR(deviceNum));
125 major = MAJOR(deviceNum);
126 //创建设备
127 pCdev = cdev_alloc();
128 if(NULL == pCdev)
129 {
130 printk("cdev_alloc error\n");
131 unregister_chrdev_region(deviceNum,devNum);
132 return -1;
133 }
134 printk("cdev_alloc ok\n");
135 //设备初始化
136 cdev_init(pCdev,&fp_arr);
137 printk("cdev_init ok\n");
138 //设备与设备号关联
139 res = cdev_add(pCdev,deviceNum,devNum);
140 if(res)
141 {
142 printk("cdev_add error\n");
143 cdev_del(pCdev);
144 }
145 printk("cdev_add ok\n");
146 //创建设备文件类
147 pClass = class_create(THIS_MODULE,"mutex");
148 if(NULL == pClass)
149 {
150 printk("class_create error\n");
151 cdev_del(pCdev);
152 }
153 printk("class_create ok\n");
154 //创建设备文件
155 for(;i < devNum;i++)
156 {
157 pDevTmp = device_create(pClass,NULL,MKDEV(major,i),NULL,"mutex%d",i);
158 if(IS_ERR(pDevTmp))
159 {
160 printk("device_create error\n");
161 for(i = 0;i < devNum;i++)
162 {
163 device_destroy(pClass,MKDEV(major,i));
164 }
165 class_destroy(pClass);
166 return -2;
167 }
168 }
169 printk("device_create ok\n");
170 //初始化互斥锁
171 mutex_init(&Mutex);
172 printk("*********leave driver init\n");
173 return 0;
174 }
175
176 void driver_clear(void) //模块清除函数
177 {
178 int i = 0;
179 printk("*********into driver clear\n");
180 for(;i < devNum; i++)
181 {
182 device_destroy(pClass,MKDEV(major,i));
183 }
184 class_destroy(pClass);
185 cdev_del(pCdev);
186 unregister_chrdev_region(deviceNum,devNum);
187 printk("*********leave driver clear\n");
188 }
189
190 module_init(driverr_init); //模块加载函数
191 module_exit(driver_clear); //模块卸载函数
192
193
194 MODULE_LICENSE("GPL");
195 MODULE_AUTHOR("cfy");
196 MODULE_ALIAS("liangzai");
197 MODULE_DESCRIPTION("2022-7-12");
#####测试代码test.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <string.h>
7
8 #define BUF_SIZE 100
9
10 int main()
11 {
12 int fd = open("/dev/mutex0",O_RDWR);
13 if(fd < 0)
14 {
15 printf("open mutex0 err\n");
16 return -1;
17 }
18 printf("open mutex0 ok\n");
19
20 char buf[BUF_SIZE] = {0};
21 //测试读
22 read(fd,buf,BUF_SIZE-1);
23 printf("read data:%s\n",buf);
24 bzero(buf,sizeof(buf));
25
26 //测试写
27 printf(">> input:");
28 scanf("%s",buf);
29 write(fd,buf,sizeof(buf));
30 bzero(buf,sizeof(buf));
31 read(fd,buf,BUF_SIZE-1);
32 printf("write end read data:%s\n",buf);
33
34 close(fd);
35 return 0;
36
37
38 }
test1.c就是打开/dev/mutex1这个文件,其余和test.c一致。
安装加了互斥锁的驱动,修改设备文件权限
先执行test测试程序,再执行test1测试程序。
在test没使用完驱动资源之前,test1是拿不到资源的,光标一直在闪。
让test释放驱动资源。
这样test1拿到驱动资源了,test1读到的数据是test输入的数据。这样就实现了互斥锁的功能。
3.2 信号量
应用
1 定义信号量
struct semaphore sem;//内核信号量
2 初始化信号量
sema_init(&sem,1)//在初始化函数中进行
3 获取信号量
down_interruptible(&sem)相当于进程中的p操作-->sem_wait//在打开函数中进行,可以中断(接收信号)
down(&sem)也可以,不能中断
4 释放信号量
up(&sem)相当于进程中的V操作-->sem_post//在关闭函数中进行
测试
效果与互斥锁一样,代码太长了要不不放了。
3.3 原子变量
1 定义原子变量并初始化
atomic_t a=ATOMIC_INIT(1);
2 打开函数
原子变量只能通过Linux内核提供的接口函数来操作
atomic_dec_and_test(&a):给原子变量a减1,然后测试a的值是否为0,如果为0,该函数返回true,否则返回false
if(!atomic_dec_and_test(&a))
{
return busy;
}
使用驱动资源;
3 关闭函数
atomic_inc(&a)-->给原子变量a加1
测试
代码按照上面写就可以
test占用之后test1使用会报错
3.4 自旋锁
跟互斥锁差不多,代码不放了。
1 定义自旋锁
spinlock_t t;
2 初始化自旋锁
spin_lock_init(&t)-->初始化函数中
3 获取自旋锁
spin_lock(&t)-->打开函数
4 释放自旋锁
spin_unlock(&t)-->关闭函数