Linux内核中共享资源竞争的互斥机制

01情况说明

当共享的资源同时被多个需要这个资源的主体访问时(并发),会产生竞态。这里的主体可能有:中断、另一个进程、另一个cpu。需要用互斥的方式来解决竞态。通俗来说,情况是并发出现竞态(多个主题竞争资源),互斥机制是解决办法,最后是实现了同步(程序按程序员想要的顺序执行)。这篇的互斥机制的所有函数均使用Linux的系统调用(即Linux内核源代码中自带的函数),和多线程中的互斥机制相似,但后者调用的是phread库。

02互斥类型

1.中断屏蔽:仅仅只针对本地cpu

2.原子操作:atomic a++;正常的自增会翻译成3条汇编,如果在中途遇到中断就会出错。

3.自旋锁:spin_lock 自旋:死循环,占死cpu; 在加锁和解锁中间的代码:简单用时少,不允许调度。

4.信号量:sem 二值信号量

5.互斥锁:mutex

02_01使用方式

1.中断屏蔽:

local_irq_disable() //屏蔽中断

local_irq_enable() //开中断

2.原子操作

void atomic_set(atomic_t *v, int i); /*设置原子变量的值为i*/

atomic_t v = ATOMIC_INIT(0); /*定义原子变量v并初始化为0*/
获取原子变量的值
atomic_read(atomic_t *v);/*返回原子变量的值*/

原子变量加/减
void atomic_add(int il, atomic_t*v); /*原子变量增加i*/
void atomic sub( int i, atomic_t *v);/*原子变量减少i*/

3.自旋锁

spinlock_t lock; //定义自旋锁

spin_lock_init(&lock) //初始化自旋锁

获得自旋锁

spin_lock(&lock); //如果能够立刻获得锁,它就马上返回,否则它将自旋在那里(阻塞)

spin_trylock(&lock);//非阻塞,如果能立刻获得锁则返回真,否则返回假

释放自旋锁

spin_unlock(&lock);   

4.信号量

定义信号量

struct semaphore sem;

初始化信号量

void sema_init(struct semaphore *sem, int val);

信号量的获取

void down(struct semaphore * sem);

int down_interruptible( struct semaphore * sem);

int down_trylock(struct semaphore* sem);

信号量的释放

void up(struct semaphore * sem);

5.互斥锁

初始化
struct mutex my_mutex;

mutex_init(&my_mutex);

获取互斥体
void inline _sched mutex_lock(struct mutex *lock);

释放互斥体
void_sched mutex_unlock(struct mutex *lock);

使用最多的是自旋锁和互斥锁

03_实例代码

1. 有三个任务
2. 任务 1 :向设备写入 “abcdefg”
3. 任务 2 :向设备写入 “1234567”
4. 任务 3 :从设备读取写入的数据
5. 要求:只修改驱动,使用自旋锁,读取出来的数据,要么是数字,要么是字母,不允许
混杂。
6. 模拟出混杂的情况
7. 应用不修改,驱动修改后, 达到要求。
应用程序:创建两个线程和主线程执行三个任务。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

static int fd = -1;
static char wbuf0[16]="1234567890";
static char wbuf1[16]="abcdefghij";
void* fun0(void* arg)
{
    while(1)
    {
        write(fd,wbuf0,16);
    }
}

void* fun1(void* arg)
{
    while(1)
    {
        write(fd,wbuf1,16);
    }
}
int main()
{
    fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("fd is %d\n",fd);
        return -1;    
    }
    pthread_t t0,t1;
    pthread_create(&t0,NULL,fun0,NULL);
    pthread_create(&t1,NULL,fun1,NULL);
    pthread_detach(t0);
    pthread_detach(t1);

    while(1)
    {
        char rbuf[16]={0};
        read(fd,rbuf,16);
        if(0!=strcmp(rbuf,wbuf0) && 0!=strcmp(rbuf,wbuf1))
            printf("rbuf is %s\n",rbuf);
    }
    close(fd);
    return 0;
}
//-lpthread

驱动:在myread和mywrite的copy_to/from_user(本质时memcpy)前后加解自旋锁即可

#include <linux/init.h>
#include <linux/module.h>

#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/uaccess.h>
#define MA 300
#define MI 0

static dev_t no;
static unsigned count = 1;
static const char * name  = "mydev";
static struct cdev mydev;

static char kbuf[16]={0};
static int ret = -1;
static spinlock_t lock;

int myopen(struct inode *pi,struct file *pf)
{
    printk("myopen success\n");
    return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
     printk("myrelease success\n");
     return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    spin_lock(&lock);
    ret = copy_to_user(ubuf,kbuf,len);
    spin_unlock(&lock);
    if(ret!=0)
        return -1;
    return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    spin_lock(&lock);
    ret = copy_from_user(kbuf,ubuf,len);
    spin_unlock(&lock);
    if(ret!=0)
        return -1;
    return len;
}
static const struct file_operations fops={
    .open = myopen,
    .release = myrelease,
    .read = myread,
    .open = myopen,
};
//函数init exit
static int myinit(void)
{
    //向上: 内核相关
    //1.注册
    no = MKDEV(MA,MI);
    ret = register_chrdev_region(no,count,name);
    if(0!=ret)
        return ret;
    //2.初始化
    cdev_init(&mydev,&fops);
    //3.添加
    ret = cdev_add(&mydev,no,count);
    if(0!=ret)
    {
         unregister_chrdev_region(no,count);
         return ret;
    } 
    spin_lock_init(&lock);
    printk("init ok\n");
    return 0;
}
static void myexit(void)
{
     cdev_del(&mydev);
     unregister_chrdev_region(no,count);
     printk("exit ok\n");
     return;
}

module_init(myinit);
module_exit(myexit);

MODULE_LICENSE("GPL");

03_02实例2

1. 2 个任务
2. 需求:设备在使用的时候,其他任务不能使用该设备,直到该设备使用完成为止。
3. 场景: task0 打开设备成功, task1 打开设备返回失败, task0 关闭设备后, task1 再次打
开成功。
应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

static int fd = -1;
void* fun0(void* arg)
{
    int fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("open0 failed");
        return -1;    
    }
    printf("open0 ok\n");
    sleep(2);
    close(fd);
    printf("open0 close ok\n");
}

void* fun1(void* arg)
{
    sleep(1);
    int fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("open1 failed");
        return NULL;    
    }
    else
    {
        printf("open1 ok\n");
        close(fd);    
    }
    sleep(2);
    
    fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("open1 2 failed");
        return NULL;    
    }
    printf("open1 2 ok\n");
}
int main()
{
    fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("fd is %d\n",fd);
        return -1;    
    }
    pthread_t t0,t1;
    pthread_create(&t0,NULL,fun0,NULL);
    pthread_create(&t1,NULL,fun1,NULL);
    pthread_detach(t0);
    pthread_detach(t1);

    while(1);
    close(fd);
    return 0;
}

驱动:
//头文件
#include <linux/init.h>
#include <linux/module.h>

#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/mutex.h>
#include"cmd.h"
#include<asm/uaccess.h>
#define MA 300
#define MI 0

static dev_t no;
static unsigned count = 1;
static const char * name  = "mydev";
static struct cdev mydev;

static int ret = -1;
static struct mutex my_mutex;

int myopen(struct inode *pi,struct file *pf)
{
    if(1!=mutex_trylock(&my_mutex))
        return -1;
    //printk("myopen success\n");
    return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
     mutex_unlock(&my_mutex);
     //printk("myrelease success\n");
     return 0;
}
static const struct file_operations fops={
    .open = myopen,
    .release = myrelease,
};
//函数init exit
static int myinit(void)
{
    //向上: 内核相关
    //1.注册
    no = MKDEV(MA,MI);
    ret = register_chrdev_region(no,count,name);
    if(0!=ret)
        return ret;
    //2.初始化
    cdev_init(&mydev,&fops);
    //3.添加
    ret = cdev_add(&mydev,no,count);
    if(0!=ret)
    {
         unregister_chrdev_region(no,count);
         return ret;
    } 
    mutex_init(&my_mutex);
     printk("init ok\n");
     return 0;
}
static void myexit(void)
{
     cdev_del(&mydev);
     unregister_chrdev_region(no,count);
     printk("exit ok\n");
     return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");

这里由于应用程序有sleep,不能用自旋锁,因为自旋锁中不能有调度。

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值