当多个应用程序访问同一个驱动的临界资源的时候,竞态就会产生了。
产生竞态的根本原因?
1.对于单核处理器,如果内核支持抢占就会产生竞态
2.多核处理器,核心与核心之间本身就会产生竞态。
3.中断和进程间也会产生竞态
中断屏蔽
中断屏蔽:中断屏蔽只针对单核处理器有效,中断屏蔽顾名思义就是将中断关闭掉,
当中断关闭掉之后就不会产生竞态了。中断屏蔽的时间要尽可能的短,否则会出现
用户数据的丢失或者内核的崩溃。
函数接口API:
local_irq_disable(); //屏蔽中断
//临界资源
local_irq_enable(); //开启中断
自旋锁(重点掌握)
自旋锁:针对多核设计的,当一个进程获取到自旋锁之后,另外一个进程也想获取
这把锁,此时后一个进程处于自旋状态(原地打转)(自旋锁又叫盲等锁)
特点:
1.自旋状态是需要消耗cpu资源的
2.自旋锁适合用在临界区比较小地方,临界区不能有延时,耗时,甚至休眠的操作,
临界区也不能够有copy_to_user/copy_from_user的函数。
3.自旋锁会导致死锁(在同一个进程中多次获取同一把未解锁的锁)
4.自旋锁可以在进程上下文中使用,也可以在中断上下文中使用
5.自旋锁在上锁前会关闭抢占
自旋锁的API:
spinlock_t lock; //定义自旋锁
spin_lock_init(&lock); //初始化自旋锁
spin_lock(&lock); //上锁
spin_unlock(&lock); //解锁
自旋锁的实例
mycdev.c
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
spinlock_t lock; //定义自旋锁
int flags=0;
int mycdev_open(struct inode *inode, struct file *file)
{
spin_lock(&lock); //上锁
if(flags !=0 ){
spin_unlock(&lock);
return -EBUSY;
}
flags=1;
spin_unlock(&lock);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf,
size_t size, loff_t *offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy data to user error\n");
return -EIO;
} // shift+alt+f 代码格式化
return size;
}
ssize_t mycdev_write(struct file *file,
const char __user *ubuf, size_t size, loff_t *off)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy data from user error\n");
return -EIO;
}
return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
spin_lock(&lock);
flags=0;
spin_unlock(&lock); //解锁
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int i, ret;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象的初始化
cdev_init(cdev, &fops);
// 3.申请设备号
if (major == 0)
{
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret)
{
printk("dynamic:alloc device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
else if (major > 0)
{
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret)
{
printk("static:alloc device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret)
{
printk("cdev register error\n");
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls))
{
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = 0; i < count; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
//初始化自旋锁
spin_lock_init(&lock);
return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
// 1.销毁设备节点
for (i = 0; i < count; i++)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
// 2.销毁字符设备驱动
cdev_del(cdev);
// 3.销毁设备号
unregister_chrdev_region(MKDEV(major, minor), count);
// 4.释放动态申请的cdev内存
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include
int main(int argc,const char * argv[])
{
int fd;
char buf[128] = "i am fine..........\n";
if((fd = open("/dev/mycdev0",O_RDWR))==-1){
perror("open error");
exit(EXIT_FAILURE);
}
write(fd,buf,sizeof(buf));
memset(buf,0,sizeof(buf));
sleep(7);
read(fd,buf,sizeof(buf));
printf("user read buf = %s\n",buf);
close(fd);
return 0;
}
信号量(重点掌握)
信号量:当一个进程获取到信号量之后,另外一个进程也想获取信号量,此时后一个进程处于休眠状态。
特点:
1.休眠的进程是不消耗cpu资源的
2.信号量不会导致死锁
3.信号量保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。
4.信号量只能工作在进程上下文。不能工作于中断上下文。
5.信号量也不会关闭抢占
信号量的API:
struct semaphore sem; //定义信号量
void sema_init(struct semaphore *sem, int val) //初始化信号量
//注意,这里的val只有初始化为1的时候才有能够解决竞态。当val初始化为0的时候代表同步机制
void down(struct semaphore *sem); //上锁
int down_trylock(struct semaphore *sem); //尝试获取锁 ,成功返回0,失败返回1,不会阻塞
void up(struct semaphore *sem);//解锁
使用信号量解决竞态问题的实例
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
struct semaphore sem; //定义信号量
int mycdev_open(struct inode *inode, struct file *file)
{
//down(&sem);
if(down_trylock(&sem)){ //尝试获取锁
return -EBUSY;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf,
size_t size, loff_t *offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy data to user error\n");
return -EIO;
} // shift+alt+f 代码格式化
return size;
}
ssize_t mycdev_write(struct file *file,
const char __user *ubuf, size_t size, loff_t *off)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy data from user error\n");
return -EIO;
}
return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
up(&sem); //解锁
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int i, ret;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象的初始化
cdev_init(cdev, &fops);
// 3.申请设备号
if (major == 0)
{
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret)
{
printk("dynamic:alloc device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
else if (major > 0)
{
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret)
{
printk("static:alloc device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret)
{
printk("cdev register error\n");
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls))
{
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = 0; i < count; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
//初始化信号量
sema_init(&sem,1);
return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
// 1.销毁设备节点
for (i = 0; i < count; i++)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
// 2.销毁字符设备驱动
cdev_del(cdev);
// 3.销毁设备号
unregister_chrdev_region(MKDEV(major, minor), count);
// 4.释放动态申请的cdev内存
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include
int main(int argc,const char * argv[])
{
int fd;
char buf[128] = "i am fine..........\n";
if((fd = open("/dev/mycdev0",O_RDWR))==-1){
perror("open error");
exit(EXIT_FAILURE);
}
write(fd,buf,sizeof(buf));
memset(buf,0,sizeof(buf));
sleep(7);
read(fd,buf,sizeof(buf));
printf("user read buf = %s\n",buf);
close(fd);
return 0;
}
互斥体(掌握)
互斥体:当一个进程获取到互斥体之后,另外一个进程也想获取互斥体,此时后一个进程处于休眠状态。
特点:
1.休眠的进程是不消耗cpu资源的
2.互斥体不会导致死锁
3.互斥体保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。
4.互斥体只能工作在进程上下文。不能工作于中断上下文。
5.互斥体也不会关闭抢占
6.互斥体在进入休眠前会稍微等一会儿在进入休眠状态,但是对于临界区比较小
的时候使用互斥体的效率是高于信号量的,优先使用互斥体
互斥体的API
struct mutex mutex; //定义互斥体
mutex_init(&mutex); //初始化互斥体
void mutex_lock(struct mutex *lock) //上锁
int mutex_trylock(struct mutex *lock) //尝试获取锁,成功返回1,失败返回0
static int mutex_unlock(unsigned long *lock) //解锁
使用互斥体解决竞态问题的实例
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
struct mutex mutex;//定义互斥体
int mycdev_open(struct inode *inode, struct file *file)
{
if(!mutex_trylock(&mutex)){
return -EBUSY;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf,
size_t size, loff_t *offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy data to user error\n");
return -EIO;
} // shift+alt+f 代码格式化
return size;
}
ssize_t mycdev_write(struct file *file,
const char __user *ubuf, size_t size, loff_t *off)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy data from user error\n");
return -EIO;
}
return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
mutex_unlock(&mutex);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int i, ret;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象的初始化
cdev_init(cdev, &fops);
// 3.申请设备号
if (major == 0)
{
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret)
{
printk("dynamic:alloc device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
else if (major > 0)
{
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret)
{
printk("static:alloc device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret)
{
printk("cdev register error\n");
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls))
{
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = 0; i < count; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
mutex_init(&mutex); //初始化互斥体
return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
// 1.销毁设备节点
for (i = 0; i < count; i++)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
// 2.销毁字符设备驱动
cdev_del(cdev);
// 3.销毁设备号
unregister_chrdev_region(MKDEV(major, minor), count);
// 4.释放动态申请的cdev内存
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include
int main(int argc,const char * argv[])
{
int fd;
char buf[128] = "i am fine..........\n";
if((fd = open("/dev/mycdev0",O_RDWR))==-1){
perror("open error");
exit(EXIT_FAILURE);
}
write(fd,buf,sizeof(buf));
memset(buf,0,sizeof(buf));
sleep(7);
read(fd,buf,sizeof(buf));
printf("user read buf = %s\n",buf);
close(fd);
return 0;
}
原子操作(掌握)
原子操作:将整个过程看做是一个不可被分割的整体。原子操作结构体内部本身就是一个变量,通过对这个变量的值的修改来实现防止竞态的效果,对这个变量的值的修改是通过内联汇编完成的。
原子操作的API接口
atomic_t atm = ATOMIC_INIT(1); //定义并初始化原子操作
int atomic_dec_and_test(atomic_t *v) //上锁
功能:让原子变量的值减去1,如果结果为0 表示获取锁成功了
参数:
@v:原子变量的地址
返回值:如果获取成功返回真,失败返回假
void atomic_inc(atomic_t *v) //将原子变量的值加1,解锁
atomic_t atm = ATOMIC_INIT(-1); //定义并初始化原子操作
int atomic_inc_and_test(atomic_t *v) //上锁
功能:让原子变量的值加1,如果结果为0 表示获取锁成功了
参数:
@v:原子变量的地址
返回值:如果获取成功返回真,失败返回假
void atomic_dec(atomic_t *v) //将原子变量的值减去1,解锁
使用原子变量实现防竞态的实例
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
atomic_t atm = ATOMIC_INIT(1);
int mycdev_open(struct inode *inode, struct file *file)
{
if(!atomic_dec_and_test(&atm)){
atomic_inc(&atm);
return -EBUSY;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf,
size_t size, loff_t *offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy data to user error\n");
return -EIO;
} // shift+alt+f 代码格式化
return size;
}
ssize_t mycdev_write(struct file *file,
const char __user *ubuf, size_t size, loff_t *off)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy data from user error\n");
return -EIO;
}
return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
atomic_inc(&atm);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int i, ret;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象的初始化
cdev_init(cdev, &fops);
// 3.申请设备号
if (major == 0)
{
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret)
{
printk("dynamic:alloc device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
else if (major > 0)
{
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret)
{
printk("static:alloc device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret)
{
printk("cdev register error\n");
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls))
{
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = 0; i < count; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
// 1.销毁设备节点
for (i = 0; i < count; i++)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
// 2.销毁字符设备驱动
cdev_del(cdev);
// 3.销毁设备号
unregister_chrdev_region(MKDEV(major, minor), count);
// 4.释放动态申请的cdev内存
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include
int main(int argc,const char * argv[])
{
int fd;
char buf[128] = "i am fine..........\n";
if((fd = open("/dev/mycdev0",O_RDWR))==-1){
perror("open error");
exit(EXIT_FAILURE);
}
write(fd,buf,sizeof(buf));
memset(buf,0,sizeof(buf));
sleep(7);
read(fd,buf,sizeof(buf));
printf("user read buf = %s\n",buf);
close(fd);
return 0;
}