一、信号量概念
信号量是用来解决进程/线程之间的同步互斥问题的一种通信机制,它表示代表某一类资源。
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;
}
运行结果: