Nuttx操作系统信号量Semaphore

来源:互联网 发布:考单片机的高级证书 编辑:程序博客网 时间:2024/06/16 23:58

Nuttx 操作系统信号量Semaphore

Nuttx系统中采用信号量来同步线程,保护临界资源。与linux系统中对信号的使用方法类似,
Nuttx系统中对信号量的操作包括以下几种:

  • sem_init
  • sem_wait
  • sem_post
  • sem_trywait
  • sem_timedwait
  • sem_getvalue
  • sem_destory

    上面的方法是对信号量操作的基本方法。Nuttx系统还提供了对”有名”信号量操作的额
    外函数:

  • sem_open
  • sem_close
  • sem_unlink

    “有名”信号量在系统中创建了一个inode与之关联,操作相对于“匿名”信号量相对
    复杂。本节内容只说明“无名”信号量。


Nuttx内核中Semaphore的数据结构

/* This is the generic semaphore structure. */struct sem_s{    volatile int16_t semcount;     /* >0 ->Num counts available */                                 /* <0 ->Num tasks waiting for semaphore */  /* If priority inheritance is enabled, then we have to keep track of which   * tasks hold references to the semaphore.   */#ifdef CONFIG_PRIORITY_INHERITANCE    uint8_t flags;                 /* See PRIOINHERIT_FLAGS_* definitions */# if CONFIG_SEM_PREALLOCHOLDERS > 0    FAR structsemholder_s *hhead; /* List of holders of semaphore counts */# else    structsemholder_s holder;     /* Single holder */# endif#endif};

为了容易理解在Nuttx系统中对信号的操作,假设没有定义宏
CONFIG_PRIORITY_INHERITANCE 和 CONFIG_SEM_PREALLOCHOLDERS 为 0.

数据结构struct sem_s的第一个变量为semcount, 它有两层含义,当其>0时,表示该
信号量可用数,也就是当有线程调用sem_wait()时无须等待。当该值<0时,表示有多
少线程等待该信号量。当每次调用sem_wait()时,信号量的sem_count减1,当调用
sem_post()时,信号量的sem_count加1.

sem_init

sem_init()函数初始化信号量,设置该信号量的sem_count值为初始值。一般地,将
信号量的初始值初始化为0,表示该信号量目前不可使用。如果用信号量表示初始时
刻可用的资源数,初始化时刻将该信号量的值初始化为>0的数值。

比如,老师有某一个教室门的钥匙2个,那么同时最多可以给两个同学使用,此时,
可以将该信号量的值初始化为2,第一个同学领走一个后剩余一个,第二个同学领走
后剩余0个,第三个同学来领钥匙的时候,钥匙数量为-1,表示有一个同学在等待其
他同学还回钥匙。

sem_wait

在企图占有信号量时,使用sem_wait()来获取信号量,如果信号量可用,即信号量
的sem_count>0,那么该函数将该信号量的sem_count减1,然后立即返回。

      if (sem->semcount > 0)        {          /* It is, let the task take the semaphore. */          sem->semcount--;          sem_addholder(sem);          rtcb->waitsem = NULL;          ret = OK;        }

否则,将sem_count减1, 然后该线程的TCB被移到内核中的g_waitingforsemaphore
链表上,其他任务将获得CPU的使用权。

          /* Handle the POSIX semaphore (but don't set the owner yet) */          sem->semcount--;          /* Save the waited on semaphore in the TCB */          rtcb->waitsem = sem;          ...          /* Add the TCB to the prioritized semaphore wait queue */          set_errno(0);          up_block_task(rtcb, TSTATE_WAIT_SEM);          /* When we resume at this point, either (1) the semaphore has been           * assigned to this thread of execution, or (2) the semaphore wait           * has been interrupted by a signal or a timeout.  We can detect these           * latter cases be examining the errno value.           *           * In the event that the semaphore wait was interrupted by a signal or           * a timeout, certain semaphore clean-up operations have already been           * performed (see sem_waitirq.c).  Specifically:           *           * - sem_canceled() was called to restore the priority of all threads           *   that hold a reference to the semaphore,           * - The semaphore count was decremented, and           * - tcb->waitsem was nullifed.           *           * It is necesaary to do these things in sem_waitirq.c because a long           * time may elapse between the time that the signal was issued and           * this thread is awakened and this leaves a door open to several           * race conditions.           */          if (get_errno() != EINTR && get_errno() != ETIMEDOUT)            {              /* Not awakened by a signal or a timeout...               *               * NOTE that in this case sem_addholder() was called by logic               * in sem_wait() fore this thread was restarted.               */              ret = OK;            }
sem_wait()函数的返回可能由两个原因导致:    - 该线程得到了信号量,即其他的线程释放了信号量。    - 该线程被信号中断或者等待超时究竟是因为什么原因导致sem_wait()函数返回,可以通过查询该线程的errno来获取返回原因。

sem_post

当有线程调用sem_post()释放信号量,该信号量的sem_count加1。如果有线程
在等待该信号量,那么等待该信号量的线程将会被唤醒。在Nuttx系统中行为就
是,检查内核中g_waitingforsemaphore链表上的任务,是否有任务在等待该
信号量,如果有,将等待该信号量的任务添加到内中中g_readytorun链表上。

        sem->semcount++;        ...        if (sem->semcount <= 0)        {          /* Check if there are any tasks in the waiting for semaphore           * task list that are waiting for this semaphore. This is a           * prioritized list so the first one we encounter is the one           * that we want.           */          for (stcb = (FAR struct tcb_s *)g_waitingforsemaphore.head;               (stcb && stcb->waitsem != sem);               stcb = stcb->flink);          if (stcb != NULL)            {              ...              /* It is, let the task take the semaphore */              stcb->waitsem = NULL;              /* Restart the waiting task. */              up_unblock_task(stcb);            }        }

sem_trywait

该函数比较简单,只是查询该信号量当前是否可用(sem_count > 0),如果可用,
sem_count 减1,然后返回OK。如果不可用,则返回ERROR,错误码值记录在线
程的errno中。

     if (sem->semcount > 0)        {          /* It is, let the task take the semaphore */          sem->semcount--;          rtcb->waitsem = NULL;          ret = OK;        }      else        {          /* Semaphore is not available */          set_errno(EAGAIN);        }

sem_timedwait

sem_timedwait()用于获取信号量。如果信号量可用(sem_count>0),那么
sem_count减1,直接返回OK。如果信号量不可用,则等待若干时间。该函数
返回时,可以通过查看线程的errno来确定是由于等待超时,或者得到信号量而
返回。

等待超时实现机制使用了内核中的watchdog(看门狗)定时器,通过设置
watchdog延迟触发时间,当时间到后,watchdog定时服务函数将会被调用,
唤醒等待线程。

  ...  rtcb->waitdog = wd_create();  if (!rtcb->waitdog)    {      errcode = ENOMEM;      goto errout;    } ...  ret = sem_trywait(sem);  if (ret == OK)    {      /* We got it! */      goto success_with_irqdisabled;    } ...   /* Start the watchdog */  (void)wd_start(rtcb->waitdog, ticks, (wdentry_t)sem_timeout, 1, getpid());    /* Now perform the blocking wait */  ret = sem_wait(sem);  if (ret < 0)    {      /* sem_wait() failed.  Save the errno value */      errcode = get_errno();    }  /* Stop the watchdog timer */  wd_cancel(rtcb->waitdog);  if (errcode != OK)    {      goto errout_with_irqdisabled;    }  ...  

等待信号量sem_wait()返回后,禁止掉watchdog。

我们再来看看watchdog定时器做了什么工作?

void sem_timeout(int argc, wdparm_t pid){  /* Get the TCB associated with this PID.  It is possible that   * task may no longer be active when this watchdog goes off.   */  wtcb = sched_gettcb((pid_t)pid);  /* It is also possible that an interrupt/context switch beat us to the   * punch and already changed the task's state.   */  if (wtcb && wtcb->task_state == TSTATE_WAIT_SEM)    {      /* Cancel the semaphore wait */      sem_waitirq(wtcb, ETIMEDOUT);    }  ...  }void sem_waitirq(FAR struct tcb_s *wtcb, int errcode){  ...  if (wtcb->task_state == TSTATE_WAIT_SEM)    {      sem_t *sem = wtcb->waitsem;      sem_canceled(wtcb, sem);      /* And increment the count on the semaphore.  This releases the count       * that was taken by sem_post().  This count decremented the semaphore       * count to negative and caused the thread to be blocked in the first       * place.       */      sem->semcount++;      /* Indicate that the semaphore wait is over. */      wtcb->waitsem = NULL;      /* Mark the errno value for the thread. */      wtcb->pterrno = errcode;      /* Restart the task. */      up_unblock_task(wtcb);    }  ...}

watchdog服务函数是在定时器的中断服务函数中被调用的,所以使用
sem_waitirq()函数唤醒等待该信号量的线程。

sem_getvalue

sem_getvalue()函数用来查询信号的sem_count的值。

int sem_getvalue(FAR sem_t *sem, FAR int *sval){  if (sem && sval)    {      *sval = sem->semcount;      return OK;    }  else    {      set_errno(EINVAL);      return ERROR;    }}

sem_destory

如果没有使用优先级继承,那么sem_destory()函数其实什么有意义的事都没有
做,唯一做的事是将sem_count的值初始化为1.

优先级继承

在信号量中使用优先级继承是为了解决优先级翻转问题。优先级翻转问题可以查
阅有关书籍了解概念。关于使用优先级继承的信号量,将在后续的博客中推出。

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我和二伯母愉情电子书 与邻居我和邻居阿夷愉情 我和农村留守妇女愉情真实故事 西游记 之三插王母 水原梨花母友人中文字幕 屁孩睡走叔母3中文字幕 林氏集团和伯母 太监跟娘娘私通小说 欧阳锋跟他大嫂私通在哪一集 太监跟娘娘私通 我和唐伯母在厨房 同居了大嫂中文字幕 年轻的后妈1完整高清免费观看 漂亮的老婆在线中字幕 母亲今晚让你入个够免费阅读 漂亮的续母韩国电影 我的漂亮后妈201电影 朋友的母亲3字幕中文翻译 邻居喝醉我睡了他老婆 白天当儿子晚上当丈夫视频 二姑装睡让我 白天是儿子晚上是丈夫阅读 老公去世和儿儿子做 白天是我妈晚上是老婆 蜜母耻中文字幕下载 友人之母中文字幕视频 友人之母增尾彩 内田春菊友人之母 母友人佐佐木明希中文字幕 友人之母谷源希美西瓜 母友人三浦中文字幕在线播放 大嫂被逼无奈和小叔传种接代 母友人作品封面 6公公与儿熄中文字幕完整视频 退休闲负的公公在家要了我5 我妻子的母亲hd高清中文字幕 叔母到我公寓在线播放 叔母来探望生病的我 叔父叔母来我家住数 请假在朋友妈猛烈中文字幕 母亲的朋友 谷原西美