查看: 393|回复: 0

[原创] 芯灵思SinlinxA64开发板 Linux内核信号量学习

[复制链接]
  • TA的每日心情
    奋斗
    2016-4-14 10:16
  • 签到天数: 9 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2019-3-15 16:13:56 | 显示全部楼层 |阅读模式
    分享到:
    在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态",因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。自旋锁与信号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本质和实现机理上完全不一样,不属于一类。
    自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"。而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的"不类"。但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。这就是它们的"类似"。鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,会只能在进程上下文使用。如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。
    一、信号量
          信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:
       (1) 测试控制该资源的信号量。
       (2) 若此信号量的值为正,则允许进行使用该资源。进程将信号量减1。
       (3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。
       (4) 当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。
        维护信号量状态的是Linux内核操作系统而不是用户进程。我们可以从头文件/usr/src/linux/include/linux/sem.h 中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合,用户可以单独使用这一集合的每个元素。要调用的第一个函数是semget,用以获得一个信号量ID。Linux2.6.26下定义的信号量结构体:

    semaphore.h        linux-3.5\include\Linux
    struct semaphore {
            raw_spinlock_t                lock;
            unsigned int                count;     //信号值,值为正数表示可以资源,0表示不能获得资源
            struct list_head        wait_list;
    };
    初始化信号量
    void sema_init (struct semaphore *sem, int val);
    该函数初始化信号量,并设置信号量sem的值为val


    void init_MUTEX (struct semaphore *sem);
    该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1,等同于sema_init (struct semaphore *sem, 1);


    void init_MUTEX_LOCKED (struct semaphore *sem);
    该函数也用于初始化一个互斥锁,但它把信号量sem的值设置为0,等同于sema_init (struct semaphore *sem, 0);
    获得信号量
    void down(struct semaphore * sem);
    该函数用于获得信号量sem,它会导致睡眠(这个睡眠和下面所说的不知道有什么不同,既然不能被其它地方唤醒,那么这个down有什么用呢?),因此不能在中断上下文使用;
    int down_interruptible(struct semaphore * sem);
    该函数功能与down类似,不同之处为,down不能被信号打断,但down_interruptible能被信号打断;(这个能被信号打断,有点疑惑,我现在做的项目是使用的是被中断打断,不知道它这个地方所说的是什么意思)
    int down_trylock(struct semaphore * sem);
    该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。它不会导致调用者睡眠,可以在中断上下文使用。

    释放信号量
    void up(struct semaphore * sem);
    该函数释放信号量sem,唤醒等待者。



    2.互斥锁
    2.1概念
    互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。mutex实际上是count=1情况下的semaphore。
    // 结构
    struct mutex {
            /* 1: unlocked, 0: locked, negative: locked, possible waiters */
            atomic_t                  count;
            spinlock_t                wait_lock;
            struct list_head          wait_list;
    #ifdef CONFIG_DEBUG_MUTEXES
            struct thread_info        *owner;
            const char                *name;
            void                      *magic;
    #endif
    #ifdef CONFIG_DEBUG_LOCK_ALLOC
            struct lockdep_map         dep_map;
    #endif
    };

    // 定义互斥锁lock
    mutex_init(struct mutex* lock)
      或者直接用 #define DEFINE_MUTEX(LOCK)即可;

    // 获取
    mutex_lock(struct mutex *lock)

    // 释放
    mutex_unlock(struct mutex *lock)
    struct mutex lock;

    mutex_init(&lock);初始化互斥锁
    或者直接用 #define DEFINE_MUTEX(LOCK)即可;
    #define __MUTEX_INITIALIZER(lockname) \
            { .count = ATOMIC_INIT(1) \
            , .wait_lock = __SPIN_LOCK_UNLOCKED(lockname.wait_lock) \
            , .wait_list = LIST_HEAD_INIT(lockname.wait_list) \
            __DEBUG_MUTEX_INITIALIZER(lockname) \
            __DEP_MAP_MUTEX_INITIALIZER(lockname) }

    #define DEFINE_MUTEX(mutexname) \
        struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)

    extern void __mutex_init(struct mutex *lock, const char *name,
                 struct lock_class_key *key);
    三:与自旋锁相关的API主要有:

    定义自旋锁
    spinlock_t spin;
    初始化自旋锁
    spin_lock_init(lock)
    该宏用于动态初始化自旋锁lock
    获得自旋锁
    spin_lock(lock)
    该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放;
    spin_trylock(lock)
    该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假,实际上不再"在原地打转";
    释放自旋锁
    spin_unlock(lock)
    该宏释放自旋锁lock,它与spin_trylock或spin_lock配对使用;
    除此之外,还有一组自旋锁使用于中断情况下的API。

    参考博客:https://www.cnblogs.com/biaohc/p/6679195.html
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|电路城

    GMT+8, 2021-4-21 23:08 , Processed in 0.056350 second(s), 9 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.