我是靠谱客的博主 清爽盼望,最近开发中收集的这篇文章主要介绍Linux驱动开发 -----互斥和同步(上),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

并发与并行

并发是指一个处理器同时处理多个任务。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。

并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。所以无论从微观还是从宏观来看,二者都是一起执行的。

并发与竞态:

那些可能会引起竞态
1、多个应用层序(进程)访问驱动
2、对称多处理器(SMP)的多个CPU
3、单CPU内进程与抢占它的进程 Linux 2.6内核支持抢占调度,一个进程在内核执行的时候可能被另一高优先级进程打断,
进程与抢占它的进程访问共享资源的情况类似于SMP的多个CPU。
4、中断(硬中断、软中断、Tasklet、底半部)与进程之间中断可以打断正在执行的进程,
如果中断处理程序访问进程正在访问的资源,则竞态也会发生。

**

内核阶段如何去处理竞态: ---- 五套机制

1、中断屏蔽
2、原子变量
3、自旋锁
4、信号量
5、互斥体

不同的机制有不同的特点和应用场景。

1、中断屏蔽

屏蔽中断容易导致kenel down掉,不推荐使用,如果要使用的话,临界区的操作应尽可能的短
—>只能够屏蔽当前CPU
屏蔽中断
local_irq_disable() /* 屏蔽中断 /
critical section /
临界区*/
开启中断
local_irq_enable() /* 开中断*/

屏蔽中断的时候把中断状态保存
local_irq_save(flags)
critical section /* 临界区*/
开启中断,并回复中断状态
local_irq_restore(flags)

注意:local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU内的中断,因此,并不能解决SMP多CPU引发的竞态

2、原子变量

特点:最小的可操作单位 原子操作 atomic 适用于变量,操作不可分割或被打断
atomic_t
本质:typedef struct {
int counter;
} atomic_t;
1、变量的定义
atomic_t myatomic;
2、变量的初始化
#define ATOMIC_INIT(i) { (i) }
#define atomic_set(v,i) (((v)->counter) = (i))
atomic_set(myatomic,1);
3、加锁-解锁
#define atomic_inc(v) atomic_add(1, v) //变量值加1
#define atomic_dec(v) atomic_sub(1, v) //变量值减1

#define atomic_inc_and_test(v)	(atomic_add_return(1, v) == 0)
//加1之后判断是否为0,
如果 == 0 为真,1,如果不为0,为假,0
#define atomic_dec_and_test(v)	(atomic_sub_return(1, v) == 0)
//减1之后判断是否为0,
如果 == 0 为真,1,如果不为0,为假,0

static inline int atomic_add_return(int i, atomic_t *v)
{
unsigned long flags;
int val;
raw_local_irq_save(flags);
val = v->counter;
v->counter = val += i;
raw_local_irq_restore(flags);
return val;
}
--------解决什么问题啊? 举个例子(伪代码):
多个进程同时控制灯,无法达到用户预期
A : open
atomic_dec(1)
xxxxxdemo_ioctl
---------
=0
close
atomic_inc(v)
B :
if(!atomic_dec_and_test(v)){
printf("设备忙,等一等吧.n");
atomic_inc(v)
return -EBUSY;
}
整合:
demo_open()
{
if(!atomic_dec_and_test(v)){
printf("设备忙,等一等吧.n");
atomic_inc(v)
return -EBUSY;
}
//atomic_dec(1);
}
demo_close()
{
atomic_inc(v)
}

代码实例(之前流水灯的代码):

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/atomic.h>
#define FS4412LEDON
1
#define FS4412LEDOFF 0
#define GPX2CON
0x11000C40
#define GPX2_7DAT 0x11000c44
#define GPX1CON 0x11000c20
#define GPX1DAT 0x11000c24
#define GPF3CON 0x114001e0
#define GPF3DAT 0x114001e4
void __iomem * gpx2con_vir;
void __iomem * gpx2dat_vir;
void __iomem * gpx1con_vir;
void __iomem * gpx1dat_vir;
void __iomem * gpf3con_vir;
void __iomem * gpf3dat_vir;
typedef struct led_desc{
int which;
int state;
}led_desc_t;
/*
ioremap
void __iomem *ioremap(phys_addr_t addr, unsigned long size)
功能:将物理地址映射为虚拟地址
参数:
@addr 物理地址
@size 映射的大小
返回值:成功返回虚拟地址,
失败返回NULL
*/
atomic_t myatomic;//定义一个原自变量
struct cdev *cdev;//定义一个字符设备对象
unsigned int major=0;//定义主设备号
const char *name="ledchar";//定义设备名
struct class *cls;
struct device *device;
int kbuf[5]={1,2,3,4,5};
int led_open (struct inode *inode, struct file *filp)
{
if(!atomic_dec_and_test(&myatomic)){
printk("设备忙!稍等!");
atomic_inc(&myatomic);//原自变量自行加一
return -EBUSY;//操作被禁止
}
printk("OPEN!n");
return 0;
}
int led_close (struct inode *inode, struct file *filp)
{
printk("CLOSE!n");
atomic_inc(&myatomic);//原自变量自行加一,表示上一个操作已经结束,下一个操作可以被访问
return 0;
}
ssize_t led_read (struct file *filp, char __user *usrbuf, size_t size, loff_t *offset)
{
int readby;
printk("READ!n");
readby=copy_to_user(usrbuf,kbuf, 20);
/*
copy_to_user: - Copy a block of data into user space.
* Copy data from kernel space to user space.
* @to:
Destination address, in user space.
* @from: Source address, in kernel space.
* @n:
Number of bytes to copy.
*/
return readby;
}
ssize_t led_write (struct file *filp, const char __user *usrbuf, size_t size, loff_t *offset)
{
int writeby;
printk("WRITE!n");
writeby=copy_from_user(kbuf, usrbuf,20);
/*
* copy_from_user: - Copy a block of data from user space.
* @to:
Destination address, in kernel space.
* @from: Source address, in user space.
* @n:
Number of bytes to copy.
* Copy data from user space to kernel space.
*/
return writeby;
}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
int lednum=0;
led_desc_t *led=(led_desc_t *)args;//通过函数传参决定是哪一个灯,与应用层对应
printk("IO!n");
switch(cmd){
case FS4412LEDON:
lednum = led->which;
if(lednum==2){
printk("led2 onn");
writel((readl(gpx2dat_vir) | 0x1 << 7),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 onn");
writel(readl(gpx1dat_vir) | (0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 onn");
writel(readl(gpf3dat_vir) | (0x1 << 4),gpf3dat_vir);
}
else if(lednum==5){
printk("led5 onn");
printk("fsled %d on.n",lednum);
}
break;
case FS4412LEDOFF:
lednum = led->which;
if(lednum==2){
printk("led2 onn");
writel((readl(gpx2dat_vir) & ~(0x1 << 7)),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 onn");
writel(readl(gpx1dat_vir)&~(0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 onn");
printk("fsled %d off.n",lednum);
}
else if(lednum==5){
printk("led5 onn");
printk("fsled %d off.n",lednum);
}
break;
default:
break;
}
return 0;
}
void gpio_ioremap(void)
{
gpx2con_vir = ioremap(GPX2CON, 4);
gpx2dat_vir = gpx2con_vir +4;
gpx1con_vir = ioremap(GPX1CON,4);
gpx1dat_vir = gpx1con_vir + 4;
gpf3con_vir = ioremap(GPF3CON,4);
gpf3dat_vir = gpf3con_vir + 4;
writel((readl(gpx2dat_vir) | 0x0 << 7),gpx2dat_vir);
writel(( readl(gpx2con_vir) & (~(0xF << 28))) | 0x1 << 28,gpx2con_vir);
writel((readl(gpx1con_vir) & ~(0XF<< 0))| (0x1 << 0),gpx1con_vir);
writel((readl(gpf3con_vir) & ~(0XFF<< 16 ))| (0x11 << 16),gpf3con_vir);
}
void gpio_iounmap(void)
{
iounmap(gpx2con_vir);
iounmap(gpx1con_vir);
iounmap(gpf3con_vir);
}
const struct file_operations fops={
.open=led_open,
.release=led_close,
.read=led_read,
.write=led_write,
.unlocked_ioctl=led_ioctl,
};//定义操纵方法集
static int __init led_init(void)
{
printk("INITn");
atomic_set(&myatomic, 1);//初始化原子变量
//1.创建并注册一个设备节点
major=register_chrdev(0, name,&fops);
/*register_chrdev函数用法:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
其中参数major如果等于0,则表示采用系统动态分配的主设备号;不为0,则表示静态注册。
name
:设备的名字
fops
:操作方法集
返回值:
* If @major == 0 返回主设备号major
* If @major > 0 成功返回0
失败返回错误码
*/
//判断字符设备是否注册成功
if(major<=0){
printk("register_chrdev failed!n");
return major;
}
printk("major :%dn",major);//如果注册成功打印主设备号major
//2.自动创建设备节点
//(1)创建一个class目录
cls=class_create(THIS_MODULE, "led");
/*#define class_create(owner, name)

({

static struct lock_class_key __key;	
__class_create(owner, name, &__key);	
})
功能:**创建一个目录
目录位于 -- /sys/class/xxx**
参数:
@owner: THIS_MODULE 第一个参数指定类的所有者是哪个模块
@name : 字符设备 第二个参数指定类名。
返回值:
struct class *_
句柄 cls
*/
if(cls==NULL){
printk("class_create faild!n");
goto err0;
}
//(2)自动创建设备节点
device = device_create(cls, NULL,MKDEV(major,0), NULL,name,0);
/*
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
功能:创建一个设备节点
参数:
@class: THIS_MODULE
指定所要创建的设备所从属的类
@parent : NULL
这个设备的父设备,如果没有就指定为NULL
@devt
:devt 设备号
@drvdata:驱动的私有数据
@fmt	:可变参数
一般是设备名
}
*/
if(device==NULL){
printk("device_create failed!");
goto err1;
}
return 0;
err1:device_destroy(cls, MKDEV(major,0));
err0:unregister_chrdev(major,name);//释放所注册的字符设备
return -1;
}
static void __exit led_exit(void)
{
printk("EXIT!n");
unregister_chrdev(major,name);//释放所注册的字符设备
/*
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
*/
class_destroy(cls);//释放创建的class目录
device_destroy(cls,
MKDEV(major,0));//释放创建的设备节点
gpio_iounmap();
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

原子操作不可被分割,原子变量虽然使用方便,但其本质是一个整型变量,对于非整型变量不可以使用。

3、自旋锁

spinlock_t 主要用于解决SMP或单CPU可抢占的问题:
忙等锁–占CPU,容易导致死锁
且加锁后不能进行睡眠或调度操作,或调用能够引起睡眠或调度的操作等 spinlock +普通变量 = 原子操作


忙等锁 : 不停的询问,CPU闲了吗?
上锁
------------------------------
|
上锁 ----------------------->|
睡眠
|
CPU使用权
|
调度 schedule
|========>死锁
|	能够引起睡眠或调度的操作的函数
|
--------------------------------
|
解锁
spinlock:
本质:
typedef struct spinlock {
union {
struct raw_spinlock rlock;
typedef struct raw_spinlock {
arch_spinlock_t raw_lock;
typedef struct {
union {
**u32 slock;**
};
} arch_spinlock_t;
} raw_spinlock_t;
};
} spinlock_t;
1、变量的定义
spinlock_t myspinlock;
2、变量的初始化
spin_lock_init(&myspinlock);
3、加锁
spin_lock(spinlock_t *lock)
可以使用这种
int spin_trylock(spinlock_t *lock) //尝试枷锁
成功要么返回0,要么返回1
4、解锁
spin_unlock(spinlock_t *lock)

代码实例:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/atomic.h>
#define FS4412LEDON
1
#define FS4412LEDOFF 0
#define GPX2CON
0x11000C40
#define GPX2_7DAT 0x11000c44
#define GPX1CON 0x11000c20
#define GPX1DAT 0x11000c24
#define GPF3CON 0x114001e0
#define GPF3DAT 0x114001e4
void __iomem * gpx2con_vir;
void __iomem * gpx2dat_vir;
void __iomem * gpx1con_vir;
void __iomem * gpx1dat_vir;
void __iomem * gpf3con_vir;
void __iomem * gpf3dat_vir;
typedef struct led_desc{
int which;
int state;
}led_desc_t;
/*
ioremap
void __iomem *ioremap(phys_addr_t addr, unsigned long size)
功能:将物理地址映射为虚拟地址
参数:
@addr 物理地址
@size 映射的大小
返回值:成功返回虚拟地址,
失败返回NULL
*/
spinlock_t myspinlock;//定义一把锁
int source=1;
struct cdev *cdev;//定义一个字符设备对象
unsigned int major=0;//定义主设备号
const char *name="ledchar";//定义设备名
struct class *cls;
struct device *device;
int kbuf[5]={1,2,3,4,5};
int led_open (struct inode *inode, struct file *filp)
{
if(source<=0){
printk("设备忙!请稍等!n");
return -EBUSY;
}
spin_lock(&myspinlock);//加锁
msleep(2000);
source-=source;//减1
spin_unlock(&myspinlock);//解锁
printk("OPEN!n");
return 0;
}
int led_close (struct inode *inode, struct file *filp)
{
spin_lock(&myspinlock);//加锁
source+=source;//加1
spin_unlock(&myspinlock);//解锁
printk("CLOSE!n");
return 0;
}
ssize_t led_read (struct file *filp, char __user *usrbuf, size_t size, loff_t *offset)
{
int readby;
printk("READ!n");
readby=copy_to_user(usrbuf,kbuf, 20);
/*
copy_to_user: - Copy a block of data into user space.
* Copy data from kernel space to user space.
* @to:
Destination address, in user space.
* @from: Source address, in kernel space.
* @n:
Number of bytes to copy.
*/
return readby;
}
ssize_t led_write (struct file *filp, const char __user *usrbuf, size_t size, loff_t *offset)
{
int writeby;
printk("WRITE!n");
writeby=copy_from_user(kbuf, usrbuf,20);
/*
* copy_from_user: - Copy a block of data from user space.
* @to:
Destination address, in kernel space.
* @from: Source address, in user space.
* @n:
Number of bytes to copy.
* Copy data from user space to kernel space.
*/
return writeby;
}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
int lednum=0;
led_desc_t *led=(led_desc_t *)args;//通过函数传参决定是哪一个灯,与应用层对应
printk("IO!n");
switch(cmd){
case FS4412LEDON:
lednum = led->which;
if(lednum==2){
printk("led2 onn");
writel((readl(gpx2dat_vir) | 0x1 << 7),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 onn");
writel(readl(gpx1dat_vir) | (0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 onn");
writel(readl(gpf3dat_vir) | (0x1 << 4),gpf3dat_vir);
}
else if(lednum==5){
printk("led5 onn");
printk("fsled %d on.n",lednum);
}
break;
case FS4412LEDOFF:
lednum = led->which;
if(lednum==2){
printk("led2 onn");
writel((readl(gpx2dat_vir) & ~(0x1 << 7)),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 onn");
writel(readl(gpx1dat_vir)&~(0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 onn");
printk("fsled %d off.n",lednum);
}
else if(lednum==5){
printk("led5 onn");
printk("fsled %d off.n",lednum);
}
break;
default:
break;
}
return 0;
}
void gpio_ioremap(void)
{
gpx2con_vir = ioremap(GPX2CON, 4);
gpx2dat_vir = gpx2con_vir +4;
gpx1con_vir = ioremap(GPX1CON,4);
gpx1dat_vir = gpx1con_vir + 4;
gpf3con_vir = ioremap(GPF3CON,4);
gpf3dat_vir = gpf3con_vir + 4;
writel((readl(gpx2dat_vir) | 0x0 << 7),gpx2dat_vir);
writel(( readl(gpx2con_vir) & (~(0xF << 28))) | 0x1 << 28,gpx2con_vir);
writel((readl(gpx1con_vir) & ~(0XF<< 0))| (0x1 << 0),gpx1con_vir);
writel((readl(gpf3con_vir) & ~(0XFF<< 16 ))| (0x11 << 16),gpf3con_vir);
}
void gpio_iounmap(void)
{
iounmap(gpx2con_vir);
iounmap(gpx1con_vir);
iounmap(gpf3con_vir);
}
const struct file_operations fops={
.open=led_open,
.release=led_close,
.read=led_read,
.write=led_write,
.unlocked_ioctl=led_ioctl,
};//定义操纵方法集
static int __init led_init(void)
{
printk("INITn");
spin_lock_init(&myspinlock);//初始化锁
//1.创建并注册一个设备节点
major=register_chrdev(0, name,&fops);
/*register_chrdev函数用法:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
其中参数major如果等于0,则表示采用系统动态分配的主设备号;不为0,则表示静态注册。
name
:设备的名字
fops
:操作方法集
返回值:
* If @major == 0 返回主设备号major
* If @major > 0 成功返回0
失败返回错误码
*/
//判断字符设备是否注册成功
if(major<=0){
printk("register_chrdev failed!n");
return major;
}
printk("major :%dn",major);//如果注册成功打印主设备号major
//2.自动创建设备节点
//(1)创建一个class目录
cls=class_create(THIS_MODULE, "led");
/*#define class_create(owner, name)

({

static struct lock_class_key __key;	
__class_create(owner, name, &__key);	
})
功能:**创建一个目录
目录位于 -- /sys/class/xxx**
参数:
@owner: THIS_MODULE 第一个参数指定类的所有者是哪个模块
@name : 字符设备 第二个参数指定类名。
返回值:
struct class *_
句柄 cls
*/
if(cls==NULL){
printk("class_create faild!n");
goto err0;
}
//(2)自动创建设备节点
device = device_create(cls, NULL,MKDEV(major,0), NULL,name,0);
/*
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
功能:创建一个设备节点
参数:
@class: THIS_MODULE
指定所要创建的设备所从属的类
@parent : NULL
这个设备的父设备,如果没有就指定为NULL
@devt
:devt 设备号
@drvdata:驱动的私有数据
@fmt	:可变参数
一般是设备名
}
*/
if(device==NULL){
printk("device_create failed!");
goto err1;
}
return 0;
err1:device_destroy(cls, MKDEV(major,0));
err0:unregister_chrdev(major,name);//释放所注册的字符设备
return -1;
}
static void __exit led_exit(void)
{
printk("EXIT!n");
unregister_chrdev(major,name);//释放所注册的字符设备
/*
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
*/
class_destroy(cls);//释放创建的class目录
device_destroy(cls,
MKDEV(major,0));//释放创建的设备节点
gpio_iounmap();
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

最后

以上就是清爽盼望为你收集整理的Linux驱动开发 -----互斥和同步(上)的全部内容,希望文章能够帮你解决Linux驱动开发 -----互斥和同步(上)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部