Linux内核中的并发与竞态
1.什么是竞态
当多个进程同时访问同一个驱动的临界资源的时间,竞态就产生了。
临界资源:一次仅允许一个进程使用的共享资源。各进程采用互斥的方式,实现共享的资源
临界区:每个进程中访问临界资源的那段代码称为临界区,每次只允许一个进程进入,进入之后不允许其他进程进入。使用临界区时,一般不允许其代码运行时间过长,只要运行在临界区的线程还没离开,其它所有进入此临界区的线程都会被挂起而进入等待状态,在一定程度上影响程序的运行性能。
2.产生竞态的根本原因
-
对于单核CPU来说,如果支持内核抢占,就会产生竞态。
-
对于多核CPU来说,核与核之间本身就会产生竞态
-
中断和进程之间也会产生竞态。
判断题:在ARM架构下,中断和中断也会产生竞态
答:false ARM架构的CPU不支持中断的嵌套
3.内核中解决竞态的方法
-
中断屏蔽
-
自旋锁
-
信号量
-
互斥体
-
原子操作
3.1中断屏蔽
中断屏蔽是将中断临时关闭,当关闭中断后抢占,中断都不会产生了。
但是中断屏蔽的时间要尽可能的短,长时间的屏蔽很可能会导致用户数据的丢失或者内核的奔溃。
中断屏蔽只支持单核CPU。
3.1.1中断屏蔽的API
local_irq_disable();//关闭中断
//临界资源
local_irq_enable();//开启中断
3.2自旋锁
自旋锁是针对多核设计的。当一个进程获取到自旋锁之后,另一个进程也想获取这把锁,此时后一个进程就处于自旋状态,也叫做盲等锁。
- 自旋锁是需要消耗CPU资源的
- 自旋锁保护的临界区要尽可能小,在临界区中只能做简短的,不耗时的,也不能有休眠的操作。
- 在自旋锁保护的临界区也不能调用copy_to_user/copy_from_user等函数,可能会被打断导致用户数据的丢失。
- 自旋锁可能会导致死锁(在同一进程内多次获取同一把未解锁的锁)
- 自旋锁可以工作在中断上下文
- 自旋锁在上锁的时间会关闭抢占
3.2.1 自旋锁的API
1.定义自旋锁
spinlock_t lock;
2.初始化自旋锁
void spin_lock_init(spinlock_t *lock)
3.上锁
void spin_lock(spinlock_t *lock)//上锁的时间中断可以打断锁
void spin_lock_irq(spinklock_t *lock)//上锁的时间中断打断不了锁
4.解锁
void spin_unlock((spinlock_t *lock))
void spin_unlock_irq((spinlock_t *lock))
3.2.2自旋锁代码实现
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define CNAME "myled"
struct cdev* cdev;
struct class* cls;
struct device* dev;
unsigned int major = 0;
unsigned int minor = 0;
char kbuf[128] = { 0 };
const int count = 3;
//1.定义自旋锁
spinlock_t lock;
int flags = 0;
int mycdev_open(struct inode* inode, struct file* file)
{
// 3.上锁
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;
}
return size;
}
ssize_t mycdev_write(struct file* file,
const 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_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)
{
spin_lock(&lock);
flags = 0;
spin_unlock(&lock);
printk("%s %s %d\n", __FILE__, __func__, __LINE__);
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 ret, i;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (NULL == cdev) {
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.初始化对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major > 0) {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret) {
printk("static request device number error\n");
goto ERR2;
}
} else {
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret) {
printk("dynamic request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
// 4.注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret) {
printk("register char device driver 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 = minor; 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;
}
}
//2.初始化自旋锁
spin_lock_init(&lock);
return 0;
ERR5:
for (--i; i >= minor; 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 = 0;
for (i = minor; i < count; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
unregister_chrdev_region(MKDEV(major, minor), count);
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#include <head.h>
char buf[128] = { 0 };
int main(int argc, const char* argv[])
{
int fd;
if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
PRINT_ERR("open error");
write(fd, buf, sizeof(buf));
sleep(7);
read(fd, buf, sizeof(buf));
close(fd);
return 0;
}
3.3信号量
当一个进程获取到信号量的时间,另外一个进程也想获取这个信号量,此时后一个进程处于休眠状态。
- 当信号量获取不到资源的时间,不消耗CPU资源
- 信号量不会产生死锁
- 信号量在上锁的时间不会关闭抢占
- 信号量保护的临界区比较大,可以有延时,耗时甚至休眠的操作
- 信号量可以工作在进程上下文
3.3.1信号量的API
1.定义信号量
struct sempore sem;
2.初始化信号量
void sema_init(struct semphore *sem ,int val)
//当val的值设置为1的时间才有互斥的效果,val为0时间是同步的效果
3.上锁
void down(struct semphore *sem);
void down_trylock(struct semphore *sem);//尝试获取锁,成功返回0,失败返回1
4.解锁
void up(struct semphore *sem);
3.3.2信号量代码实现
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define CNAME "myled"
struct cdev* cdev;
struct class* cls;
struct device* dev;
unsigned int major = 0;
unsigned int minor = 0;
char kbuf[128] = { 0 };
const int count = 3;
// 1.定义信号量
struct semaphore sem;
int flags = 0;
int mycdev_open(struct inode* inode, struct file* file)
{
// 3.上锁
if (down_trylock(&sem)) {
printk("get lock error\n");
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;
}
return size;
}
ssize_t mycdev_write(struct file* file,
const 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_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__);
// 4.解锁
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 ret, i;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (NULL == cdev) {
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.初始化对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major > 0) {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret) {
printk("static request device number error\n");
goto ERR2;
}
} else {
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret) {
printk("dynamic request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
// 4.注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret) {
printk("register char device driver 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 = minor; 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;
}
}
// 2.初始化信号量
sema_init(&sem, 1);
return 0;
ERR5:
for (--i; i >= minor; 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 = 0;
for (i = minor; i < count; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
unregister_chrdev_region(MKDEV(major, minor), count);
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#include <head.h>
char buf[128] = { 0 };
int main(int argc, const char* argv[])
{
int fd;
if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
PRINT_ERR("open error");
write(fd, buf, sizeof(buf));
sleep(7);
read(fd, buf, sizeof(buf));
close(fd);
return 0;
}
3.4 互斥体
当一个进程获取到互斥体的时间,另外一个进程也想获取这个互斥体,后一个进程处于休眠状态
- 当互斥体获取不到资源的时间,不消耗CPU资源
- 互斥体不会产生死锁
- 互斥体在上锁的时间不会关闭抢占
- 互斥体的临界资源比较大,可以有延时,耗时,甚至休眠的操作
- 互斥体可以工作在进程上下文
- 互斥体在上锁前会稍微等一会,在不确定临界区大小的时间预先选择互斥体
3.4.1互斥体的API
1.定义互斥体
struct mutex mutex;
2.初始化互斥体
mutex_init(&mutex);
3.上锁
void mutex_lock(struct mutex *lock);
int mutex_trylock(struct mutex*lock);//尝试获取锁,成功返回1,失败返回0
4.解锁
void mutex_unlock(struct mutex*lock)
3.4.2互斥体代码实现
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define CNAME "myled"
struct cdev* cdev;
struct class* cls;
struct device* dev;
unsigned int major = 0;
unsigned int minor = 0;
char kbuf[128] = { 0 };
const int count = 3;
// 1.定义互斥体
struct mutex mutex;
int mycdev_open(struct inode* inode, struct file* file)
{
// 3.尝试上锁,成功返回1,失败返回0
if (mutex_trylock(&mutex)) {
printk("get lock error\n");
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;
}
return size;
}
ssize_t mycdev_write(struct file* file,
const 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_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__);
// 4.解锁
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 ret, i;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (NULL == cdev) {
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.初始化对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major > 0) {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret) {
printk("static request device number error\n");
goto ERR2;
}
} else {
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret) {
printk("dynamic request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
// 4.注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret) {
printk("register char device driver 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 = minor; 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;
}
}
// 2.初始化互斥体
mutex_init(&mutex);
return 0;
ERR5:
for (--i; i >= minor; 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 = 0;
for (i = minor; i < count; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
unregister_chrdev_region(MKDEV(major, minor), count);
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#include <head.h>
char buf[128] = "12345";
int main(int argc, const char* argv[])
{
int fd;
if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
PRINT_ERR("open error");
write(fd, buf, sizeof(buf));
sleep(7);
read(fd, buf, sizeof(buf));
printf("buf=%s\n", buf);
close(fd);
return 0;
}
3.5原子操作
本身强调的原子性,对变量的修改是一个不可分割的整体。内部实现时间保证了只有一个CPU在执行,对这个变量的修改是通过内联汇编完成的
typedef struct {
int counter;
} atomic_t;
3.5.1 原子操作的API
1.定义原子变量并初始化
atomic_t atm = ATOMIC_INIT(1);
atomic_t atm = ATOMIC_INIT(-1);
2.上锁
int atomic_dec_and_test(atomic_t *v)
//让原子变量的值减去1,然后和0比较,如果结果为0代表获取锁成功了,这个函数返回真,否者返回假
int atomic_inc_and_test(atomic_t *v);
//让原子变量的值加1,然后和0比较,如果结果为0代表获取锁成功了,这个函数返回真,否者返回假
3.解锁
atomic_inc(atomic_t *v) //加1 必须保证原子变量为初始化时的值
atomic_dec(atomic_t *v) //减1 必须保证原子变量为初始化时的值
3.5.2原子操作代码实现
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define CNAME "myled"
struct cdev* cdev;
struct class* cls;
struct device* dev;
unsigned int major = 0;
unsigned int minor = 0;
char kbuf[128] = { 0 };
const int count = 3;
// 1.定义原子变量并初始化
atomic_t atomic = ATOMIC_INIT(1);
int mycdev_open(struct inode* inode, struct file* file)
{
// 让原子变量减去1,和0比较,结果为0就代表获取锁成功了,函数返回真
if (!atomic_dec_and_test(&atomic)) {
// 进来代表获取锁失败 此时原子变量减1为0 必须恢复为初始化的值,也就是执行解锁操作
atomic_inc(&atomic);
printk("get lock error\n");
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;
}
return size;
}
ssize_t mycdev_write(struct file* file,
const 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_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__);
//此时原子变量为0 必须恢复为初始化的值 也就是解锁的操作
atomic_inc(&atomic);
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 ret, i;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (NULL == cdev) {
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.初始化对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major > 0) {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
if (ret) {
printk("static request device number error\n");
goto ERR2;
}
} else {
//动态申请
ret = alloc_chrdev_region(&devno, minor, count, CNAME);
if (ret) {
printk("dynamic request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
// 4.注册
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret) {
printk("register char device driver 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 = minor; 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;
ERR5:
for (--i; i >= minor; 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 = 0;
for (i = minor; i < count; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
unregister_chrdev_region(MKDEV(major, minor), count);
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#include <head.h>
char buf[128] = "12345";
int main(int argc, const char* argv[])
{
int fd;
if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)
PRINT_ERR("open error");
write(fd, buf, sizeof(buf));
sleep(7);
read(fd, buf, sizeof(buf));
printf("buf=%s\n", buf);
close(fd);
return 0;
}