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.
优先级继承
在信号量中使用优先级继承是为了解决优先级翻转问题。优先级翻转问题可以查
阅有关书籍了解概念。关于使用优先级继承的信号量,将在后续的博客中推出。
- Nuttx操作系统信号量Semaphore
- 信号量semaphore
- 信号量Semaphore
- 信号量semaphore
- 信号量Semaphore
- 信号量Semaphore
- 信号量Semaphore
- 信号量semaphore
- semaphore信号量
- 信号量Semaphore
- 信号量 semaphore
- 信号量 semaphore
- 信号量Semaphore
- Semaphore信号量
- 信号量semaphore
- 信号量Semaphore
- Semaphore (信号量)
- Semaphore(信号量)
- 一个JDK版本问题引发的思考--Java环境配置 && Eclipse的JDK配置
- APP安全报告第一期:难以置信,手机银行APP的安全性居然低到22分!
- 机器学习十大算法的核心思想、工作原理、适用 情况及优缺点
- Galactic History URAL
- vector的基本操作和用法
- Nuttx操作系统信号量Semaphore
- 决策树之C4.5算法
- Linux网络编程--初等网络函数介绍(TCP)
- js jquery 获取元素(父节点,子节点,兄弟节点),元素筛选
- maven+eclipse搭建项目工程ssm整合(三)
- slf4j
- 软件需求说明
- Linux的wget命令
- 数据库优化