fmql之字符驱动设备(3)-并发与竞争

学会使用设备树后,要学习linux驱动编写中容易出现的”并发与竞争“。

代码是在之前代码的基础上进行修改。

并发与竞争

(本部分来自于正点原子pdf)

什么是并发与竞争,为什么会出现并发与竞争:

要保护的内容是:

什么是原子操作:

Linux提供的API函数

atomic

整形操作

 位操作

自旋锁

信号量 互斥体

代码修改(atomic)

设备结构体

多了atomic_t lock;

设备操作函数

 模块入口/出口函数

__init        :

APP程序

在之前的APP.c中修改,添加了部分内容。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

/*
 * @description         : main主程序
 * @param - argc        : argv数组元素个数
 * @param - argv        : 具体参数
 * @return              : 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
    int fd, ret;
    int cnt = 0;
    unsigned char buf[1];

    if(3 != argc) {
        printf("Usage:\n"
               "\t./atomicAPP /dev/gpio-led 1          @ close LED\n"
               "\t./atomicAPP /dev/gpio-led 0          @ open LED\n"
               );
        return -1;
    }

    /* 打开设备 */
    fd = open(argv[1], O_RDWR);
    if(0 > fd) {
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }

    /* 将字符串转换为int型数据 */
    buf[0] = atoi(argv[2]);

    /* 向驱动写入数据 */
    ret = write(fd, buf, sizeof(buf));
    if(0 > ret){
        printf("LED Control Failed!\r\n");
        close(fd);
        return -1;
    }

    /* 模拟占用25s LED设备 */
    for(;;) {
        sleep(5);
        cnt++;
        printf("APP running times: %d\r\n", cnt);
        if(cnt >= 5)    break;
    }
    printf("APP running finished\r\n");

    /* 关闭设备 */
    close(fd);

    return 0;

}

代码修改(自旋锁)

代码在atomic.c的基础上修改

自旋锁(上锁、解锁) ,控制的是dev_stats变量。也就是在改变dev_stats变量的值前,要上锁(其他设备不能修改dev_stats值),修改值后,要进行解锁。7uuuuujuj

代码修改(信号量)

 在spinlock.c的基础上进行修改。

代码修改(互斥体)

运行

atomic

正点原子pdf:

实际运行:        failed

后来查看代码,发现led_open操作函数的if条件写错了。

因为atomic_dec_and_test函数,是用来上锁的:将lock值减1,然后判断是否为0,如果是,则返回1(上锁成功,设备占用成功),否则返回1。

所以原来的代码是:

if(atomic_dec_and_test(&gpioled.lock))
{ 
    // error
    return -EBUSY;
}

即,设备占用成功,atomic_dec_and_test返回值为1,满足if条件,进入if条件下的语句(但是是error的语句) ,认为设备busy,占用失败。【相互矛盾了不是】

修改代码:

修改代码后:        succeed

 自旋锁

信号量

互斥体

总结

1. Linux是一个多任务系统。当多个任务共同操作同一段内存或同一个设备(共享资源)时,容易出现并发与竞争的情况。所以要处理对共享资源的并发访问。

2. 并发与竞争出现的几个主要原因:多线程并发访问;抢占式并发访问;中断程序并发访问;SMP(多核)核间并发访问,etc。

3. 在编写驱动的时候,就要注意避免并发核防止竞争访问。不然,会给后期埋下隐患。

4. 什么是共享资源,哪些内容需要保护。如,全局变量,设备结构体,etc。要弄清楚需要保护的内容或数据。

5. 几种处理并发和竞争的方法:原子操作;自旋锁;信号量;互斥体,etc。

  •         1. 原子操作,是指不能进一步分割的操作。一般用于变量或者位操作。e.g 对某个变量进行赋值,将这个赋值的过程作为一个整体进行运行,即一个原子。

                (整形操作)需要用到:atomic_t结构体。初始化、自增、自减函数。

                (位操作)对内存直接操作。

  •         2. 自旋锁,保护结构体变量。        当一个线程访问某共享资源前,要获取相应的锁,此线程不释放锁,其他线程就不能获取。        锁只能被一个线程持有。        如果另一个线程想要获取锁,就处于循环-等待的状态,直至锁可用。

                缺点:等待自旋锁的线程会一直等待,会浪费处理器时间,降低系统性能。

                适用于:短时期的轻量级加锁。       

                注意事项:被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的API函数。        不能递归申请自旋锁。                取锁之前一定要禁止本地中断,否则可能导致死锁。

                需要:spinlock_t结构体。初始化、加锁、解锁函数。

  •         3. 信号量,用于控制对共享资源的访问。

                特点:可以使线程(等待的)进入休眠状态。        使用信号量会提高处理器的使用效率(不用像自旋锁那样一直在等待)。

                适用于:占用资源比较久的场合。

                缺点:信号量的开销比自旋锁大,因为信号量使线程进入休眠状态后会切换线程。

                注意事项:不能用于中断中。因为中断不能休眠。

                需要:struct semaphore。初始化、获取信号量、释放信号量的函数。

  •         4. 互斥体,即,一次只有一个线程可以访问共享资源,不能递归申请互斥体。

                        适用于:建议需要互斥访问的地方用互斥体。

                        特点:互斥体可以导致休眠。因此中断中不能使用。        和信号量一样,互斥体保护的临界区可以调用引起阻塞的API函数。                不能递归上锁和解锁。

6. 注意事项

        中断中只能使用自旋锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值