当前位置: 首页 > news >正文

宁波建设局网站首页seo研究中心qq群

宁波建设局网站首页,seo研究中心qq群,什么网站max做环境的全景图,开网店需要什么流程第三十八章 linux-并发解决方法二(信号量) 文章目录第三十八章 linux-并发解决方法二(信号量)信号量的定义DOWN操作UP操作相对于自旋锁,信号量的最大特点是允许调用它的线程进入睡眠状态这意味着试图获得某一信号的进程…

第三十八章 linux-并发解决方法二(信号量)


文章目录

  • 第三十八章 linux-并发解决方法二(信号量)
  • 信号量的定义
  • DOWN操作
  • UP操作


相对于自旋锁,信号量的最大特点是允许调用它的线程进入睡眠状态·这意味着试图获得某一信号的进程会导致对处理器拥有权的丧失,也即出现进程的切换。

信号量的定义

struct semaphore {
raw_spinlock_t        lock;//lock是个自旋锁变量,用于实现对信号量的另一个成员count的原子操作。
unsigned int        count;//无符号整型变量count用于表示通过该信号量允许进入临界区的执行路径的个数。
struct list_head    wait_list;//wait_list用于管理所有在该信号量上睡眠的进程,无法获得该信号量的进程将进入睡眠状态。
};

如果驱动程序中定义了一个struct semaphore型的信号量变量,需要注意的是不要直接对该变量的成员进行赋值,而应该使用sema_init函数来初始化该信号量。sema_init函数定义如下:

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);
}

初始化主要通过__SEMAPHORE_INITIALIZER宏完成:

#define DEFINE_SEMAPHORE(name)    \struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
#define __SEMAPHORE_INITIALIZER(name, n)                \
{                                    \.lock        = __RAW_SPIN_LOCK_UNLOCKED((name).lock),    \.count        = n,                        \.wait_list    = LIST_HEAD_INIT((name).wait_list),        \
}

所以 sema_init(struct semaphore *sem, int val)调用会把信号量sem的lock值设定为解锁状态,count值设定为函数的调用参数val,同时初始化wait_list链表头。

DOWN操作

信号量上的主要操作是DOWN和UP,在Linux内核中对信号量的DOWN操作有:

  • void down(struct semaphore *sem) 获取信号量,不建议使用此函数,因为是 UNINTERRUPTABLE 的睡眠。
  • int down_interruptible(struct semaphore *sem) 可被中断地获取信号量,如果睡眠被信号中断,返回错误-EINTR。
  • int down_killable (struct semaphore *sem) 可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR。
  • int down_trylock(struct semaphore *sem) 尝试原子地获取信号量,如果成功获取,返回0,不能获取,返回1。
  • int down_timeout(struct semaphore *sem, long jiffies) 在指定的时间jiffies内获取信号量,若超时未获取,返回错误-ETIME。

上面这些函数中,驱动程序使用最频繁的是down_interruptible函数,本节将重点讨论该函数,之后再对其他入操作的功能作一概述性的描述。

int down_interruptible(struct semaphore *sem)
{unsigned long flags;int result = 0;raw_spin_lock_irqsave(&sem->lock, flags);//保证对sem->count操作的原子性防止多个进程对sem->count同时操作//可能引起混乱if (likely(sem->count > 0))sem->count--;elseresult = __down_interruptible(sem);raw_spin_unlock_irqrestore(&sem->lock, flags);return result;
}

函数首先通过spin_lock_irqsave的调用来保证对sem->count操作的原子性防止多个进程对sem->count同时操作
可能引起混乱。如果代码成功进入临界区,则判断sem->count是否大于0:如果count大于0,表明当前进程可以获得信号量,就将count值减I,然后退出:如果count不大于0,表明当前进程无法获得该信号量,此时调用down_interruptible,由后者完成一个进程无法获得信号量时的操作,在内部调用__down_common(struct semaphore *sem,long state,longt timeout),调用时的参数state=TASK_INTERRUPTIBLE,timeout=LONG_MAX所以当一个进程无法获得信号量时,最终调用的函数为__down_common:
__down_interruptible->__down_common

static inline int __sched __down_common(struct semaphore *sem, long state,long timeout)
{struct task_struct *task = current;/*通过对一个struct semaphore_waiter变量waiter的使用,把当前进程放到信号量sem的成员变量wait所管理的队列中*/struct semaphore_waiter waiter;list_add_tail(&waiter.list, &sem->wait_list);waiter.task = task;waiter.up = false;for (;;) {if (signal_pending_state(state, task))//把当前进程的状态设置为TASK_INTERRUPTIBLEgoto interrupted;if (unlikely(timeout <= 0))goto timed_out;__set_task_state(task, state);raw_spin_unlock_irq(&sem->lock);timeout = schedule_timeout(timeout);//使当前进程进入睡眠状态,函数将停留在schedule_timeout调用上,直到再次被调度执行。raw_spin_lock_irq(&sem->lock);if (waiter.up)return 0;}timed_out:list_del(&waiter.list);return -ETIME;interrupted:list_del(&waiter.list);return -EINTR;
}

函数的功能是,首先通过对一个struct semaphore_waiter变量waiter的使用,把当前进程放到信号量sem的成员变量wait所管理的队列中,接着在一个for循环中把当前进程的状态设置为TASK_INTERRUPTIBLE,再调用schedule_timeout使当前进程进入睡眠状态,函数将停留在schedule_timeout调用上,直到再次被调度执行。当该进程再一次被调度执行时,schedule_timeout开始返回,接下来根据进程被再次调度的原因进行处理:如果waiter.up不为0,说明进程在信号量sem的wati_list队列中被该信号量的UP操作所唤醒,进程可以获得信号量,返回0。如果进程是因为被用户空间发送的信号所中断或者是超时引起的唤醒,则返回相应的错误代码。因此对面down_interruptible的调用总是应该坚持检查其返回值,以确定函数是已经获得了信号量还是因为操作被中断因而需要特别处理,通常驱动程序对返回的非0值要做的工作是返回-ERESTARTSYS,比如下面的代码段:

//定义一个信号量
struct semaphore demosem;
sema_init(&demosem,2);
if(down_interruptiable(&demosem));return -ERESTARTSYS;

然而对down_interruptible的调用最常见的可能还是返回0表明调用者获得了信号量。为了让讨论具体化,下面以一个例子来说明,假设一个信号量sem的count=2,说明允许有两个进程进入临界区,假设有进程A、B、C、D和E先后调用down_interruptible来获得信号量,那么进程A和B将得到信号量进入临界区,C、D和E将睡眠在sem的wait_list中,此时的情形如图2所示:
在这里插入图片描述
在接下来的UP操作中还会用到这里的例子,来讨论进程A和B结束临界区中的操作返回时执行UP操作对wait_list中进程C、D和E的影响。

在讨论完驱动程序最常使用的down_interruptible函数之后,再回过头来看看其他几种DOWN操作:

  • void down(struct semaphore *sem);
    与down_interruptible相比,down函数是不可中断的,这意味着调用它的进程如果无法获得信号量,将一直处于睡眠状态直到有别的进程释放了该信号量。从用户空间的角度,如果应用程序阻塞在了驱动程序的down函数中,将无法通过一些强制措施比如按Ctrl+D组合键等来结束该进程。因此,除非必要,否则驱动程序中应该避免使用down函数。
  • int down_killable(struct semaphore *sem);
    睡眠的进程可以因收到一些致命性信号(fatal signal)被唤醒而导致获取信号量的操作被中断,在驱动程序中极少使用。
  • int down_trylock(struct semaphore *sem);
    进程试图获得信号量,但若无法获得信号量则直接返回1而不进入睡眠状态,返回0意味着函数的调用者己经获得了信号量。
  • int down_timeout(struct semaphore *sem, long jiffies);
    函数在无法获得信号量的情况下将进入睡眠状态,但是处于这种睡眠状态有时间限制,如果在jiffies指明的时间到期时函数依然无法获得信号量,则将返回一错误码-ETIME,在到期前进程的睡眠状态为TASK_UNINTERRUPTIBLE。成功获得信号量的函数返回0。

UP操作

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

如果信号量的wait_list队列为空,则表明没有其他进程正在等待该信号量,那么只要把sem的count加1即可。如果wait_list队列不为空,则说明有其他进程正睡眠在wait_list上等待该信号量,此时调用__up(sem)来唤醒进程:

static noinline void __sched __up(struct semaphore *sem)
{struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,struct semaphore_waiter, list);list_del(&waiter->list);waiter->up = true;wake_up_process(waiter->task);
}

下面在图2的基础上讨论此处的操作。__up函数首先用list_first_entry取得sem->wait_list链表上的第一个waiter节点C,然后将其从sem->wait_list链表中删除,waiter->up=1,最后调用wake_up_process来唤醒waiter C上的进程C。这样进程C将从之前down_interruptible。

调用中的timeout=schedule_timeout(timeout)处醒来,waiter->up=1,down_interruptible返回0,进程c获得信号量,进程D和E继续等待直到有进程释放信号量或者被用户空间中断掉。即使不是信号量的拥有者,也可以调用up函数来释放一个信号量,这点与下节介绍的mutex是不同的。

在Linux系统中,信号量的一个常见的用途是实现互斥机制,这种情况下信号量的count值为1,也就是任意时刻只允许一个进程进入临界区。为此Linux内核源码提供了一个宏DECLARE_MUTEX,专门用于这种用途的信号量定义和初始化:

static noinline void __sched __up(struct semaphore *sem)
{struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,struct semaphore_waiter, list);list_del(&waiter->list);waiter->up = true;wake_up_process(waiter->task);
}

该宏定义了一个count=1的信号量变量name,并初始化了相关成员。所以接下来就可以使用信号量的DOWN和UP操作来实现互斥,比如下面的这个用DECLARE_MUTEX定义的信号量来实现互斥的代码段:

//先用DECLARE_MUTEX定义一个全局的信号量demo_sem
DECLARE_MUTEX(demo_sem);//函数demo_write里面使用demo_sem作互斥用
int demo_write{//打算进入临界区,调用down_interruptible获得信号量if(down_interruptible(&down_sem)){return -ERESTARTSYS            }//成功获取信号量进入临界区.....//离开临界区,调用up释放信号量up(&demo_sem)
}
http://www.shuangfujiaoyu.com/news/59136.html

相关文章:

  • 广东圆心科技网站开发需要多少钱目前搜索引擎排名
  • 政府部门网站建设百度推广联系方式
  • 国家重点学科建设网站视频号链接怎么获取
  • 白城做网站舆情报告范文
  • 犀牛云做网站骗人怎么制作网页里面的内容
  • 做网站视频图片加载不出来搜索引擎网站推广如何优化
  • 网站建设有什么出路seo搜索推广
  • 台州网站建设系统中国十大小说网站排名
  • 用什么软件做网站最快免费的外链网站
  • 秦皇岛做网站汉狮网络推广普通话宣传标语
  • 赌球网站推广怎么做产品网络营销推广方案
  • 哪里有做网站公司的无锡百度快速优化排名
  • 做网站时如何上传图片最好用的系统优化软件
  • 爱站网排行榜seo01网站
  • 泰安网站建设培训我国网络营销现状分析
  • 网站建设知名软文写作技巧及范文
  • wordpress付费下载破解版seo营销策略
  • 七个php源码下载的网站自媒体平台注册官网
  • 隆基泰和 做网站竞价托管服务多少钱
  • 百度公司做网站可靠吗南平网站seo
  • 网站建设都包括哪些方面个人在线做网站免费
  • 自己做网站详细步骤中国十大公关公司排名
  • java做网站用什么框架seo引流什么意思
  • 哪个网站有成品的毕业论文seo关键词排名工具
  • 网站开发 自我评价发帖子最好的几个网站
  • 百度网站怎么用网络广告的概念
  • 沧州市网站制作公司怎么在百度上推广自己的公司信息
  • 建设服务型政府网站友情链接交换要注意哪些问题
  • 怎么做交易猫钓鱼网站电商培训心得体会
  • 企业网站代码模板aso优化榜单