博客
关于我
Linux并发与同步专题 (3) 信号量
阅读量:674 次
发布时间:2019-03-16

本文共 2429 字,大约阅读时间需要 8 分钟。

信号量(Semaphore)是操作系统中常用的同步机制,用于管理进程间的互斥和同步问题。本文将深入探讨信号量的实现原理、初始化方法以及常见的down()/up()操作函数。

信号量数据结构

信号量由struct semaphore结构体描述,主要成员包括:

  • spinlock_t lock:用于保护信号量中的count和wait_list成员,spinlock是一种EncodingException,允许在持锁状态下进行短时间内_Draw的操作。
  • unsigned int count:表示允许进入临界区的内核执行路径个数。
  • list_head wait_list:用于管理所有在该信号量上睡眠的进程,未成功获取锁的进程会在这个链表上等待。
  • 信号量还定义了一个与其相关的struct semaphore_waiter结构体,该结构体用于描述等待信号量的进程:

    • list_head list:表示该进程在信号量的等待队列中的位置。
    • task_struct task*:指向等待信号量的进程结构。
    • bool up:标记该进程是否需要被唤醒。

    信号量的初始化

    信号量的初始化分为两种:动态初始化和静态定义。两种方式均通过__SEMAPHORE_INITIALIZER()初始化相关成员变量。

    #define __SEMAPHORE_INITIALIZER(name, n) {    .lock        = __RAW_SPIN_LOCK_UNLOCKED((name).lock),    .count        = n,    .wait_list    = LIST_HEAD_INIT((name).wait_list),}#define DEFINE_SEMAPHORE(name)    struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)static inline void sema_init(struct semaphore *sem, int val) {    static struct lock_class_key __key;    *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);    lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);}

    down()/up()

    信号量的使用基础上,down()/up()函数对信号量的加锁和解锁操作起着关键作用。

    down()的实现主要包括以下步骤:

    void down(struct semaphore *sem) {    unsigned long flags;    raw_spin_lock_irqsave(&sem->lock, flags);    if (likely(sem->count > 0)) {        sem->count--;    } else {        __down(sem);    }    raw_spin_unlock_irqrestore(&sem->lock, flags);}

    当信号量.count大于0时,本地加锁并减少count值,表示当前进程已获得信号量。否则,调用__down()进入等待队列。

    __down()函数会根据不同的 errno类型调用__down_common(),参数state决定进程进入哪种睡眠状态:

    • TASK_UNINTERRUPTIBLE:在不可中断睡眠,直到信号量被唤醒。
    • TASK_INTERRUPTIBLE或TASK_KILLABLE:进入可中断或不可中断但可杀死的睡眠等待。

    down_interruptible()与down()类似,但进程进入可中断睡眠状态。

    down_killable()允许进程在不可中断状态下被唤醒。

    down_timeout()是在超时后唤醒的特定实现。

    down_trylock()尝试获取锁但不阻塞。

    int down_trylock(struct semaphore *sem) {    unsigned long flags;    int count;    raw_spin_lock_irqsave(&sem->lock, flags);    count = sem->count - 1;    if (likely(count >= 0)) {        sem->count = count;    }    raw_spin_unlock_irqrestore(&sem->lock, flags);    return (count < 0);}

    up()函数的实现相对简单:

    void up(struct semaphore *sem) {    unsigned long flags;    raw_spin_lock_irqsave(&sem->lock, flags);    if (list_empty(&sem->wait_list)) {        sem->count++;    } else {        __up(sem);    }    raw_spin_unlock_irqrestore(&sem->lock, flags);}

    信号量与spinlock的对比

    spinlock和信号量都是同步机制,但主要区别如下:

    • spinlock:不允许进程在加锁期间进入睡眠,采用忙等待策略。同一时刻只能被一个内核代码路径持有,最适用于快速完成的临界区保护。
    • 信号量:允许进程在加锁失败时进入睡眠状态。同一时刻可以由多个进程持有,最适用于加锁时间较长或需要等待的场景。

    转载地址:http://fpoqz.baihongyu.com/

    你可能感兴趣的文章
    Openlayers实战:输入WKT数据,输出GML、Polyline、GeoJSON格式数据
    查看>>
    Openlayers实战:非4326,3857的投影
    查看>>
    Openlayers高级交互(10/20):绘制矩形,截取对应部分的地图并保存
    查看>>
    Openlayers高级交互(11/20):显示带箭头的线段轨迹,箭头居中
    查看>>
    Openlayers高级交互(14/20):汽车移动轨迹动画(开始、暂停、结束)
    查看>>
    Openlayers高级交互(15/20):显示海量多边形,10ms加载完成
    查看>>
    Openlayers高级交互(16/20):两个多边形的交集、差集、并集处理
    查看>>
    Openlayers高级交互(17/20):通过坐标显示多边形,计算出最大幅宽
    查看>>
    Openlayers高级交互(18/20):根据feature,将图形适配到最可视化窗口
    查看>>
    Openlayers高级交互(19/20): 地图上点击某处,列表中显示对应位置
    查看>>
    Openlayers高级交互(2/20):清除所有图层的有效方法
    查看>>
    Openlayers高级交互(20/20):超级数据聚合,页面不再混乱
    查看>>
    Openlayers高级交互(3/20):动态添加 layer 到 layerGroup,并动态删除
    查看>>
    Openlayers高级交互(4/20):手绘多边形,导出KML文件,可以自定义name和style
    查看>>
    Openlayers高级交互(5/20):右键点击,获取该点下多个图层的feature信息
    查看>>
    Openlayers高级交互(6/20):绘制某点,判断它是否在一个电子围栏内
    查看>>
    Openlayers高级交互(7/20):点击某点弹出窗口,自动播放视频
    查看>>
    Openlayers高级交互(8/20):选取feature,平移feature
    查看>>
    Openlayers高级交互(9/20):编辑图形(放缩、平移、变形、旋转),停止编辑
    查看>>
    Openlayers:DMS-DD坐标形式互相转换
    查看>>