当前位置:网站首页>嵌入式系统驱动初级【4】——字符设备驱动基础下_并发控制
嵌入式系统驱动初级【4】——字符设备驱动基础下_并发控制
2022-08-04 05:21:00 【imysy_22_】
为了实现多个进程同时使用我们的字符设备,我们引入“并发控制”这一概念。
一、上下文和并发场合
执行流:有开始有结束总体顺序执行的一段代码 又称上下文
应用编程:任务上下文
内核编程:
1. 任务上下文:五状态 可阻塞
a. 应用进程或线程运行在用户空间
b. 应用进程或线程运行在内核空间(通过调用syscall来间接使用内核空间)
c. 内核线程始终在内核空间
2. 异常上下文:不可阻塞
中断上下文
竞态:多任务并行执行时,如果在一个时刻同时操作同一个资源,会引起资源的错乱,这种错乱情形被称为竞态
共享资源:可能会被多个任务同时使用的资源
临界区:操作共享资源的代码段
为了解决竞态,需要提供一种控制机制,来避免在同一时刻使用共享资源,这种机制被称为并发控制机制
并发控制机制分类:
1. 原子操作类
2. 忙等待类
3. 阻塞类
通用并发控制机制的一般使用套路:
/*互斥问题:*/
并发控制机制初始化为可用
P操作
临界区
V操作
/*同步问题:*/
//并发控制机制初始化为不可用
//先行方:
。。。。。
V操作
//后行方:
P操作
。。。。。
二、中断屏蔽(了解)
一种同步机制的辅助手段
禁止本cpu中断 使能本cpu中断
local_irq_disable(); local_irq_enable();
local_irq_save(flags); local_irq_restore(flags); 与cpu的中断位相关
local_bh_disable(); local_bh_enable(); 与中断低半部有关,关闭、打开软中断
禁止中断
临界区 //临界区代码不能占用太长时间,需要很快完成
打开中断
适用场合:中断上下文与某任务共享资源时,或多个不同优先级的中断上下文间共享资源时
三、原子变量(掌握)
原子变量:存取不可被打断的特殊整型变量
a.设置原子量的值
void atomic_set(atomic_t *v,int i); //设置原子量的值为i
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
v = 10;//错误
b.获取原子量的值
atomic_read(atomic_t *v); //返回原子量的值
c.原子变量加减
void atomic_add(int i,atomic_t *v);//原子变量增加i
void atomic_sub(int i,atomic_t *v);//原子变量减少i
d.原子变量自增自减
void atomic_inc(atomic_t *v);//原子变量增加1
void atomic_dec(atomic_t *v);//原子变量减少1
e.操作并测试:运算后结果为0则返回真,否则返回假
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);
原子位操作方法:
a.设置位
void set_bit(nr, void *addr); //设置addr的第nr位为1
b.清除位
void clear_bit(nr , void *addr); //清除addr的第nr位为0
c.改变位
void change_bit(nr , void *addr); //改变addr的第nr位为1
d.测试位
void test_bit(nr , void *addr); //测试addr的第nr位是否为1
适用场合:共享资源为单个整型变量的互斥场合
示例代码:atomic.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/atomic.h>
struct openonce_dev {
struct cdev mydev;
atomic_t open_flag;
};
int major = 11;
int minor = 0;
int num = 1;
struct openonce_dev gmydev;
int myopen (struct inode *pnode, struct file *pfile)
{
struct openonce_dev *pgmydev = NULL;
pfile->private_data = (void *)container_of(pnode->i_cdev,struct openonce_dev,mydev);
pgmydev = (struct openonce_dev *)pfile->private_data;
if(atomic_dec_and_test(&pgmydev->open_flag))
{
return 0;
}
else
{
atomic_inc(&pgmydev->open_flag);
printk("The device is open already\n");
return -1;
}
return 0;
}
int myclose (struct inode *pnode, struct file *pfile)
{
struct openonce_dev *pgmydev = (struct openonce_dev *)pfile->private_data;
atomic_set(&gmydev.open_flag,1);
return 0;
}
struct file_operations myops = {
.owner = THIS_MODULE,
.open = myopen,
.release = myclose,
};
int __init atomic_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major,minor);
ret = register_chrdev_region(devno,num,"openonce");
if(ret)
{
ret = alloc_chrdev_region(&devno,minor,num,"openonce");
if(ret)
{
printk("devno failed.\n");
return -1;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
/*给mydev指定操作函数集*/
cdev_init(&gmydev.mydev,&myops);
/*将mydev添加到内核对应的数据结构里*/
gmydev.mydev.owner = THIS_MODULE;
cdev_add(&gmydev.mydev,devno,num);
atomic_set(&gmydev.open_flag,1);
return 0;
}
void __exit atomic_exit(void)
{
dev_t devno = MKDEV(major,minor);
cdev_del(&gmydev.mydev);
unregister_chrdev_region(devno, num);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("imysy_22");
MODULE_DESCRIPTION("This is a script explained by the author who's name is imysy_22.");
MODULE_ALIAS("HI");
module_init(atomic_init);
module_exit(atomic_exit);
openonce_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("The argument is too few.\n");
return -1;
}
int fd = -1;
fd = open(argv[1],O_RDONLY);
if(fd < 0)
{
printf("open failed\n");
return -1;
}
while(1)
{
}
close(fd);
fd = -1;
return 0;
}
Makefile:
ifeq ($(KERNELRELEASE),)
ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/fs4412/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
clean:
rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
else
obj-m += atomic.o
endif



同一时刻只有一个应用程序app能打开这个驱动。
四、自旋锁:基于忙等待的并发控制机制
a.定义自旋锁
spinlock_t lock;
b.初始化自旋锁
spin_lock_init(spinlock_t *);
c.获得自旋锁
spin_lock(spinlock_t *); //成功获得自旋锁立即返回,否则自旋在那里直到该自旋锁的保持者释放
spin_trylock(spinlock_t *); //成功获得自旋锁立即返回真,否则返回假,而不是像上一个那样"在原地打转”
d.释放自旋锁
spin_unlock(spinlock_t *);
```
#include <linux/spinlock.h>
定义spinlock_t类型的变量lock
spin_lock_init(&lock)后才能正常使用spinlock
spin_lock(&lock);
临界区
spin_unlock(&lock);
```
适用场合:
1. 异常上下文之间或异常上下文与任务上下文之间共享资源时
2. 任务上下文之间且临界区执行时间很短时
3. 互斥问题
示例代码:spinlock.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/spinlock.h>
struct openonce_dev {
struct cdev mydev;
int open_flag;
spinlock_t lock;
};
int major = 11;
int minor = 0;
int num = 1;
struct openonce_dev gmydev;
int myopen (struct inode *pnode, struct file *pfile)
{
struct openonce_dev *pgmydev = NULL;
pfile->private_data = (void *)container_of(pnode->i_cdev,struct openonce_dev,mydev);
pgmydev = (struct openonce_dev *)pfile->private_data;
spin_lock(&pgmydev->lock);
if(pgmydev->open_flag)
{
spin_unlock(&pgmydev->lock);
pgmydev->open_flag = 0;
return 0;
}
else
{
spin_unlock(&pgmydev->lock);
printk("The device is open already\n");
return -1;
}
return 0;
}
int myclose (struct inode *pnode, struct file *pfile)
{
struct openonce_dev *pgmydev = (struct openonce_dev *)pfile->private_data;
spin_lock(&pgmydev->lock);
pgmydev->open_flag = 1;
spin_unlock(&pgmydev->lock);
return 0;
}
struct file_operations myops = {
.owner = THIS_MODULE,
.open = myopen,
.release = myclose,
};
int __init spinlock_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major,minor);
ret = register_chrdev_region(devno,num,"openonce");
if(ret)
{
ret = alloc_chrdev_region(&devno,minor,num,"openonce");
if(ret)
{
printk("devno failed.\n");
return -1;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
/*给mydev指定操作函数集*/
cdev_init(&gmydev.mydev,&myops);
/*将mydev添加到内核对应的数据结构里*/
gmydev.mydev.owner = THIS_MODULE;
cdev_add(&gmydev.mydev,devno,num);
gmydev.open_flag = 1;
spin_lock_init(&gmydev.lock);
return 0;
}
void __exit spinlock_exit(void)
{
dev_t devno = MKDEV(major,minor);
cdev_del(&gmydev.mydev);
unregister_chrdev_region(devno, num);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("imysy_22");
MODULE_DESCRIPTION("This is a script explained by the author who's name is imysy_22.");
MODULE_ALIAS("HI");
module_init(spinlock_init);
module_exit(spinlock_exit);
其他和原子变量一模一样
五、信号量:基于阻塞的并发控制机制
a.定义信号量
struct semaphore sem;
b.初始化信号量
void sema_init(struct semaphore *sem, int val);
c.获得信号量P
int down(struct semaphore *sem);//深度睡眠
int down_interruptible(struct semaphore *sem);//浅度睡眠
d.释放信号量V
void up(struct semaphore *sem);
```
#include <linux/semaphore.h>
```
适用场合:任务上下文之间且临界区执行时间较长时的互斥或同步问题
六、互斥锁:基于阻塞的互斥机制
a.初始化
struct mutex my_mutex;
mutex_init(&my_mutex);
b.获取互斥体
void mutex_lock(struct mutex *lock);
c.释放互斥体
void mutex_unlock(struct mutex *lock);
1. 定义对应类型的变量
2. 初始化对应变量
P/加锁
临界区
V/解锁
```
#include <linux/mutex.h>
```
适用场合:任务上下文之间且临界区执行时间较长时的互斥问题
七、选择并发控制机制的原则
1. 不允许睡眠的上下文需要采用忙等待类,可以睡眠的上下文可以采用阻塞类。在异常上下文中访问的竞争资源一定采用忙等待类。
2. 临界区操作较长的应用建议采用阻塞类,临界区很短的操作建议采用忙等待类。
3. 中断屏蔽仅在有与中断上下文共享资源时使用。
4. 共享资源仅是一个简单整型量时用原子变量
边栏推荐
- word 公式编辑器 键入技巧 | 写数学作业必备速查表
- [21 Days Learning Challenge] Image rotation problem (two-dimensional array)
- 高性能高可靠性高扩展性分布式防火墙架构
- [Cloud Native--Kubernetes] Pod Resource Management and Probe Detection
- C专家编程 第5章 对链接的思考 5.6 轻松一下---看看谁在说话:挑战Turning测验
- Chapter 5 C programming expert thinking 5.4 alert Interpositioning of links
- [Cocos] cc.sys.browserType可能的属性
- [Evaluation model] Topsis method (pros and cons distance method)
- Converts XML tags to TXT format (voc conversion for yolo convenient training)
- The difference between px, em, and rem
猜你喜欢

关于yolo7和gpu

企业需要知道的5个 IAM 最佳实践

Turn: Management is the love of possibility, and managers must have the courage to break into the unknown

编程大杂烩(四)

TSF微服务治理实战系列(一)——治理蓝图

How to keep the source code confidential in the development under the burning scenario

Converts XML tags to TXT format (voc conversion for yolo convenient training)

FPGA学习笔记——知识点总结
![[C language advanced] program environment and preprocessing](/img/ac/a13dd2cc47136d4938b6fc7fad660c.png)
[C language advanced] program environment and preprocessing

《看见新力量》第四期免费下载!走进十五位科技创业者的精彩故事
随机推荐
See how DevExpress enriches chart styles and how it empowers fund companies to innovate their business
3面头条,花7天整理了面试题和学习笔记,已正式入职半个月
What is the salary of a software testing student?
详解八大排序
力扣:509. 斐波那契数
关于yolo7和gpu
[21 Days Learning Challenge] Image rotation problem (two-dimensional array)
The 2022 PMP exam has been delayed, should we be happy or worried?
信息学奥赛一本通 1312:【例3.4】昆虫繁殖
The symbol table
Interesting Kotlin 0x0E: DeepRecursiveFunction
符号表
Converts XML tags to TXT format (voc conversion for yolo convenient training)
【一步到位】Jenkins的安装、部署、启动(完整教程)
BFC、IFC、GFC、FFC概念理解、布局规则、形成方法、用处浅析
Use Patroni callback script to bind VIP pit
高性能高可靠性高扩展性分布式防火墙架构
day13--postman接口测试
el-Select selector bottom fixed
[Cloud Native--Kubernetes] Pod Resource Management and Probe Detection