
- 中文名称
- 信号量
- 作 用
- 两个或多个关键代码不被并发调用
- 别 名
- 信号灯
- 外文名称
- Semaphore
- 要 求
- 线程必须获取一个信号量
- 类 型
- 计算机、电子
-
2022-01-05 20:40:39
信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步, FreeRTOS中信号量又分为二值信号量、 计数型信号量、互斥信号量和递归互斥信号量。不同的信号量其应用场景不同,但有些应用场景是可以互换着使用的
信号量用于控制共享资源访问的场景相当于一个上锁机制, 代码只有获得了这个锁的钥匙才能够执行。
信号量的另一个重要的应用场合就是任务同步,用于任务与任务或中断与任务之间的同步。 在执行中断服务函数的时候可以通过向任务发送信号量来通知任务它所期待的事件发生了, 当退出中断服务函数以后在任务调度器的调度下同步的任务就会执行。在编写中断服务函数的时候我们都知道一定要快进快出,中断服务函数里面不能放太多的代码,否则的话会影响的中断的实时性。 裸机编写中断服务函数的时候一般都只是在中断服务函数中打个标记,然后在其他的地方根据标记来做具体的处理过程。在使用 RTOS 系统的时候我们就可以借助信号量完成此功能, 当中断发生的时候就释放信号量,中断服务函数不做具体的处理。具体的处理过程做成一个任务,这个任务会获取信号量,如果获取到信号量就说明中断发生了,那么就开始完成相应的处理,这样做的好处就是中断执行时间非常短。 这个例子就是中断与任务之间使用信号量来完成同步,当然了, 任务与任务之间也可以使用信号量来完成同步。一、二值信号量
二值信号量通常用于互斥访问或同步, 二值信号量和互斥信号量非常类似,但是互斥信号量拥有优先级继承机制, 二值信号量没有优先级继承。 因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。
使用二值信号量来完成中断与任务同步的这个机制中,任务优先级确保了外设能够得到及时的处理,这样做相当于推迟了中断处理过程。 也可以使用队列来替代二值信号量,在外设事件的中断服务函数中获取相关数据,并将相关的数据通过队列发送给任务。如果队列无效的话任务就进入阻塞态,直至队列中有数据,任务接收到数据以后就开始相关的处理过程。
二值信号量的使命就是同步,完成任务与任务或中断与任务之间的同步。大多数情况下都是中断与任务之间的同步1.1、创建二值信号量
1、函数 xSemaphoreCreateBinary()
使用此函数创建二值信号量的话信号量所需要的 RAM 是由 FreeRTOS 的内存管理部分来动态分配的。函数原型如下:SemaphoreHandle_t xSemaphoreCreateBinary( void )
返回值:NULL: 二值信号量创建失败。其他值: 创建成功的二值信号量的句柄。
2、函数 xSemaphoreCreateBinaryStatic()
此函数也是创建二值信号量的,只不过使用此函数创建二值信号量的话信号量所需要的RAM 需要由用户来分配,函数原型如下:SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
参数:pxSemaphoreBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:NULL: 二值信号量创建失败。其他值: 创建成功的二值信号量句柄。1.2、释放信号量
释放信号量分为任务级和中断级。不管是二值信号量、计数型信号量还是互斥信号量,它们都使用以下两个函数释放信号量。
1、 函数 xSemaphoreGive()
此函数用于释放二值信号量、计数型信号量或互斥信号量, 函数原型如下:BaseType_t xSemaphoreGive( xSemaphore )
参数:xSemaphore: 要释放的信号量句柄。
返回值:pdPASS: 释放信号量成功。errQUEUE_FULL: 释放信号量失败。
2、函数 xSemaphoreGiveFromISR()
此函数用于在中断中释放信号量, 此函数只能用来释放二值信号量和计数型信号量,绝对不能用来在中断服务函数中释放互斥信号量。此函数原型如下:BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
参数:xSemaphore: 要释放的信号量句柄。pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS: 释放信号量成功。errQUEUE_FULL: 释放信号量失败。1.3、获取信号量
不管是二值信号量、计数型信号量还是互斥信号量,它们都使用以下两个函数获取信号量。
1、函数 xSemaphoreTake()
此函数用于获取二值信号量、计数型信号量或互斥信号量,函数原型如下:BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
参数:xSemaphore: 要获取的信号量句柄。xBlockTime: 阻塞时间。
返回值:pdTRUE: 获取信号量成功。pdFALSE: 超时,获取信号量失败。
2、函数 xSemaphoreTakeFromISR ()
此函数用于在中断服务函数中获取信号量, 此函数用于获取二值信号量和计数型信号量,绝 对 不 能 使 用 此 函 数 来 获 取 互 斥 信 号 量 。此函数原型如下:BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
参数:xSemaphore: 要获取的信号量句柄。pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS: 获取信号量成功。pdFALSE: 获取信号量失败。二、计数型信号量
计数型信号量叫做数值信号量
2.1、创建计数型信号量
1、函数 xSemaphoreCreateCounting()
此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数原型如下:SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,UBaseType_t uxInitialCount )
参数:uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。uxInitialCount: 计数信号量初始值。
返回值:NULL: 计数型信号量创建失败。其他值: 计数型信号量创建成功,返回计数型信号量句柄。
2、函数 xSemaphoreCreateCountingStatic()
此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存需要由用户分配。函数原型如下:SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount,StaticSemaphore_t * pxSemaphoreBuffer )
参数:uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。uxInitialCount: 计数信号量初始值。pxSemaphoreBuffer: 指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:NULL: 计数型信号量创建失败。其他值: 计数型号量创建成功,返回计数型信号量句柄。2.2、释放和获取计数信号量
计数型信号量的释放和获取与二值信号量相同。
三、互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量, 在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。 互斥信号量适合用于那些需要互斥访问的应用中。
互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。 当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级, 这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。
优先级继承并不能完全的消除优先级翻转, 它只是尽可能的降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,原因如下:
1、互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
2、中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。3.1、创建互斥信号量
1、函数 xSemaphoreCreateMutex()
此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数原型如下:SemaphoreHandle_t xSemaphoreCreateMutex( void )
返回值:NULL: 互斥信号量创建失败。其他值: 创建成功的互斥信号量的句柄。
2、函数 xSemaphoreCreateMutexStatic()
此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的RAM 需要由用户来分配,函数原型如下:SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
参数:pxMutexBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:NULL: 互斥信号量创建失败。其他值: 创建成功的互斥信号量的句柄。3.2、释放互斥信号量
释放互斥信号量的时候和二值信号量 、计数型信号量一样 、都是用的函数xSemaphoreGive()
3.3、获取互斥信号量
获取互斥信号量的函数同获取二值信号量和计数型信号量的函数 相同 ,都是xSemaphoreTake()
更多相关内容 -
信号量
2019-04-23 21:43:44信号量称为进化版的互斥锁。由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据...信号量称为进化版的互斥锁。由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。线程从并行执行,变成了串行执行。与直接使用单进程无异。信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。例如,有5台打印机被多个线程共同使用,如果使用互斥锁,则每次同时只能有一个打印机被一个线程使用,但是如果采用信号量,那么同时最多可有5个线程共同使用五台打印机,使得共享资源得到充分利用,程序并行性提高,效率更高。其实信号量初值为1时的情况就是互斥锁。信号量是互斥锁的加强版。
主要应用函数:
sem_init函数 sem_destroy函数 sem_wait函数
sem_trywait函数 sem_timedwait函数 sem_post函数
以上6 个函数的返回值都是:成功返回0,失败返回-1,同时设置errno。(注意,它们没有pthread前缀) 因为,信号量既可以用于线程间同步,也可以用于进程间同步,因此失败返回值为-1,同时置errno。
sem_t类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。
sem_t sem; 规定信号量sem不能 < 0。 头文件 <semaphore.h>
(1)信号量基本操作
sem_wait: 信号量大于0,则信号量--(类比pthread_mutex_lock);信号量等于0,造成线程阻塞
sem_post:将信号量++,同时唤醒阻塞在信号量上的线程(类比pthread_mutex_unlock)。
由于sem_t的实现对用户隐藏,所以所谓的++、--操作只能通过函数来实现,而不能直接++、--符号。信号量的初值,决定了占用信号量的线程的个数。
(2)sem_init函数
int sem_init(sem_t *sem, int pshared, unsigned int value);
作用:初始化一个信号量。参1:sem信号量;参2:pshared取0用于线程间;取非0(一般为1)用于进程间;参3:value指定信号量初值。
(3)sem_destroy函数
int sem_destroy(sem_t *sem);
作用:销毁一个信号量
(4)sem_wait函数
int sem_wait(sem_t *sem);
作用:给信号量加锁
(5)sem_post函数
int sem_post(sem_t *sem);
作用:给信号量解锁 ++
(6)sem_trywait函数
int sem_trywait(sem_t *sem);
作用:尝试对信号量加锁--(与sem_wait的区别类比lock和trylock)
(7)sem_timedwait函数
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
作用:限时尝试对信号量加锁。参2:abs_timeout采用的是绝对时间(同前面)。
总结:互斥量、信号量既可以用于进程间同步,也可以用于线程间同步(一般来说,进程间同步信号量用的多一点);条件变量需要结合互斥量使用;读写锁用于线程间同步,而文件锁用于进程间同步。
(8)生产者消费者信号量模型
使用信号量完成线程间同步,模拟生产者,消费者问题。
分析:如果仓库中装满了产品,生产者不能生产,只能阻塞;仓库中没有产品,消费者不能消费,只能等待数据。
//两个消费者,一个生产者的情况
#include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <stdio.h> #include <semaphore.h> #include <string.h> #define NUM 10 //仓库空间容量为10 int prdc[NUM] = {0}; //0值代表该位置为闲置(空) sem_t sem_blank; //定义一个信号量表示闲置的空间 sem_t sem_prdc; //定义一个信号量表示产品的数量 int j=0; //两个消费者都需要消耗产品,因此定义全局变量j,两消费者互斥访问 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //互斥锁 用于互斥访问j void *productor( void *arg ) //生产者 { srand( time( NULL ) ); int i = 0; while(1) { sem_wait( &sem_blank ); prdc[i] = (rand( )%400+1); printf("++++++++The production is %d.\n", prdc[i]); sem_post( &sem_prdc ); //对全局变量的操作必须位于wait与post之间 i=( (i+1)%NUM ); sleep( rand( )%3 ); } return NULL; } void *consumer( void *arg ) { int s = (int)arg; srand( time( NULL ) ); while(1) { sem_wait( &sem_prdc ); pthread_mutex_lock( &mutex ); //互斥访问j printf("I am the %dth consumer, and I consumed the product of %d.\n", s, prdc[j]); prdc[j] = 0; j =( (j+1)%NUM ); pthread_mutex_unlock( &mutex ); sem_post( &sem_blank ); sleep( rand( )%3 ); } return NULL; } int main( void ) { pthread_t pid, cid1, cid2; int ret; sem_init( &sem_blank, 0, NUM ); sem_init( &sem_prdc, 0, 0 ); ret = pthread_create( &pid, NULL, productor, NULL ); if( ret != 0 ) { fprintf( stderr, "pthread_create error1: %s.\n", strerror(ret) ); exit(1); } ret = pthread_create( &cid1, NULL, consumer, (void *)1 ); if( ret != 0 ) { fprintf( stderr, "pthread_create error2: %s.\n", strerror(ret) ); exit(1); } ret = pthread_create( &cid2, NULL, consumer, (void *)2 ); if( ret != 0 ) { fprintf( stderr, "pthread_create error3: %s.\n", strerror(ret) ); exit(1); } pthread_join( pid, NULL ); pthread_join( cid1, NULL ); pthread_join( cid2, NULL ); sem_destroy( &sem_blank ); sem_destroy( &sem_prdc ); pthread_mutex_destroy( &mutex); return 0; }
[root@localhost 02_pthread_sync_test]# ./semaphore
++++++++The production is 331.
I am the 2th consumer, and I consumed the product of 331.
++++++++The production is 371.
I am the 1th consumer, and I consumed the product of 371.
++++++++The production is 213.
I am the 1th consumer, and I consumed the product of 213.
++++++++The production is 260.
I am the 2th consumer, and I consumed the product of 260.
++++++++The production is 296.
I am the 1th consumer, and I consumed the product of 296.
++++++++The production is 59.
++++++++The production is 118.
I am the 2th consumer, and I consumed the product of 59.
I am the 1th consumer, and I consumed the product of 118.
++++++++The production is 228.
I am the 1th consumer, and I consumed the product of 228.
++++++++The production is 27.
++++++++The production is 360.
++++++++The production is 190.
I am the 2th consumer, and I consumed the product of 27.
I am the 2th consumer, and I consumed the product of 360.
I am the 1th consumer, and I consumed the product of 190.
++++++++The production is 177.
++++++++The production is 211.
I am the 1th consumer, and I consumed the product of 177.
I am the 2th consumer, and I consumed the product of 211.
分析:
- 注意一点:生产者从i=0开始生产,那么消费者必须从j=0位置开始消费,保证位置一一对应,如果不对应,从逻辑上就是错误的。即整个逻辑过程就是:生产者在队列头部加产品,消费者从队列尾部拿走产品,整个队列从逻辑上是一个循环队列。
- 对j的访问要互斥访问;
- 从结果可以看出,先生产出来的先被消费,符合逻辑。
练习:结合生产者消费者信号量模型,揣摩sem_timedwait函数作用。编程实现,一个线程读用户输入, 另一个线程打印“hello world”。如果用户无输入,则每隔5秒向屏幕打印一个“hello world”;如果用户有输入,立刻打印“hello world”到屏幕。
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> #define N 1024 sem_t s; void *tfn(void *arg) { char buf[N]; while (1) { read(STDIN_FILENO, buf, N); //阻塞读 printf( " %s", buf); sem_post(&s); } return NULL; } int main(void) { pthread_t tid; struct timespec t = {0, 0}; sem_init(&s, 0, 0); pthread_create(&tid, NULL, tfn, NULL); t.tv_sec = time(NULL) + 1; //防止时间失效,使sem_timedwait参数无效 t.tv_nsec = 0; while (1) { sem_timedwait(&s, &t); printf(" hello world\n"); t.tv_sec = time(NULL) + 5; t.tv_nsec = 0; } pthread_join(tid, NULL); sem_destroy(&s); return 0; }
分析:
理解read函数的作用。read系统调用从文件中读数据时,默认方式为阻塞读,即如果没有读取数据(没有遇到换行符(enter键或者结束符)),则会一直阻塞等待数据的到来。对于空文件,有结束符,因此read读取时直接返回结果,读取的字节数为0,结束符不会打印出来;对于从标准输入(键盘)读取数据时,只有按到了enter(换行符),则read才会读取到数据,而换行符会被打印出来。read函数阻塞的话,则信号量的值一直为0,则主控线程中每隔5s打印一次hello world;(此时sem_timedwait函数执行失败,返回-1,置errno)。一旦read读取了数据,则信号量加1,主控线程马上执行sem_timedwait(&s, &t);成功,返回0 ,且向屏幕打印一次hello world。
-
UCOS-Ⅲ:信号量
2021-03-09 20:52:38uC/OS-Ⅲ操作系统,任务间同步篇之信号量的API调用接口文章目录
UCOS-Ⅲ:信号量
一、信号量基本概念
信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问 (临界资源指同一时刻只能有有限个访问),常用于协助一组相互竞争的任务来访问临界资源。运行机制可以理解为:信号量是一个正值,代表资源的可访问数目,当有任务访问时,这个数目减一,任务访问完成时,任务访问结束,释放他,让他加一,信号量为0时,其他任务则不能获取他,选择退出或者等待挂起,直到有信号量释放后,按照优先级来获取信号量,获取后就绪,其运行流程大概如下图:
UCOS中信号量是内核对象,通过数据类型OS_SEM 定义,OS_SEM 源于结构体os_sem,UCOS中-Ⅲ的信号量相关的代码都被放在OS_SEM.C 中,通过设置OS_CFG.H 中的OS_CFG_SEM_EN 为1 使能信号量
/* ----------------------------- SEMAPHORES ---------------------------- */ #define OS_CFG_SEM_EN 1u //使能或禁用多值信号量 #define OS_CFG_SEM_DEL_EN 1u //使能或禁用 OSSemDel() 函数 #define OS_CFG_SEM_PEND_ABORT_EN 1u //使能或禁用 OSSemPendAbort() 函数 #define OS_CFG_SEM_SET_EN 1u //使能或禁用 OSSemSet() 函数
信号量的结构体如下:
struct os_sem { /* Semaphore */ /* ------------------ GENERIC MEMBERS ------------------ */ OS_OBJ_TYPE Type; /* Should be set to OS_OBJ_TYPE_SEM */ CPU_CHAR *NamePtr; /* Pointer to Semaphore Name (NUL terminated ASCII) */ OS_PEND_LIST PendList; /* List of tasks waiting on semaphore */ /* ------------------ SPECIFIC MEMBERS ------------------ */ OS_SEM_CTR Ctr; CPU_TS TS; };
第一个变量是**“Type”域**:表明UCOS识别所定义的是一个信号量。其它的内核对象也有“Type”域作为结构体的第一个变量。如果函数要调用一个内核对象,UCOS会检测所调用的内核对象的数据类型是否对应。例如,如果需要传递一个消息队列OS_Q 给函数,但实际传递的是一个信号量OS_SEM,UCOS就会检测出这是一个无效的参数,并返回错误代号。
第二个指针指向内核对象的名字:每个内核对象都可以被赋予一个名字,名字有ASCII 字符串组成,但必须以空字符结尾。
第三个等待列表PendList:若有多个任务等待信号量,信号量就会将这些任务放入其挂起队列中。
第四个包含一个信号量计数值:信号量计数值可以定义为8 位,16 位,或32 位,取决于OS_TYPE.H 中的OS_SEM_CTR是如何被定义的,这个值用于判断信号量可用的资源数目**(此值的上限大于1为多值信号量,只为0或者1则为二值信号量,UCOS-Ⅲ没有对这两个多做区分,全看如何使用)**
第五个CPU_TS时间戳:信号量中包含了一个时间戳变量,存储了上一次信号量被提交时的时间戳。当信号量被提交时,CPU 的时间戳被读取并存在信号量的时间戳变量中,当OSSemPend()被调用时就能读取这个时间戳变量。
用户代码也不能直接访问信号量中的变量。必须通过UCOS-III 提供的API来进行访问!
二、调用API
UCOS中信号量的调用API有以下四个API,分别为创建、删除、获取、释放信号量
2.1 创建信号量函数OSSemCreate()
OSSemCreate()函数进行创建一个信号量,跟消息队列的创建差不多,我们知道,其实这里的“创建信号量”指的就是对内核对象(信号量)的一些初始化。
函数入口:
void OSSemCreate (OS_SEM *p_sem, //多值信号量控制块指针 CPU_CHAR *p_name, //多值信号量名称 OS_SEM_CTR cnt, //资源数目或事件是否发生标志 OS_ERR *p_err) //返回错误类型
介绍:
p_sem 指向信号量变量的指针。 p_name 指向信号量变量名字字符串的指针。 cnt 信号量的初始值,用作资源保护的信号量这个值通常跟资源的数量相同,用做标志事件发生的信号量这个值设置为0,标志事情还没有发生。 p_err 指向返回错误类型的指针。 p_err返回错误标志,其具体的返回值对应的情景如下
错误返回值 错误类型 OS_ERR_CREATE_ISR 在中断中创建信号量是不被允许的,返回错误。 OS_ERR_ILLEGAL_CREATE_RUN_TIME 在定义OSSafetyCriticalStartFlag 为DEF_TRUE 后就不运行创建任何内核对象。 OS_ERR_NAME 参数p_name 是个空指针。 OS_ERR_OBJ_CREATED 信号量已经被创建(不过函数中并没有涉及到这个错误的代码) OS_ERR_OBJ_PTR_NULL 参数p_sem 是个空指针。 OS_ERR_OBJ_TYPE 参数p_sem 被初始化为别的内核对象了。 OS_ERR_NONE 无错误,继续执行 使用实例:
//定义结构体 OS_SEM SemOfKey; //标志KEY1是否被按下的多值信号量 //使用前调用API初始化 任务体 { /* 创建多值信号量 SemOfKey */ OSSemCreate((OS_SEM *)&SemOfKey, //指向信号量变量的指针 (CPU_CHAR *)"SemOfKey", //信号量的名字 (OS_SEM_CTR )5, //表示现有资源数目 (OS_ERR *)&err); //错误类型 }
2.2 信号量删除函数OSSemDel()
OSSemDel()用于删除一个信号量,信号量删除函数是根据信号量结构(信号量句柄)直接删除的,删除之后这个信号量的所有信息都会被系统清空,而且不能再次使用这个信号量了,但是需要注意的是,如果某个信号量没有被定义,那也是无法被删除的,如果有任务阻塞在该信号量上,那么尽量不要删除该信号量。使用之前首先要将OS_CFG_SEM_DEL_EN 这个宏置1,注意调用这个函数后,之前用信号量保护的资源将不再得到保护。
函数入口:
OS_OBJ_QTY OSSemDel (OS_SEM *p_sem, //多值信号量指针 OS_OPT opt, //选项 OS_ERR *p_err) //返回错误类型
参数名称 参数作用 p_sem 指向信号量变量的指针。 opt 删除信号量时候的选项,有以下两个选择。 p_err 指向返回错误类型的指针,有以下几种可能。 参数选项选择
选项 作用 OS_OPT_DEL_NO_PEND 当信号量的等待列表上面没有相应的任务的时候才删除信号量。 OS_OPT_DEL_ALWAYS 不管信号量的等待列表是否有相应的任务都删除信号量。 错误类型
错误返回值 错误类型 OS_ERR_DEL_ISR 企图在中断中删除信号量。 OS_ERR_OBJ_PTR_NULL 参数p_sem 是空指针。 OS_ERR_OBJ_TYPE 参数p_sem 指向的内核变量类型不是信号量 OS_ERR_OPT_INVALID opt 在给出的选项之外 OS_ERR_TASK_WAITING 在选项opt 是OS_OPT_DEL_NO_PEND 的时候,并且信号量等待列表上有等待的任务。 同时该函数有一个返回值,返回值的含义为:
删除信号量的时候,会将信号量等待列表上的任务脱离该信号量的等待列表。返回值表示的就是脱离等待列表的任务个数。
使用实例
OS_SEM SemOfKey;; //声明信号量 /* 删除信号量 sem*/ OSSemDel ((OS_SEM *)&SemOfKey, //指向信号量的指针 OS_OPT_DEL_NO_PEND, (OS_ERR *)&err); //返回错误类型
2.3 信号量释放函数OSSemPost()
当信号量有值的时候,任务才能获取信号量,有两个方式使信号量有值,一个是在创建的时候进行初始化,将它可用的信号量个数设置一个初始值;如果该信号量用作二值信号量,那么我们在创建信号量的时候其初始值的范围是0~1,假如初始值为1个可用的信号量的话,被获取一次就变得无效了,那就需要我们释放信号量,uCOS 提供了信号量释放函数,每调用一次该函数就释放一个信号量。UCOS可以一直释放信号量,但如果用作二值信号量的话,一直释放信号量就达不到同步或者互斥访问的效果,虽然说uCOS 的信号量是允许一直释放的,但是,信号量的范围还需我们用户自己根据需求进行决定,当用作二值信号量的时候,必须确保其可用值在0~1 范围内;而用作计数信号量的话,其范围是由用户根据实际情况来决定的
函数入口:
OS_SEM_CTR OSSemPost (OS_SEM *p_sem, //多值信号量控制块指针 OS_OPT opt, //选项 OS_ERR *p_err) //返回错误类型
参数名称 参数作用 p_sem 指向要提交的信号量的指针 opt 发布信号时的选项,可能有以下几个选项 p_err 指向返回错误类型的指针,错误的类型如下。(只列了必要部分) 选项列表:
选项 功能 OS_OPT_POST_1 发布给信号量等待列表中优先级最高的任务。 OS_OPT_POST_ALL 发布给信号量等待列表中所有的任务。 OS_OPT_POST_NO_SCHED 提交信号量之后要不要进行任务调度,默认是要进行任务调度的,选择该选项可能的原因是想继续运行当前任务,因为发布信号量可能让那些等待信号量的任务就绪,这个选项没有进行任务调度,发布完信号量当前任务还是继续运行。当任务想发布多个信号量,最后同时调度的话也可以用这个选项。可以跟上面两个选项之一相与做为参数。 错误值:
OS_ERR_SEM_OVF 信号量计数值已经达到最大范围了,这次提交会引起信号量计数值溢出。
返回值:
信号量计数值
使用实例:
OS_SEM SemOfKey; //标志KEY1 是否被按下的信号量 OSSemPost((OS_SEM *)&SemOfKey, //发布SemOfKey (OS_OPT )OS_OPT_POST_ALL, //发布给所有等待任务 (OS_ERR *)&err); //返回错误类型
2.4 信号量获取函数OSSemPend()
当任务获取了某个信号量的时候,该信号量的可用个数就减一,当它减到0 的时候,任务就无法再获取了,并且获取的任务会进入阻塞态(假如用户指定了阻塞超时时间的话)。如果某个信号量中当前拥有1 个可用的信号量的话,被获取一次就变得无效了,那么此时另外一个任务获取该信号量的时候,就会无法获取成功,该任务便会进入阻塞态,阻塞时间由用户指定。uCOS 支持系统中多个任务获取同一个信号量,假如信号量中已有多个任务在等待,那么这些任务会按照优先级顺序进行排列,如果信号量在释放的时候选择只释放给一个任务,那么在所有等待任务中最高优先级的任务优先获得信号量,而如果信号量在释放的时候选择释放给所有任务,则所有等待的任务都会获取到信号量
函数入口:
OS_SEM_CTR OSSemPend (OS_SEM *p_sem, //多值信号量指针 OS_TICK timeout, //等待超时时间 OS_OPT opt, //选项 CPU_TS *p_ts, //等到信号量时的时间戳 OS_ERR *p_err) //返回错误类型
参数:
参数 作用 p_sem 指向要获取的信号量变量的指针。 opt 可能是以下几个选项之一。 timeout 这个参数是设置的是获取不到信号量的时候等待的时间。如果这个值为0,表示一直等待下去,如果这个值不为0,则最多等待timeout 个时钟节拍。 p_ts 指向等待的信号量被删除,等待被强制停止,等待超时等情况时的时间戳的指针。 p_err 指向返回错误类型的指针,有以下几种类型。 功能选项:
功能 作用 OS_OPT_PEND_BLOCKING 如果不能即刻获得信号量,选项表示要继续等待。 OS_OPT_PEND_NON_BLOCKING 如果不能即刻获得信号量,选项表示不等待信号量。 错误类型:
错误 类型 OS_ERR_OBJ_DEL 信号量已经被删除了。 OS_ERR_OBJ_PTR_NULL 输入的信号量变量指针是空类型。 OS_ERR_OBJ_TYPE p_sem 指向的变量内核对象类型不是信号量。 OS_ERR_OPT_INVALID 参数opt 不符合要求。 OS_ERR_PEND_ABORT 等待过程,其他的任务调用了函数OSSemPendAbort 强制取消等待。 OS_ERR_PEND_ISR 企图在中断中等待信号量。 OS_ERR_PEND_WOULD_BLOCK 开始获取不到信号量,且没有要求等待。 OS_ERR_SCHED_LOCKED 调度器被锁住。 OS_ERR_STATUS_INVALID 系统出错,导致任务控制块的元素PendStatus 不在可能的范围内。 OS_ERR_TIMEOUT 等待超时。 OS_ERR_NONE 成功获取 返回值:
信号量计数值
使用实例:
ctr = OSSemPend ((OS_SEM *)&SemOfKey, //等待该信号量 SemOfKey (OS_TICK )0, //下面选择不等待,该参无效 (OS_OPT )OS_OPT_PEND_NON_BLOCKING,//如果没信号量可用不等待 (CPU_TS *)0, //不获取时间戳 (OS_ERR *)&err); //返回错误类型 if(err == OS_ERR_NONE) { //获取成功 }
三、信号量BUG-优先级反转
优先级反转是实时系统中的一个常见问题,存在于基于优先级的抢占式内核中,优先级反转的原理如下:
下图有三个调度任务L、M、H,任务H 的优先级高于任务M,任务M 的优先级高于任务L
任务L最开始运行的时候获取信号量,之后任务H开始执行,抢占了任务L,在任务H运行的时候,刚好需要获取信号量,但此时信号量还在任务L的手里,于是任务H进入挂起队列,任务L继续运行,在任务L没有释放信号量的时候,任务M过来抢占L运行,在任务L释放信号量时,任务H才继续执行,若任务M 需要执行很长时间,则任务H 会被延迟很长时间才执行,这叫做优先级反转。
解决方法是临时提高任务L的优先级,这一内容我们下一节互斥量再分析
四、使用实例
功能:信号量管理停车位资源,按键2按下释放信号量,停车位+1,串口打印数目,按键1按下获取信号量,停车位-1,串口显示是否获取成功
启动任务创建信号量
/* 创建多值信号量 SemOfKey */ OSSemCreate((OS_SEM *)&SemOfKey, //指向信号量变量的指针 (CPU_CHAR *)"SemOfKey", //信号量的名字 (OS_SEM_CTR )5, //表示现有资源数目 (OS_ERR *)&err); //错误类型
再创建两个任务
任务一主体:
/* ********************************************************************************************************* * KEY1 TASK ********************************************************************************************************* */ static void AppTaskKey1 ( void * p_arg ) { OS_ERR err; OS_SEM_CTR ctr; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变 //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR) //,开中断时将该值还原。 uint8_t ucKey1Press = 0; (void)p_arg; while (DEF_TRUE) { //任务体 if( Key_Scan ( macKEY1_GPIO_PORT, macKEY1_GPIO_PIN, 1, & ucKey1Press ) ) //如果KEY1被按下 { ctr = OSSemPend ((OS_SEM *)&SemOfKey, //等待该信号量 SemOfKey (OS_TICK )0, //下面选择不等待,该参无效 (OS_OPT )OS_OPT_PEND_NON_BLOCKING,//如果没信号量可用不等待 (CPU_TS *)0, //不获取时间戳 (OS_ERR *)&err); //返回错误类型 OS_CRITICAL_ENTER(); //进入临界段 if ( err == OS_ERR_NONE ) printf ( "\r\nKEY1被按下:成功申请到停车位,剩下%d个停车位。\r\n", ctr ); else if ( err == OS_ERR_PEND_WOULD_BLOCK ) printf ( "\r\nKEY1被按下:不好意思,现在停车场已满,请等待!\r\n" ); OS_CRITICAL_EXIT(); } OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err ); //每20ms扫描一次 } }
任务二主体:
static void AppTaskKey2 ( void * p_arg ) { OS_ERR err; OS_SEM_CTR ctr; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变 //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR) //,开中断时将该值还原。 uint8_t ucKey2Press = 0; (void)p_arg; while (DEF_TRUE) { //任务体 if( Key_Scan ( macKEY2_GPIO_PORT, macKEY2_GPIO_PIN, 1, & ucKey2Press ) ) //如果KEY2被按下 { ctr = OSSemPost((OS_SEM *)&SemOfKey, //发布SemOfKey (OS_OPT )OS_OPT_POST_ALL, //发布给所有等待任务 (OS_ERR *)&err); //返回错误类型 OS_CRITICAL_ENTER(); //进入临界段 printf ( "\r\nKEY2被按下:释放1个停车位,剩下%d个停车位。\r\n", ctr ); OS_CRITICAL_EXIT(); } OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err ); //每20ms扫描一次 } }
串口现象:
-
信号量——pv操作
2021-01-12 15:06:02无名信号量:无名信号量只能存在于内存中,要求使用信号量的进程必须能访问信号量所在的这一块内存,所以无名信号量只能应用在同一进程间的线程或者共享内存间的进程。 b.有名信号量:用于进程(线程)间同步互斥 1&...一、信号量概念
信号量是用来解决进程/线程之间的同步互斥问题的一种通信机制,它表示代表某一类资源。1.信号量的类型
a.无名信号量:无名信号量只能存在于内存中,要求使用信号量的进程必须能访问信号量所在的这一块内存,所以无名信号量只能应用在同一进程间的线程或者共享内存间的进程。
b.有名信号量:用于进程(线程)间同步互斥1>有名信号量必须指定一个相关联的文件名称,名称通常是文件系统中的某个文件,无名信号量不需要指定 2>有名信号量既可以用于线程间的同步,也可以用于进程间的同步;无名信号量通过shared参数来决定 是进程间还是相关线程共享 3>有名信号量随内核时间持续,一个进程创建一个信号量,另外的进程也可以通过该信号量的外部名(创建信号使用的文件名称) 来访问它。进程结束后,信号量还存在,并且信号量的值也不会改动。 4>无名信号量的持续性却是不定的:如果无名信号量是由单个进程内的各个线程共享的,那么该信号量就是随进程持续的,当该进程终止时它也会消失。如果某个无名信号量是在不同进程间同步的,该信号量必须存放在共享内存区中,只要该共享内存区存在,该信号量就存在。
2.信号量的pv操作
p操作:对信号量减1,若结果大于等于0,则进程继续,否则执行p操作的进程被阻塞等待释放。
v操作:对信号量加1,若结果小于等于0,则唤醒队列中一个因为p操作而阻塞的进程。3.信号量的三种操作
1>信号量初始化
初始某类资源到可用的值#include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); 参数1:sem,无名信号量 参数2:pshared,确定信号量是由进程间共享还是线程间共享,0-线程间,非0进程间 参数3:信号量的初始值 //有名信号量创建 #include <semaphore.h> sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); //第一种函数是当使用已有的有名信号量时调用该函数,flag 参数设为 0 //调用第二种函数,flag 参数应设为 O_CREAT ,如果有名信号量不存在,则会创建一个新的,如果存在,则会被使用并且不会再初始化。 //mode 参数指定谁可以访问信号量,即权限组
2>p操作申请资源
#include <semaphore.h> int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 参数:sem_wait是以原子操作的方式给信号量的值减1,但它会等到信号量非0时才会开始减法操作,如果此时信号量为0会进入阻塞等待的状态,直到信号量的值使它不再为0 sem:信号量 返回值:0成功 -1并设置出错码 第二个函数作用与第一个相同,此函数不阻塞线程,如果 sem 小于 0,直接返回一个错误(错误设置为 EAGAIN )。 第三个函数作用也与第一个相同,第二个参数表示阻塞时间,如果 sem 小于 0 ,则会阻塞,参数指定阻塞时间长度。 struct timespec { time_t tv_sec; /*Seconds*/ long tv_nsec; /*Nanoseconds [0 ...999999999]*/ }; if (信号量的值 > 0) { 申请资源的任务继续;信号量值--; } else { 申请资源的任务阻塞 }
3>V操作释放资源
#include <semaphore.h> int sem_post(sem_t *sem); 参数:sem 信号量 返回值: 0-成功 -1-失败并设置出错码 #include <semaphore.h> int sem_close(sem_t *sem); //如果进程没有调用该函数便退出了,内核会自动关闭任何打开的信号量。无论是调用该函数还是内核自动关闭,都不会改变释放之前的信号量值。
4>信号量销毁
无名信号量会随着进程的终结,自动销毁。在不终止进程的情况下,销毁信号量可以使用sem_destroy;(有名信号量,如果不释放,会一直存在)#include <semaphore.h> int sem_destroy(sem_t *sem); 参数:sem 信号值 返回值: 0-成功 -1并设置出错码
代码demon
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define TIME_OUT 2000 void *function1(void *arg); void *function2(void *arg); sem_t sem1,sem2; int main() { pthread_t tid1,tid2; int ret; ret = sem_init(&sem1,0,0); if( -1 == ret) { perror("sem_init"); } ret = sem_init(&sem2,0,0); if( -1 == ret) { perror("sem_init"); } ret = pthread_create(&tid1,NULL,function1,NULL); if( 0 != ret) { perror("pthread_create 1"); } ret = pthread_create(&tid2,NULL,function2,NULL); if( 0 != ret) { perror("pthreat_create 2"); } ret = pthread_join(tid1,NULL); if( 0 != ret) { perror("pthread_join1"); } ret = pthread_join(tid2,NULL); if( 0 != ret) { perror("pthread_join2"); } ret = sem_destroy(&sem1); if( -1 == ret ) { perror("sem_destory"); } ret = sem_destroy(&sem2); if( -1 == ret ) { perror("sem_destory"); } return 0; } void time_add_ms(struct timeval *time, uint ms) { time->tv_usec += ms * 1000; // 微秒 = 毫秒 * 1000 if(time->tv_usec >= 1000000) // 进位,1000 000 微秒 = 1 秒 { time->tv_sec += time->tv_usec / 1000000; time->tv_usec %= 1000000; } } void *function1(void *arg) { int i,ret; struct timespec timeout; struct timeval time; ret = sem_wait(&sem1); //p操作申请资源 if( -1 == ret) { perror("sem_wait"); } for(i = 1; i <=2; i++) { printf("sem1----ppppppppppppppppp----\n"); sleep(1); } ret = sem_post(&sem2); //v操作 if( -1 == ret ) { perror("sem_post"); } printf("sem2 -------vvvvvvvvvvvvvvvvvvv-------\n"); gettimeofday(&time, NULL); time_add_ms(&time, TIME_OUT); timeout.tv_sec = time.tv_sec; timeout.tv_nsec = time.tv_usec * 1000; printf("===time——out %d\n",sem_timedwait(&sem1, &timeout)); printf("sem1 time out -----ppppppppppppppppp----\n"); return NULL; } void *function2(void *arg) { int i,ret; for(i = 1; i <=2; i++) { printf("######ppppppppppppppppp######\n"); sleep(1); } ret = sem_post(&sem1);//v操作释放资源 if( -1 == ret ) { perror("sem_post"); } printf("sem2 ====vvvvvvvvvvvvvvvvvvv====\n"); ret = sem_wait(&sem2); //p操作申请资源 if( -1 == ret) { perror("sem_wait"); } for(i = 1; i <=3; i++) { printf("sem2 ====ppppppppppppppppp====\n"); sleep(1); } return NULL; }
运行结果:
-
iOS信号量详解
2020-12-31 21:31:02一、信号量的简单介绍: 1.信号量: 信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号... -
【三】POSIX信号量(POSIX有名信号量、POSIX无名信号量)
2019-01-03 17:18:352.POSIX 信号量和 System V 信号量的作用是相同的,都是用于同步进程之间及线程之间的操作,以达 到无冲突地访问共享资源的目的。 3.POSIX 信号量的作用和 System V 信号量是一样的。但是两者在接口上有很大... -
树莓派linux led字符设备驱动(信号量)
2021-09-22 20:14:41一、信号量 Linux 内核提供了信号量机制,信号量常常用于控制对共享资源的访问。相比于自旋锁,信号量可以使线程进入休眠状态,使用信号量会提高处理器的使用效率。但是,信号量的开销要比自旋锁大,因为信号量... -
信号量——二值信号量
2020-02-05 16:34:20FreeRTOS中信号量分为二值信号量、互斥信号量、计数信号量和递归互斥信号量,应用场景各不同。 二值信号量 二值信号量简述 二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常相似,但互斥信号量有... -
【Linux】POSIX信号量详解,从原理认识到代码实现线程间通信
2020-11-29 20:42:54POSIX信号量详解 -
FreeRTOS操作系统互斥信号量和递归信号量(九)
2022-04-06 22:59:26文章目录前言一、创建互斥信号量二、释放互斥信号量三、获取互斥信号量四、互斥信号量实验五、递归互斥信号量*1、创建递归互斥信号量**2、释放递归互斥信号量*3、获取递归互斥信号量*总结 前言 互斥信号量其实就是... -
Linux信号量
2022-02-27 17:29:50POSIX信号量 -
信号量 信号量的初始化 信号量 P 操作 sem_init sem_wait 信号量的 V 操作 获取信号量的计数值 信号量的...
2020-03-28 19:59:19信号量 信号量的概念 信号量广泛用于进程或线程间的同步和互斥, 信号量本质上是一个非负的整数计数器, 它被用来控制对公共资源的访问 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限, 当... -
RT-Thread学习笔记——信号量
2019-01-25 19:18:12本文讲RT-Thread的线程间同步之信号量,包括为什么要进行线程间同步、信号量创建与删除、信号量获取与释放以及基于STM32的二值信号量示例和计算型信号量示例,采用RTT&正点原子联合出品潘多拉开发板进行实验。 ... -
辨析:自旋锁与信号量
2020-02-05 15:09:51信号量(Semaphore): 1. 自旋锁与信号量简介 自旋是锁的一种实现方式,通过忙等待(“自旋,spinning”)来实现【例如通过while循环持续请求获取锁】。 信号量的概念比锁的范围更大, 可以说, 锁是信号量的一种特殊... -
FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)
2021-11-26 17:11:06我们在前面单独介绍过FreeRTOS的任务通知和消息队列, 但是在FreeRTOS中任务间的通讯还有信号量,邮箱,事件组标志等可以使用 这篇文章就这些成员与消息队列和任务通知的关系进行说明分析 -
操作系统考点之PV操作、信号量
2020-11-11 23:41:05如题:2020年8月 分析: 要明白PV操作,是对信号量进行的操作。什么是信号量呢?含义就是临界资源。当Sem大于等于零时代表可供并发进程使用的资源实体数,但sem小于零时则表示正在等待使用临街区的进程数。 当前值为... -
信号量详解
2020-12-23 00:01:372.14 信号量(重点) 进程通过传递信号进行协作 进程因为某个条件阻塞,不能继续推进 进程因为某个条件被唤醒,可以继续推进 可以实现信号灯作用的整数变量:信号量 信号量的三种操作 初始化:初始化为非负数,... -
公用信号量 私用信号量
2019-10-15 22:31:23公用信号量 私用信号量 公用信号量 用来实现进程间的互斥,初值为1,允许它所联系的一组进程对它执行P/V操作。 私用信号量 用来实现进程间的同步,初值为0或者某个正整数,仅允许拥有它的进程对其执行P/V操作。 ... -
四种信号量
2021-06-02 20:19:46传送门1、整型信号量2、记录型信号量3、AND型信号量4、信号量集5、信号量实现互斥 说在前:①信号量机制是对具体物理资源的抽象②同类资源的个数用>0的信号量值来表示③0或1的则为临界资源 1、整型信号量 1.1 ... -
【Linux】Linux的信号量集
2018-08-18 22:33:08所谓信号量集,就是由多个信号量组成的一个数组。作为一个整体,信号量集中的所有信号量使用同一个等待队列。Linux的信号量集为进程请求多个资源创造了条件。Linux规定,当进程的一个操作需要多个共享资源时,如果只... -
操作系统学习笔记02【信号量机制——死锁的处理策略——预防死锁】【自用】
2022-04-07 14:20:551.信号量机制 知识总览 信号量概述 整形信号量 记录型信号量 整型信号量和记录型信号量的根本区别是满不满足“让权等待”原则,会不会发生“忙等”现象。 记录型信号量具体例子 知识回顾与重要... -
互斥信号量与信号量的理解
2018-09-17 10:10:24至于信号量,和互斥信号量是用区别的,简单来说(个人理解,欢迎纠正)就是互斥信号量再同一时刻,任务得到互斥信号量量后是独占共享资源的,在他没有释放信号量之前,任何其他任务都是不能访问共享资源的。而信号... -
操作系统——信号量(理解什么是信号量,信号量如何解决同步互斥问题,信号量一些注意点)
2020-08-31 13:25:15信号量是什么 信号量(semaphore)是操作系统用来解决并发中的互斥和同步问题的一种方法。 信号量是一个与队列有关的整型变量,你可以把它想象成一个数后面拖着一条排队的队列,如图: 那信号量上面值n代表什么意思... -
信号量和互斥信号量的理解
2019-04-09 08:26:11信号量和互斥信号量的记住和理解应用是不一样的哦,面试常问。 做下本人理解和参考他人后的笔记 对于互斥锁(Mutex)来说,只要有线程占有了该资源,那么不好意思,其他线程就是优先级再高,您也得等着,等我用完... -
2.3.4 操作系统之信号量机制(整型信号量、记录型信号量P、V)
2020-03-18 20:11:50记录型信号量(1)举一个生动形象的例子了解记录型信号量(2)梳理一下记录型信号量的知识点(P、V) 0.思维导图 1.为什么引入信号量机制? 为了更好的解决进程互斥与同步的问题 2.什么是信号量机制? 3.整型...