精华内容
下载资源
问答
  • 自旋锁替代互斥锁使用场景

    千次阅读 2018-11-22 16:47:45
    自旋锁替代互斥锁使用场景

    自旋锁与互斥锁

    自旋锁和互斥锁是多线程程序中的重要概念。 它们被用来锁住一些共享资源, 以防止并发访问这些共享数据时可能导致的数据不一致问题。 但是它们的不同之处在哪里? 我们应该在什么时候用自旋锁代替互斥锁?

    理论分析

    从理论上说, 如果一个线程尝试加锁一个互斥锁的时候没有成功, 因为互斥锁已经被锁住了, 这个未获取锁的线程会休眠以使得其它线程可以马上运行。 这个线程会一直休眠, 直到持有锁的线程释放了互斥锁, 休眠的线程才会被唤醒。 如果一个线程尝试获得一个自旋锁的时候没有成功, 该线程会一直尝试加锁直到成功获取锁。 因此它不允许其它线程运行(当然, 操作系统会在该线程所在的时间片用完时, 强制切换到其它线程)。

    互斥锁的问题

    互斥锁存在的问题是, 线程的休眠和唤醒都是相当昂贵的操作, 它们需要大量的CPU指令, 因此需要花费一些时间。 如果互斥量仅仅被锁住很短的一段时间, 用来使线程休眠和唤醒线程的时间会比该线程睡眠的时间还长, 甚至有可能比不断在自旋锁上轮训的时间还长。自旋锁的问题是, 如果自旋锁被持有的时间过长, 其它尝试获取自旋锁的线程会一直轮训自旋锁的状态, 这将非常浪费CPU的执行时间, 这时候该线程睡眠会是一个更好的选择。

    自旋锁应用场景

    在单核/单CPU系统上使用自旋锁是没用的, 因为当线程尝试获取自旋锁不成功的时候会一直尝试, 这会一直占用CPU, 其它线程不可能运行, 因为其他线程不能运行, 这个锁也就不会被解锁。 换句话说, 在单核/单CPU的系统上,自旋锁除了浪费时间没有一点好处。 这时如果这个线程(记为A)可以休眠, 其它线程可以立即运行, 因为其它有可能解锁, 那么线程A可能在唤醒后继续执行。
    在多核/多CPU的系统上, 特别是大量的线程只会短时间的持有锁的时候, 在使线程睡眠和唤醒线程上浪费大量的时间, 也许会显著降低程序的运行性能。 使用自旋锁, 线程可以充分利用调度程序分配的时间片(经常阻塞很短的时间, 不用休眠, 然后马上继续它们的工作了), 以达到更高的处理能力和吞吐量。

    自旋锁实践

    因为程序员往往并不能事先知道哪种方案会更好(比如, 不知道运行环境的CPU核的数量), 操作系统也不知道一段指令是不是针对单核或者多核环境下做过优化, 所以大部分操作系统并不严格区分互斥锁和自旋锁。 实际上, 绝大部分现代的操作系统采用的是混合型互斥锁(hybrid mutexes)和混合型自旋锁(hybrid spinlocks)。 它们是什么意思呢?
    混合型互斥锁, 在多核系统上起初表现的像自旋锁一样, 如果一个线程不能获取互斥锁, 它不会马上被切换为休眠状态, 因为互斥量可能很快就被解锁, 所以这种机制会表现的像自旋锁一样。 只有在一段时间以后(或者尝试一定次数,或者其他指标)还不能获取锁, 它就会被切换为休眠状态。 如果运行在单核/单CPU上, 这种机制将不会自旋(就像上面解释的, 这种情况自旋没有什么好处)。
    混合型自旋锁, 起初表现的和正常自旋锁一样, 但是为了避免浪费大量的CPU时间, 会有一个折中的策略。 这种机制不会把线程切换到休眠态(既然想要使用自旋锁, 那么你并不希望这种情况发生), 也许会决定放弃这个线程的执行(马上放弃或者等一段时间)并允许其他线程运行, 这样提高了自旋锁被解锁的可能性(大多数情况, 线程之间的切换操作比使线程休眠而后唤醒它要昂贵, 尽管那不是很明显)。

    总结

    如果对选择哪种方案感到疑惑, 那就使用互斥锁吧, 并且大多数现代的操作系统都允许在获取锁的时候自旋一段时间(混合型互斥锁)。 只有在一定条件下使用自旋锁才可以提高性能, 事实上, 你现在在做的项目可能没有一个能在通过自旋锁提高性能。 也许你考虑使用你自己定义的”锁对象”, 它可以在内部使用互斥锁或者自旋锁(例如: 在创建锁对象时, 用哪种机制是可配置的), 刚开始在所有的地方都是用互斥锁, 如果你认为在有些地方用自旋锁确实可以提高性能, 你可以试试, 并且比较两种情况的结果(使用一些性能评测工具), 但一定要在单核和多核环境上测试之后再下结论(如果你的代码是夸平台的, 也要尽可能在不同的平台上测试下)。

    展开全文
  • 互斥锁使用不当导致线程阻塞

    千次阅读 2019-03-05 17:00:07
    互斥锁使用不当导致线程阻塞@TOC 写作目的 多线程之间通过消息队列进行进程间通信,在线程内部互斥锁使用不当,导致线程阻塞。花费了比较多的时间去定位,故整理下自己所犯的错误,也为后来阅读者起一个提示作用,...

    写作目的

    多线程之间通过消息队列进行进程间通信,在线程内部互斥锁使用不当,导致线程阻塞。花费了比较多的时间去定位,故整理下自己所犯的错误,也为后来阅读者起一个提示作用,加强自己对锁的认识与理解。

    错误示例

    本文讲述的互斥锁,关于互斥锁的创建、加锁、解锁和互斥锁的删除这里就不做详细的介绍。

    该错误示例:A线程和B线程通过消息队列实现线程间通信,A线程接收到B线程的数据后,会加锁进行处理,处理完成后,再解锁。本来这个流程是没有问题。后来新增需求,直接移植了一个函数C,该函数内部也进行了加锁处理,该锁和A线程的锁相同,所以B线程发送新的消息给A的时候,B加锁后,调用C进行处理的时候,C也进行了加锁,但是因为锁已经lock了,导致B线程阻塞了,从而导致整个进程的异常,如socket链接断开、程序无响应等。

    问题引入过程

    起因:一个新的需求,要求导出日志文件。
    因为B线程负责的就是日志模块,对于这个新的需求,第一反应就是在该模块实现该功能函数C,对于该功能的实现,涉及操作全局变量,故进行了加锁保护。自测试导出功能没有问题。

    1. 因为A线程负责接收UI发送的消息,收到导出日志的消息,A线程直接调用C函数,自测试OK。于是提交了代码。
      代码审批意见:不同线程之间不能直接调用函数,增加了模块之间的耦合性,不利于模块的维护,建议使用回调处理。

    2. 于是改用回调函数的方式实现,自测是OK,于是又提交了代码。这个时间很快。
      代码审批意见:因为A线程负责接收UI发送的消息,需要快速处理,而导出日志这个函数的时间不能确定的,和日志的大小有关系,可能会导致线程阻塞一会,影响A线程对其他UI消息的处理,建议将C函数放到B线程中进行处理。

    3. 于是想到A线程发送一个消息给B线程,B线程收到A线程发送的消息后,就调用C函数实现日志导出功能。于是就开始coding代码,coding完成后,就进行自测试,发现功能不正常,而且整个进程都不正常,socket链接中断……于是就开始梳理整个流程、增加日志定位,最后才发现是C函数加锁一直等不到资源,阻塞住了

    解决办法

    解决方法去掉函数C中的加锁和解锁,统一在B线程的任务函数中进行加锁处理,B线程任务函数所有调用的函数不得进行加锁与解锁操作,由B线程任务函数统一控制。

    总结

    就是因为互斥锁使用的不当,重复加锁,导致线程阻塞,足足花费了4个小时的时间去定位问题,对自己来说,算是自己给自己挖了一个坑。以后对用到互斥锁的地方一定要看上下文环境,避免此类问题的出现。

    展开全文
  • 多线程编程:互斥锁使用。 打包文件包含两个文件:c文件源代码、Makefile文件,运行环境在Ubuntu14.04下,使用自带的gcc编译器,同学们只需将文件夹复制到某一目录下之后在终端执行:1.“make”生成“test”可执行...
  • 自旋锁互斥锁使用注意区别

    千次阅读 2018-07-12 14:17:12
    2.由于一直查询,所以自旋锁一直占用cpu,互斥锁不会,自旋锁导致cpu使用效率低3.自旋锁容易造成死锁 互斥锁和自旋锁的区别https://blog.csdn.net/susidian/article/details/510688581、自旋锁一直占用CPU,他在未...



    自旋锁:
    互斥锁:

    1.自旋锁不会睡眠,互斥锁会有睡眠,因此自旋锁效率高于互斥锁。
    2.由于一直查询,所以自旋锁一直占用cpu,互斥锁不会,自旋锁导致cpu使用效率低
    3.自旋锁容易造成死锁

    互斥锁和自旋锁的区别

    https://blog.csdn.net/susidian/article/details/51068858

    1、自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时 间内获得锁,这无疑会使CPU效率降低。
        2、在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数也可能造成死锁,如 copy_to_user()、copy_from_user()、kmalloc()等。

    因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。


    自旋锁

    https://www.cnblogs.com/kuliuheng/p/4064680.html

    展开全文
  • linux内核信号量和互斥锁使用

    千次阅读 2019-09-01 11:29:08
    信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为 1 就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。 一个任务要想...

    信号量概念

    Linux 内核的信号量在概念和原理上与用户态的 System V 的 IPC 机制信号量是一样的,但是它绝不可能在内核之外使用,因此它与 System V 的 IPC 机制信号量毫不相干。
    信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为 1 就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。
    一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作将把信号量的值减 1,若当前信号量的值为负数,表明无法获得信号量,该任务必须挂起在该信号量的等待队列等待该信号量可用;若当前信号量的值为非负数,表示可以获得信号量,因而可以立刻访问被该信号量保护的共享资源。
    当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加 1 实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。

    内核信号量核心结构

    内核中使用 struct semaphore 结构来描述一个信号量,结构定义如下:

    struct semaphore {
    raw_spinlock_t lock;
    unsigned int count; //信号值,只要这个值为正数,则信号可用, 一般情况设置 0 或 1。
    struct list_head wait_list;
    };

    信号量相关 API

    DEFINE_SEMAPHORE (name)

    宏原型#define DEFINE_SEMAPHORE(name) \
    struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
    宏功能该宏声明一个信号量 name 并初始化它的值为 1,即声明一个互斥锁
    宏参数name: 要定义的信号量变量名
    所在头文件include\linux\semaphore.h
    宏定义文件include\linux\semaphore.h
    备注Linux3.5 内核出现 在 linux 2.6 中名字为 DECLARE_MUTEX(name)

    void sema_init(struct semaphore *sem, int val)

    宏(函数) 原型

    void sema_init(struct semaphore *sem, int val)

    宏功能该函数用于初始化一个互斥锁,即它把信号量 sem 的值设置为 1。
    宏参数sem: 要初始化的信号量的指针
    所在头文件include\linux\semaphore.h
    宏定义文件include\linux\semaphore.h
    备注 

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

    宏原型void sema_init (struct semaphore *sem, int val);
    宏功能初始化设置信号量的初值,它设置信号量 sem 的值为 val
    宏参数sem: 要初始化的信号量的指针;
    val: 信号量的值
    所在头文件include\linux\semaphore.h
    宏定义文件include\linux\semaphore.h

    定义、初始化信号量相关 API:这是一个静态定义方式。 定义一个名字为 name,信号量值为 1 的信号量结构变量。
    示例:
    struct semaphore sem;
    sema_init(&sem, 1);
    以上两行等效:
    DEFINE_SEMAPHORE(sem);
     

    void down(struct semaphore *sem)

    函数原型void down(struct semaphore * sem);
    函数功能用于获得信号量 sem,它会导致睡眠,因此不能在中断上下文(包括 IRQ 上下文和 softirq 上
    下文)使用该函数。该函数将把 sem 的值减 1,如果信号量 sem 的值非负,就直接返回,否
    则调用者将被挂起,直到别的任务释放该信号量才能继续运行。
    函数参数sem: 要初始化获取的信号量的指针;
    函数返回值
    所在头文件include\linux\semaphore.h
    函数定义文件kernel\semaphore.c

    int down_timeout(struct semaphore *sem, long jiffies);
    sem: 信号量结构指针
    jiffies: 要设置超时时间,单位是时钟节拍。
    阻塞地请求一个信号量,如果信号量大于 0, 则可以马上返回, 否则休眠,直到有其他进程释放信号量
    (把信号量的 count 修改为大于 0 的值) 或超时时间到。
    这个函数效果和 down 函数很像,它也是不可中断的休眠。
     

    int down_interruptible(struct semaphore * sem);

    函数原型int down_interruptible(struct semaphore * sem);
    函数功能该函数功能与 down 类似,不同之处为, down 不会被信号(signal)打断,但 down_interruptible
    能被信号打断,因此该函数有返回值来区分是正常返回还是被信号中断,如果返回 0,表示
    获得信号量正常返回,如果被信号打断,返回-EINTR。
    函数参数sem: 要获取的信号量的指针;
    函数返回值0:得到信号量正常返回 ; 负数:被信号中断返回
    所在头文件include\linux\semaphore.h
    函数定义文件kernel\semaphore.c

    int down_trylock(struct semaphore * sem);

    函数原型int down_trylock(struct semaphore * sem);
    函数功能该函数试着获得信号量 sem,如果能够立刻获得,它就获得该信号量并返回 0,否则,表示
    不能获得信号量 sem,返回值为非 0 值。因此,它不会导致调用者睡眠,可以在中断上下文
    使用。
    函数参数sem: 要获取的信号量的指针;
    函数返回值0:得到信号量正常返回 ; 非 0:得不到信号量
    所在头文件include\linux\semaphore.h
    函数定义文件kernel\semaphore.c

    void up(struct semaphore * sem);

    函数原型void up(struct semaphore * sem);
    函数功能该函数释放信号量 sem,即把 sem 的值加 1,如果 sem 的值为非正数,表明有任务等待该信
    号量,因此唤醒这些等待者。
    函数参数sem: 要初释放的信号量的指针;
    函数返回值
    头文件include\linux\semaphore.h
    函数定义文件kernel\semaphore.c

    虽然信号量可以设置为大于 1 的值,但是信号量在绝大部分情况下作为互斥锁使用。

     


    简单信号量使用例子实现设备只能被一个进程打开:

    #include <linux/module.h> /* Needed by all modules */
    #include <linux/init.h> /* Needed for the module-macros */
    #include <linux/fs.h>
    #include <linux/miscdevice.h>
    #include <asm/atomic.h>
    #define LEDS_MAJOR 255
    #define DEVICE_NAME "miscdev_semaphore"
    
    DECLARE_MUTEX(lock); //定义一个互斥信号量,并初始化 1;
    
    static int first_miscdev_open(struct inode *pinode, struct file *pfile)
    {
    printk (KERN_EMERG "Linux miscdevice:%s is call\r\n",__FUNCTION__);
    
    /* 获取信号量 */
    if (!down_trylock(&lock))
    return 0;
    else
    return -EBUSY;
    }
    
    int first_miscdev_release (struct inode *inode, struct file *file)
    {
    printk (KERN_EMERG "Linux miscdevice:%s is call\r\n",__FUNCTION__);
    
    up(&lock); //释放信号量
    return 0;
    }
    static struct file_operations dev_fops =
    {
    .owner = THIS_MODULE,
    .open = first_miscdev_open,
    .release= first_miscdev_release,
    };
    static struct miscdevice misc =
    {
    .minor = LEDS_MAJOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
    };
    /* 模块装载执行函数 */
    static int __init first_miscdev_init(void)
    {
    int ret;
    ret = misc_register(&misc); //注册混杂设备
    if(ret <0)
    printk (KERN_EMERG DEVICE_NAME"\t err\r\n");
    printk (KERN_EMERG DEVICE_NAME"\tinitialized\n");
    return ret;
    }
    
    /* 模块卸载执行函数 */
    static void __exit first_miscdev_exit(void)
    {
    misc_deregister(&misc);
    printk(KERN_EMERG "Goodbye,cruel world!, priority = 0\n");
    }
    module_init(first_miscdev_init);
    module_exit(first_miscdev_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("XYD");
    MODULE_DESCRIPTION("This the samlpe drv_test");

    互斥信号量

    互斥信号量概念:

    互斥锁主要用于实现内核中的互斥访问功能。内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见的。 在大部分场合中,对共享资源的访问,都是使用独占方式, 在这种情况下, 提供专门的互斥接口给编程者使用。 对它的访问必须遵循一些规则:同一时间只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行解锁。互斥锁不能进行递归锁定或解锁。一个互斥锁对象必须通过其 API 初始化,而不能使用 memset 或复制初始化。一个任务在持有互斥锁的时候是不能结束的。互斥锁所使用的内存区域是不能被释放的。使用中的互斥锁是不能被重新初始化的。并且互斥锁不能用于中断上下文。但是互斥锁比当前的内核信号量选项更快,并且更加紧凑,因此如果它们满足您的需求,那么它们将是您明智的选择。

    内核信号量核心结构

    内核中使用 struct mutex 结构来描述一个信号量,结构定义如下:
     

    struct mutex {
    /* 1: unlocked, 0: locked, negative: locked, possible waiters */
    atomic_t count;
    spinlock_t wait_lock;
    struct list_head wait_list;
    #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
    struct task_struct *owner;
    #endif
    #ifdef CONFIG_DEBUG_MUTEXES
    const char *name;
    void *magic;
    #endif
    #ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map dep_map;
    #endif
    };

    关键成员说明:
    atomic_t count:
    指示互斥锁的状态:
    1--没有上锁,可以获得
    0--被锁定,不能获得
    负数--被锁定,且可能在该锁上有等待进程
    初始化时为没有上锁状态。


    spinlock_t wait_lock:
    等待获取互斥锁中使用的自旋锁。在获取互斥锁的过程中,操作会在自旋锁的保护中进行。初始化为为
    锁定, 用户一般无需关心。

    struct list_head wait_list:
    等待互斥锁的进程队列, 用户无需关心。

    互斥信号量相关 API

    DEFINE_MUTEX(mutexname)

    宏原型#define DEFINE_MUTEX(mutexname) \
    struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
    宏功能该宏声明一个互斥信号量 name 并初始化它
    宏参数name: 要定义的互斥信号量变量名
    所在头文件include\linux\mutex.h
    备注linux3.5出现

    mutex_init(mutex)

    宏原型# define mutex_init(mutex) \
    do { \
    static struct lock_class_key __key; \
    __mutex_init((mutex), #mutex, &__key); \
    } while (0)
    宏功能该宏初始化一个已经定义的互斥信号量, metex 传递的是指针
    宏参数mutex: 初始化的的互斥信号量指针
    所在头文件include\linux\mutex.h
    宏定义文件include\linux\mutex.h

    int mutex_is_locked(struct mutex *lock)

    宏原型int mutex_is_locked(struct mutex *lock)
    宏功能该函数检测互斥锁是否已经被锁定,
    宏参数lock: 互斥锁变量是指针
    返回值1 : 已经锁定 0: 没有锁定
    所在头文件include\linux\mutex.h

    mutex_lock(lock) 或 void mutex_lock(struct mutex *lock);

    宏(函数) 原型mutex_lock(lock)

    void mutex_lock(struct mutex *lock);
    宏功能该宏(函数) 获取互斥锁是否, 如没有得到会休眠,直接得到为止
    宏参数lock: 互斥锁变量是指针
    返回值
    所在头文件include\linux\mutex.h
    宏定义文件include\linux\mutex.h
    备注根据内核配置不同,该功能有有宏版本和函数版本

    mutex_lock_interruptible(lock) 或 int mutex_lock_interruptible(struct mutex *lock);

    宏原型#define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)

    int __must_check mutex_lock_interruptible(struct mutex *lock);
    宏功能该宏(函数)获取互斥锁是否, 如没有得到会休眠,直接得到为止, 但是可以被信号中断
    宏参数lock: 互斥锁变量是指针
    返回值成功获得锁返回 0, 被信号中断返回-EINIR
    所在头文件include\linux\mutex.h
    宏定义文件include\linux\mutex.h
    备注根据内核配置不同,该功能有有宏版本和函数版本

    int mutex_trylock(struct mutex *lock);

    宏原型int mutex_trylock(struct mutex *lock);
    宏功能该函数是获取互斥锁, 如没有得到不会休眠,马上返回
    宏参数lock: 互斥锁变量是指针
    返回值
    所在头文件include\linux\mutex.h
    宏定义文件include\linux\mutex.c
    备注根据内核配置不同,该功能有有宏版本和函数版本

    void mutex_unlock(struct mutex *lock);

    宏原型void mutex_unlock(struct mutex *lock);
    宏功能该函数是释放互斥锁
    宏参数lock: 互斥锁变量是指针
    返回值
    所在头文件include\linux\mutex.h
    宏定义文件include\linux\mutex.c

    互斥锁应用例子:

    /* chardev.c */
    #include <linux/module.h>       /* Needed by all modules */
    #include <linux/init.h>              /* Needed for the module-macros */
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>       /* 自动创建设备文件 */
    #include <linux/miscdevice.h>
    #include <asm/io.h>                /*ioremap*/
    #include <asm/uaccess.h>       /*copy_from_user ,copy_to_user*/
    #include <linux/atomic.h>
    #include <linux/mutex.h>
    
    #include <linux/gpio.h>
    #include <mach/gpio.h>
    #include <plat/gpio-cfg.h>  /* s3c_gpio_cfgpin  S3C_GPIO_OUTPUT */
    static struct cdev *pcdev;
    static struct device *this_device = NULL;
    static struct class *leds_class = NULL;
    
    /* 是否开启 gpio_request 函数功能 */
    #define GPIO_REQUEST          1     //0 :关,非0 :开 
    
    static int led_gpios[] = {
      EXYNOS4X12_GPM4(0),
      EXYNOS4X12_GPM4(1),
      EXYNOS4X12_GPM4(2),
      EXYNOS4X12_GPM4(3),
    };
    
    #define LED_NUM     ARRAY_SIZE(led_gpios)
    #define   DEV_NAME   "myleds"
    unsigned int major  = 0;                    //主设备号
    unsigned int minor = 0;                   //次设备号
    
    unsigned int devnr = 0;                    //设备号
    char *chrdev_name = DEV_NAME;  //设备名
    module_param(major, int, S_IRUGO | S_IWUSR);
    module_param(minor, int, S_IRUGO | S_IWUSR);
    module_param(chrdev_name, charp, S_IRUGO | S_IWUSR);
    
    
    struct mutex lock;
    
    //打开设备时候执行的程序
    static int  chrdev_open(struct inode *pinode, struct file *pfile)
    {
       mutex_lock(&lock);
    
      printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
      return 0;
    }
    
    //关闭设备时执行的程序
    static int chrdev_release(struct inode *pinode, struct file *pfile)
    {
       mutex_unlock(&lock);
      printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
      return 0;
    }
    
    
    
    //读接口函数
    static ssize_t chrdev_read ( struct file *file,
                                 char __user *buf,
                                 size_t count,
                                 loff_t * f_pos )
    {
      char led_buffer[4] = {2, 2, 2, 2};
      int i = 0;
      //  printk ( KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__ );
    
      /* count ==0 ,则返回0,不是错误 */
      if ( !count ) {
        return 0;
      }
    
      /* 当前已经到文件末尾,不能再写,返回0,*/
      if ( count > LED_NUM ) {
        count =  LED_NUM;
      }
    
    
      /* 准备数据,0表示灭,1表示亮 */
      for (i = 0; i < LED_NUM; i++) {
        led_buffer[i] = !gpio_get_value(led_gpios[i]);
      }
    
      if ( copy_to_user ( buf, &led_buffer[0], count ) ) {
        return -EFAULT;
      }
    
      return count;
    }
    
    
    
    //写接口函数
    static ssize_t chrdev_write(struct file *pfile,
                                const char __user *user_buf,
                                size_t count,
                                loff_t *off)
    {
      int ret = 0;
      char buf[LED_NUM] = {0};
      int i = 0;
      // printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
      //应用程序传递count=0下来,并不是错误,应该返回0
      if(count == 0) {
        return 0;
      }
    
      //因为板子只有4个灯,所以防止用户程序恶意破坏系统。
      if(count > LED_NUM) {
        count = LED_NUM;
      }
    
      //把用户空间传递下来的数据复制到内核空间的buf数组中
      ret = copy_from_user(buf, user_buf, count); //返回,成功是0,其他是失败
      if(ret) { //如果复制失败返回-1;
        return -EFAULT;
      }
    
      for(i = 0; i < count; i++) {
        if(buf[i] == 1)
          //                  GPM4DAT &= ~(1 << (0 + i));/* 亮 */
    
        {
          gpio_set_value(led_gpios[i], 0);
        }
    
        else if(buf[i] == 0)
          //                  rGPM4DAT |=  (1 << (0 + i));/* 灭 */
        {
          gpio_set_value(led_gpios[i], 1);
        }
    
      }
    
      count  = 1;
    
    
      return count;
    }
    
    
    
    //文件操作方法:
    static const  struct file_operations chrdev_fops = {
      .read      = chrdev_read,
      .write     = chrdev_write,
      .release  = chrdev_release,
      .open     = chrdev_open,
    };
    
    
    
    static int __init chrdev_init(void)
    {
    
      int ret = 0;
      int i;
    
      /* IO口配置代码 */
      for (i = 0; i < LED_NUM; i++) {
    #if GPIO_REQUEST
        ret = gpio_request(led_gpios[i], "LED");
        if (ret) {
          printk("%s: request GPIO %d for LED failed, ret = %d\n",
                 chrdev_name, led_gpios[i], ret);
          goto gpio_request_err;
        }
    #endif
    
        //s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
        //gpio_set_value(led_gpios[i], 1);
        //gpio_direction_output等效于上面两条语句,
        gpio_direction_output(led_gpios[i], 1);
    
      }
    
      //分配cdev核心结构;
      pcdev = cdev_alloc();
      if (pcdev == NULL) {
        ret = -ENOMEM;
        printk(KERN_EMERG"cdev_alloc_err error\n");
        goto cdev_alloc_err;
      }
    
      //如果主设备号是0,则动态分配设备号
      if(!major) {
        ret  = alloc_chrdev_region(&devnr, minor, 1, chrdev_name);
        if(ret < 0) {
          printk(KERN_EMERG"alloc_chrdev_region error\n");
          goto devnr_requst_err;
        }
    
      } else {
    
        //合成设备号
        devnr =  MKDEV(major, minor);
    
        //静态注册设备号
        ret = register_chrdev_region(devnr, 1, chrdev_name);
        if(ret != 0) {
          printk(KERN_EMERG"register_chrdev_region error\n");
          goto devnr_requst_err;
        }
      }
    
      //cdev结构体初始化
      cdev_init(pcdev, &chrdev_fops);
    
      //注册字符设备
      ret = cdev_add(pcdev, devnr, 1);
      if ( ret < 0) {
        printk(KERN_EMERG"cdev_add error\n");
        goto cdev_add_err;
      }
    
      //输出字符设备主设备号和次设备号
      printk(KERN_EMERG "name:%s,major:%d,minor:%d\n",
             chrdev_name, MAJOR(devnr), MINOR(devnr));
    
      //创建一个类
      leds_class =   class_create(THIS_MODULE, "leds_class");
      if ( IS_ERR(leds_class) ) {
        ret = PTR_ERR(leds_class);
        printk(KERN_EMERG"class_create error\n");
        goto class_create_err;
      }
    
      //创建一个设备
      this_device = device_create(leds_class, NULL, devnr, NULL, "%s", chrdev_name);
      if ( IS_ERR(this_device) ) {
        ret = PTR_ERR(this_device);
        printk(KERN_EMERG"this_device error\n");
        goto device_create_err;
      }
    
      mutex_init(&lock);
      return 0;
    
    device_create_err:
      class_destroy(leds_class);
    class_create_err:
      unregister_chrdev(major, chrdev_name);
    cdev_add_err:
    devnr_requst_err:
      if (pcdev) {
        cdev_del(pcdev);
      }
      unregister_chrdev_region(devnr, 1);
    cdev_alloc_err:
    
    #if GPIO_REQUEST
    gpio_request_err:
      /* gpio 反向释放 */
      for ( --i ; i >= 0 ; i-- ) {
        gpio_free(led_gpios[i]);
      }
    #endif
    
      return ret;
    }
    
    static void  __exit chrdev_exit(void)
    {
      int i = 0;
      device_destroy(leds_class, devnr);
      class_destroy(leds_class);
    
      /* gpio 反向释放 */
      for (   ; i < 4 ; i++) {
        gpio_free(led_gpios[i]);
      }
    
      cdev_del(pcdev);
      unregister_chrdev_region(devnr, 1);
      printk(KERN_EMERG "Goodbye,chrdev\n");
    }
    module_init(chrdev_init);
    module_exit(chrdev_exit);
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("learn");                //optional
    MODULE_DESCRIPTION("STUDY_MODULE"); //optional


     

    展开全文
  • 首先要弄清楚,线程互斥锁的作用是要保护共享资源在同一时刻只能被同一线程操作,即保证某一线程在上锁到解锁这一过程中对共享资源的操作为原子的。现举例说明: 设有两个线程:线程A和线程B(线程A和B在程序运行...
  • lock = threading.Lock() x = 0 def fun1(): global lock, x for i in range(1000): lock.acquire() #加锁 x = x + 1 lock.release() #解锁 def fun2(): global lock, x ...#使用互斥锁 输出正确结果2000
  • 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由...基于上面的特点,互斥锁一般用于控制一段临界代码,当然信号量也可以做到。但是如果释放和获取不在一个函数中甚至不在一个线程中时就必须使用信号量了。
  • 互斥锁使用流程 定义互斥锁变量 pthread_mutex_t:互斥锁变量的类型 例:pthread_mutex_t mutex 初始化互斥锁变量 int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr); muter:...
  • mutex-server是一个npm模块,可用于构建互斥锁服务器。 当您需要在整个系统级别上控制关键部分时,例如使用网络通信的分布式处理系统,此mutex-server可能是一个很好的解决方案。 安装并打开一个mutex-server并让...
  • 互斥锁

    2010-06-16 15:08:00
    使用 mutex_init(3C) 可以初始化 mp 所指向的互斥锁。对于 POSIX 线程,请参见初始化互斥锁。 mutex_init(3C) 语法 #include #include int mutex_init(mutex_t *mp, int type, void *arg)); type ...
  • Windows互斥锁使用

    2019-08-27 11:03:14
    互斥锁std::_Mutex的使用 首先是来看一下没使用互斥锁的情况 // InterlockedIncrementUse.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "InterlockedIncrementUse.h" using namespace ...
  • 什么是互斥锁

    千次阅读 2021-03-29 22:44:05
    注意: 互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。 互斥锁的使用 threading模块中定义了Lock变量,这个变量本质上是一个...
  • 主要介绍了PHP程序中的文件锁、互斥锁、读写锁使用技巧解析,其中重点讲解了sync模块和pthreads模块中的使用实例,需要的朋友可以参考下
  • 互斥锁与读写锁

    千次阅读 2019-05-21 15:28:12
    1、互斥锁 锁是一种很形象的说法:就像一个房间只能住一个人,任何人进去之后就...互斥锁使用与一对一情况之下,任何情况下都是只有一条线程访问共享资源 2、例子 #include <pthread.h> #include <stdlib.h...
  • 使用互斥锁

    2010-03-05 07:43:00
    使用互斥锁表 4–3 列出了用来处理互斥锁的函数。表 4–3 互斥锁的例程 操作 相关函数说明 初始化互斥锁pthread_mutex_init 语法 使互斥锁保持一致pthread_mutex_consistent_np 语法 锁定互斥锁pthread_mu
  • 初始化互斥锁 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 上锁 int pthread_mutex_lock(pthread_mutex_t *mutex); 解锁 int pthread_mutex_unlock(pthread_mutex_...
  • 互斥锁程序解析

    2013-07-19 21:43:16
    互斥锁使用流程
  • 主要介绍了Java使用synchronized实现互斥锁功能,结合实例形式分析了Java使用synchronized互斥锁功能简单实现方法与操作技巧,需要的朋友可以参考下
  • 互斥锁例程

    2015-09-22 14:40:31
    互斥锁例程
  • 互斥锁使用

    千次阅读 2018-12-08 17:44:55
    多个线程临界资源争抢写入操作会造成逻辑混乱/数据丢失二义性,因此就引入线程安全概念。  线程使用本身就是线程间通信方便成本低而广为使用。... 互斥:互斥锁---保证数据的同一时间唯一访问  ...
  • MFC多线程互斥锁使用

    千次阅读 2019-10-04 23:14:12
    MFC多线程互斥锁使用 本例演示在MFC中使用多线程。第一部分实现多线程的开启、暂停、继续、注销(见上一篇文章MFC多线程的开启、暂停、继续和注销)。第二部分实现两个线程互斥锁使用。 演示系统为Win10,平台为...
  • 互斥锁演示

    2018-03-30 14:33:07
    演示了互斥锁的原因,演示了互斥锁的原因,演示了互斥锁的原因
  • qt互斥锁使用

    2019-12-01 18:15:54
    qt互斥锁使用 class CallbackStorage { public: int insertCallback(const QJSValue &callback) { QMutexLocker locker(&m_mtx); const int nextId = qMax(++m_counter, 0); ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 181,654
精华内容 72,661
关键字:

互斥锁的使用